Upload Tizen:Main source
authorKim Kibum <kb0929.kim@samsung.com>
Mon, 21 May 2012 09:00:19 +0000 (18:00 +0900)
committerKim Kibum <kb0929.kim@samsung.com>
Mon, 21 May 2012 09:00:19 +0000 (18:00 +0900)
1105 files changed:
CMakeLists.txt [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile.cvs [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION.cmake [new file with mode: 0644]
cmake/modules/FindCurl.cmake [new file with mode: 0644]
cmake/modules/FindDbus.cmake [new file with mode: 0644]
cmake/modules/FindGettext.cmake [new file with mode: 0644]
cmake/modules/FindHal.cmake [new file with mode: 0644]
cmake/modules/FindLibxml.cmake [new file with mode: 0644]
cmake/modules/FindOpenSSL.cmake [new file with mode: 0644]
cmake/modules/FindRpm.cmake [new file with mode: 0644]
cmake/modules/FindSatsolver.cmake [new file with mode: 0644]
cmake/modules/FindUdev.cmake [new file with mode: 0644]
cmake/modules/FindZsync.cmake [new file with mode: 0644]
cmake/modules/FindZypp.cmake [new file with mode: 0644]
cmake/modules/Findlibproxy.cmake [new file with mode: 0644]
cmake/modules/ZyppCommon.cmake [new file with mode: 0644]
devel/CMakeLists.txt [new file with mode: 0644]
devel/devel.dmacvicar/CMakeLists.txt [new file with mode: 0644]
devel/devel.dmacvicar/CURLM_tp.cc [new file with mode: 0644]
devel/devel.dmacvicar/README [new file with mode: 0644]
devel/devel.dmacvicar/ScanSource.cc [new file with mode: 0644]
devel/devel.dmacvicar/YUMReader_tp.cc [new file with mode: 0644]
devel/devel.dmacvicar/getfile.cc [new file with mode: 0644]
devel/devel.dmacvicar/multiple-download.cc [new file with mode: 0644]
devel/devel.dmacvicar/repodata/filelists.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/other.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-avahi-1399.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-dhcdbd-1315.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-dhcp-1316.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-dhcp-1424.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-dovecot-1398.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-ivman-1423.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-libextractor-1426.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-nagios-www-1311.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-openldap2-1323.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-opera-1313.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-pdns-1314.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patch-util-linux-crypto-1425.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/patches.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/primary.xml.gz [new file with mode: 0644]
devel/devel.dmacvicar/repodata/repomd.xml [new file with mode: 0644]
devel/devel.dmacvicar/repodata/repomd.xml.asc [new file with mode: 0644]
devel/devel.dmacvicar/repodata/repomd.xml.key [new file with mode: 0644]
devel/devel.dmacvicar/rpmbuilder.cc [new file with mode: 0644]
devel/devel.dmacvicar/testbed.cc [new file with mode: 0644]
devel/devel.dmacvicar/zsync.cc [new file with mode: 0644]
devel/devel.dmacvicar/zypp-lock.cc [new file with mode: 0644]
devel/devel.jkupec/CMakeLists.txt [new file with mode: 0644]
devel/devel.jkupec/YUMParser_test.cc [new file with mode: 0644]
devel/devel.jkupec/bridge/Base.h [new file with mode: 0644]
devel/devel.jkupec/bridge/BaseImpl.cc [new file with mode: 0644]
devel/devel.jkupec/bridge/BaseImpl.h [new file with mode: 0644]
devel/devel.jkupec/bridge/Derived.cc [new file with mode: 0644]
devel/devel.jkupec/bridge/Derived.h [new file with mode: 0644]
devel/devel.jkupec/bridge/bridge.cc [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/deltainfo.xml [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/primary.xml.gz [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.asc [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.key [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates/repodata/updateinfo.xml.gz [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/deltainfo.xml [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/primary.xml.gz [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.asc [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.key [new file with mode: 0644]
devel/devel.jkupec/data/deltarpm/updates2/repodata/updateinfo.xml.gz [new file with mode: 0644]
devel/devel.jkupec/data/pathinfo/afile [new file with mode: 0644]
devel/devel.jkupec/data/pathinfo/alink [new symlink]
devel/devel.jkupec/data/pathinfo/subdir/anotherfile [new file with mode: 0644]
devel/devel.jkupec/data/pathinfo/subdirlink [new symlink]
devel/devel.jkupec/dbqueries.sql [new file with mode: 0644]
devel/devel.jkupec/deltarpm.cc [new file with mode: 0644]
devel/devel.jkupec/pathinfo.cc [new file with mode: 0644]
devel/devel.jkupec/play.cc [new file with mode: 0644]
devel/devel.jkupec/poolquery.cc [new file with mode: 0644]
devel/devel.jkupec/repos.cc [new file with mode: 0755]
devel/devel.jkupec/yum/patterns-example.xml [new file with mode: 0644]
devel/devel.jkupec/yum/products-example.xml [new file with mode: 0644]
devel/devel.ma/AOUT.cc [new file with mode: 0644]
devel/devel.ma/Basic.cc [new file with mode: 0644]
devel/devel.ma/CMakeLists.txt [new file with mode: 0644]
devel/devel.ma/CleandepsOnRemove.cc [new file with mode: 0644]
devel/devel.ma/CommitCb.cc [new file with mode: 0644]
devel/devel.ma/DumpSolv.cc [new file with mode: 0644]
devel/devel.ma/Ex.cc [new file with mode: 0644]
devel/devel.ma/ExPure.cc [new file with mode: 0644]
devel/devel.ma/ExplicitMap.h [new file with mode: 0644]
devel/devel.ma/FakePool.cc [new file with mode: 0644]
devel/devel.ma/FakePool.h [new file with mode: 0644]
devel/devel.ma/FilelistTransform.cc [new file with mode: 0644]
devel/devel.ma/Iorder.cc [new file with mode: 0644]
devel/devel.ma/MaTest.cc [new file with mode: 0644]
devel/devel.ma/Main.cc [new file with mode: 0644]
devel/devel.ma/NewPool.cc [new file with mode: 0644]
devel/devel.ma/Parse.cc [new file with mode: 0644]
devel/devel.ma/PluginTest.cc [new file with mode: 0644]
devel/devel.ma/PluginTest.py [new file with mode: 0755]
devel/devel.ma/Printing.h [new file with mode: 0644]
devel/devel.ma/Sat.cc [new file with mode: 0644]
devel/devel.ma/SigTrackableFail.cc [new file with mode: 0644]
devel/devel.ma/Signal.cc [new file with mode: 0644]
devel/devel.ma/Test.cc [new file with mode: 0644]
devel/devel.ma/ToolProvideSignedDir.cc [new file with mode: 0644]
devel/devel.ma/Tools.h [new file with mode: 0644]
devel/devel.ma/TransList.cc [new file with mode: 0644]
devel/devel.ma/Xml.cc [new file with mode: 0644]
devel/devel.ma/defstr.txt [new file with mode: 0644]
devel/devel.ma/iorderbug.pool [new file with mode: 0644]
devel/devel.ma/lines2array [new file with mode: 0755]
devel/devel.ma/ma_test [new file with mode: 0755]
devel/devel.ma/main.cc [new file with mode: 0644]
devel/devel.ma/main.h [new file with mode: 0644]
devel/genclass.in [new file with mode: 0644]
doc/CMakeLists.txt [new file with mode: 0644]
doc/autodoc/CMakeLists.txt [new file with mode: 0644]
doc/autodoc/Doxyfile.cmake [new file with mode: 0644]
doc/autoinclude/CodePitfalls.doc [new file with mode: 0644]
doc/autoinclude/CodeSnippets.doc [new file with mode: 0644]
doc/autoinclude/EnvironmentVariables.doc [new file with mode: 0644]
doc/autoinclude/FeatureTest.doc [new file with mode: 0644]
doc/autoinclude/Mainpage.doc [new file with mode: 0644]
doc/autoinclude/Notes.doc [new file with mode: 0644]
doc/autoinclude/Plugin-Commit.doc [new file with mode: 0644]
doc/autoinclude/Plugins.doc [new file with mode: 0644]
doc/autoinclude/README [new file with mode: 0644]
doc/autoinclude/Services.doc [new file with mode: 0644]
doc/autoinclude/Testcases.doc [new file with mode: 0644]
doc/autoinclude/g_BOOST.doc [new file with mode: 0644]
doc/autoinclude/g_SATSOLVER.doc [new file with mode: 0644]
doc/autoinclude/groups.doc [new file with mode: 0644]
doc/autoinclude/notes/n_ResPool_nomorenameiter [new file with mode: 0644]
doc/downloaders-mediaset.txt [new file with mode: 0644]
doc/libzypp.zargo [new file with mode: 0644]
doc/locks.5 [new file with mode: 0644]
doc/solverstates.dot [new file with mode: 0644]
examples/CMakeLists.txt [new file with mode: 0644]
examples/COW_debug.cc [new file with mode: 0644]
examples/EditionCompare.cc [new file with mode: 0644]
examples/README [new file with mode: 0644]
examples/whatprovides.cc [new file with mode: 0644]
libzypp.pc.cmake [new file with mode: 0644]
libzypp.spec.cmake [new file with mode: 0644]
mkChangelog [new file with mode: 0755]
package/libzypp-rpmlint.cmake [new file with mode: 0644]
package/libzypp.changes [new file with mode: 0644]
packaging/0001-Disable-proxy-only-if-_none_-is-set-in-repo-file.patch [new file with mode: 0644]
packaging/MeeGo-Add-Rpm-Checker.patch [new file with mode: 0644]
packaging/MeeGo-dont-use-multcurl-by-default.patch [new file with mode: 0644]
packaging/MeeGo-patch-readd-thumb-arch-definitions.patch [new file with mode: 0644]
packaging/MeeGo-resume-download.patch [new file with mode: 0644]
packaging/MeeGo-use-fullname-in-search_deltafile.patch [new file with mode: 0644]
packaging/docs.patch [new file with mode: 0644]
packaging/libzypp-6.29.2-meego.patch [new file with mode: 0644]
packaging/libzypp-log-issue-bug704.patch [new file with mode: 0644]
packaging/libzypp-meego-release.patch [new file with mode: 0644]
packaging/libzypp-rpmlintrc [new file with mode: 0644]
packaging/libzypp.changes [new file with mode: 0644]
packaging/libzypp.conf [new file with mode: 0644]
packaging/libzypp.spec [new file with mode: 0644]
packaging/linker.patch [new file with mode: 0644]
packaging/meego-check-products-dir-while-using-rpmdb2solv.patch [new file with mode: 0644]
packaging/meego-try-again-while-downloading-fails.patch [new file with mode: 0644]
packaging/use_gpg2.patch [new file with mode: 0644]
po/CMakeLists.txt [new file with mode: 0644]
po/createPot [new file with mode: 0755]
po/zypp-po.tar.bz2 [new file with mode: 0644]
systemCheck [new file with mode: 0644]
tests/CMakeLists.txt [new file with mode: 0644]
tests/README [new file with mode: 0644]
tests/data/11.0-update/repodata/deltainfo.xml.gz [new file with mode: 0644]
tests/data/11.0-update/repodata/primary.xml.gz [new file with mode: 0644]
tests/data/11.0-update/repodata/repomd.xml [new file with mode: 0644]
tests/data/11.0-update/repodata/repomd.xml.asc [new file with mode: 0644]
tests/data/11.0-update/repodata/repomd.xml.key [new file with mode: 0644]
tests/data/11.0-update/repodata/updateinfo.xml.gz [new file with mode: 0644]
tests/data/Mirrorlist/remote-site/metalink.xml [new file with mode: 0644]
tests/data/Mirrorlist/remote-site/mirrors.txt [new file with mode: 0644]
tests/data/OBS_zypp_svn-11.1/repodata/primary.xml.gz [new file with mode: 0644]
tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml [new file with mode: 0644]
tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.asc [new file with mode: 0644]
tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.key [new file with mode: 0644]
tests/data/TCSelectable/RepoHIGH.xml [new file with mode: 0644]
tests/data/TCSelectable/RepoLOW.xml [new file with mode: 0644]
tests/data/TCSelectable/RepoMID.xml [new file with mode: 0644]
tests/data/TCSelectable/solver-system.xml [new file with mode: 0644]
tests/data/TCSelectable/solver-test.xml [new file with mode: 0644]
tests/data/TCWhatObsoletes/solver-system.xml [new file with mode: 0644]
tests/data/TCWhatObsoletes/solver-test.xml [new file with mode: 0644]
tests/data/TCdup/solver-system.xml [new file with mode: 0644]
tests/data/TCdup/solver-test.xml [new file with mode: 0644]
tests/data/TCdup/update.xml [new file with mode: 0644]
tests/data/obs_virtualbox_11_1/repodata/primary.xml.gz [new file with mode: 0644]
tests/data/obs_virtualbox_11_1/repodata/repomd.xml [new file with mode: 0644]
tests/data/obs_virtualbox_11_1/repodata/repomd.xml.asc [new file with mode: 0644]
tests/data/obs_virtualbox_11_1/repodata/repomd.xml.key [new file with mode: 0644]
tests/data/openSUSE-11.1/content [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-0dfb3188-41ed929b.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-307e3d54-481f30aa.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-3d25d3d9-36e12d04.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-56b4177a-47965b33.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-7e2e3b05-4816488f.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-9c800aca-481f343a.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/gpg-pubkey-a1912208-446a0899.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/license.tar.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/media.1/media [new file with mode: 0644]
tests/data/openSUSE-11.1/media.1/products [new file with mode: 0644]
tests/data/openSUSE-11.1/media.1/products.asc [new file with mode: 0644]
tests/data/openSUSE-11.1/media.1/products.key [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/dvd-11.1-46.1.x86_64.pat.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/non_oss-11.1-46.1.x86_64.pat.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/packages.DU.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/packages.cs.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/packages.en.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/packages.gz [new file with mode: 0644]
tests/data/openSUSE-11.1/suse/setup/descr/patterns [new file with mode: 0644]
tests/lib/CMakeLists.txt [new file with mode: 0644]
tests/lib/TestSetup.h [new file with mode: 0644]
tests/lib/WebServer.cc [new file with mode: 0644]
tests/lib/WebServer.h [new file with mode: 0644]
tests/media/CMakeLists.txt [new file with mode: 0644]
tests/media/CredentialFileReader_test.cc [new file with mode: 0644]
tests/media/CredentialManager_test.cc [new file with mode: 0644]
tests/media/MetaLinkParser_test.cc [new file with mode: 0644]
tests/media/data/credentials.cat [new file with mode: 0644]
tests/media/data/credentials.d/cred1 [new file with mode: 0644]
tests/media/data/openSUSE-11.3-NET-i586.iso.meta4 [new file with mode: 0644]
tests/media/data/openSUSE-11.3-NET-i586.iso.metalink [new file with mode: 0644]
tests/media/file_exists_test.cc [new file with mode: 0644]
tests/media/media1_test.cc [new file with mode: 0644]
tests/media/media2_test.cc [new file with mode: 0644]
tests/media/media3_test.cc [new file with mode: 0644]
tests/media/media4_test.cc [new file with mode: 0644]
tests/media/mymediaverifier.h [new file with mode: 0644]
tests/media/throw_if_not_exists_test.cc [new file with mode: 0644]
tests/parser/CMakeLists.txt [new file with mode: 0644]
tests/parser/ProductFileReader_test.cc [new file with mode: 0644]
tests/parser/ProductFileReader_test.dat [new file with mode: 0644]
tests/parser/RepoFileReader_test.cc [new file with mode: 0644]
tests/parser/RepoindexFileReader_test.cc [new file with mode: 0644]
tests/parser/inifile/CMakeLists.txt [new file with mode: 0644]
tests/parser/inifile/data/1.ini [new file with mode: 0644]
tests/parser/inifile/data/2.ini [new file with mode: 0644]
tests/parser/inifile/inidict_test.cc [new file with mode: 0644]
tests/parser/inifile/iniparser_test.cc [new file with mode: 0644]
tests/parser/ws/CMakeLists.txt [new file with mode: 0644]
tests/parser/ws/WebpinResultFileReader_test.cc [new file with mode: 0644]
tests/parser/ws/data/search-kopete.xml [new file with mode: 0644]
tests/parser/yum/CMakeLists.txt [new file with mode: 0644]
tests/parser/yum/PatchesFileReader_test.cc [new file with mode: 0644]
tests/parser/yum/RepomdFileReader_test.cc [new file with mode: 0644]
tests/parser/yum/data/README [new file with mode: 0644]
tests/parser/yum/data/patch-fetchmsttfonts.sh-4347.xml [new file with mode: 0644]
tests/parser/yum/data/patches-1.xml [new file with mode: 0644]
tests/parser/yum/data/patches-1.xml.solution [new file with mode: 0644]
tests/parser/yum/data/patches.xsl [new file with mode: 0644]
tests/parser/yum/data/repomd-1.xml [new file with mode: 0644]
tests/parser/yum/data/repomd-1.xml.solution [new file with mode: 0644]
tests/repo/CMakeLists.txt [new file with mode: 0644]
tests/repo/ExtendedMetadata_test.cc [new file with mode: 0644]
tests/repo/MirrorList_test.cc [new file with mode: 0644]
tests/repo/PluginServices_test.cc [new file with mode: 0644]
tests/repo/RepoVariables_test.cc [new file with mode: 0644]
tests/repo/susetags/CMakeLists.txt [new file with mode: 0644]
tests/repo/susetags/Downloader_test.cc [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/media.1/build [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/media.1/directory.yast [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/media.1/media [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/media.1/products [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/SHA1SUMS [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/content [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/content.asc [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/content.key [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/directory.yast [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/license.tar.gz [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/i586/SHA1SUMS [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/SHA1SUMS [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/directory.yast [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.DU [new file with mode: 0644]
tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.en [new file with mode: 0644]
tests/repo/susetags/data/shared_attributes/content [new file with mode: 0644]
tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages [new file with mode: 0644]
tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.DU [new file with mode: 0644]
tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.en [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/content [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/content.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/content.key [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/control.xml [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-0dfb3188-41ed929b.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-307e3d54-44201d5d.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-3d25d3d9-36e12d04.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-7e2e3b05-44748aba.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-9c800aca-40d8063e.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-a1912208-446a0899.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/installation.xml [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/license.tar.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/directory.yast [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/info.txt [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/license.zip [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/media [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/products [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.key [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/kde-10.3-71.i586.pat.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.DU.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.en.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.es.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/patterns.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/content [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/content.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/content.key [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/control.xml [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-0dfb3188-41ed929b.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-307e3d54-44201d5d.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-3d25d3d9-36e12d04.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-7e2e3b05-44748aba.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-9c800aca-40d8063e.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-a1912208-446a0899.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/installation.xml [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/license.tar.gz [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/directory.yast [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/info.txt [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/license.zip [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/media [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/products [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/products.asc [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/media.1/products.key [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/kde-10.3-71.i586.pat [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.DU [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.en [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.es [new file with mode: 0644]
tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/patterns [new file with mode: 0644]
tests/repo/yum/CMakeLists.txt [new file with mode: 0644]
tests/repo/yum/YUMDownloader_test.cc [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/filelists.xml.gz [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/other.xml.gz [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-fetchmsttfonts.sh-2333.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-flash-player-2359.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-glabels-2348.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-gv-2350.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-openssl-2349.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patch-tar-2351.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/patches.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/primary.xml.gz [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.asc [new file with mode: 0644]
tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.key [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/filelists.xml.gz [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/other.xml.gz [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/primary.xml.gz [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/repomd.xml [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/susedata.xml.gz [new file with mode: 0644]
tests/repo/yum/data/extensions/repodata/suseinfo.xml.gz [new file with mode: 0644]
tests/sat/AttrMatcher_test.cc [new file with mode: 0644]
tests/sat/CMakeLists.txt [new file with mode: 0644]
tests/sat/IdString_test.cc [new file with mode: 0644]
tests/sat/LookupAttr_test.cc [new file with mode: 0644]
tests/sat/SolvParsing_test.cc [new file with mode: 0644]
tests/sat/Solvable_test.cc [new file with mode: 0644]
tests/sat/WhatObsoletes_test.cc [new file with mode: 0644]
tests/sat/WhatProvides_test.cc [new file with mode: 0644]
tests/zypp/Arch_test.cc [new file with mode: 0644]
tests/zypp/CMakeLists.txt [new file with mode: 0644]
tests/zypp/Capabilities_test.cc [new file with mode: 0644]
tests/zypp/CheckSum_test.cc [new file with mode: 0644]
tests/zypp/Date_test.cc [new file with mode: 0644]
tests/zypp/Deltarpm_test.cc [new file with mode: 0644]
tests/zypp/Digest_test.cc [new file with mode: 0644]
tests/zypp/Dup_test.cc [new file with mode: 0644]
tests/zypp/Edition_test.cc [new file with mode: 0644]
tests/zypp/Fetcher_test.cc [new file with mode: 0644]
tests/zypp/FileChecker_test.cc [new file with mode: 0644]
tests/zypp/InstanceId_test.cc [new file with mode: 0644]
tests/zypp/KeyRingTestReceiver.h [new file with mode: 0644]
tests/zypp/KeyRing_test.cc [new file with mode: 0644]
tests/zypp/Locks_test.cc [new file with mode: 0644]
tests/zypp/MediaSetAccess_test.cc [new file with mode: 0644]
tests/zypp/PathInfo_test.cc [new file with mode: 0644]
tests/zypp/PluginFrame_test.cc [new file with mode: 0644]
tests/zypp/PoolQuery_test.cc [new file with mode: 0644]
tests/zypp/ProgressData_test.cc [new file with mode: 0644]
tests/zypp/PtrTypes_test.cc [new file with mode: 0644]
tests/zypp/PublicKey_test.cc [new file with mode: 0644]
tests/zypp/RWPtr_test.cc [new file with mode: 0644]
tests/zypp/RepoInfo_test.cc [new file with mode: 0644]
tests/zypp/RepoManager_test.cc [new file with mode: 0644]
tests/zypp/RepoStatus_test.cc [new file with mode: 0644]
tests/zypp/ResKind_test.cc [new file with mode: 0644]
tests/zypp/ResStatus_test.cc [new file with mode: 0644]
tests/zypp/Resolvable_test.cc [new file with mode: 0644]
tests/zypp/Selectable_test.cc [new file with mode: 0644]
tests/zypp/Signature_test.cc [new file with mode: 0644]
tests/zypp/Target_test.cc [new file with mode: 0644]
tests/zypp/Url_test.cc [new file with mode: 0644]
tests/zypp/Vendor2_test.cc [new file with mode: 0644]
tests/zypp/Vendor_test.cc [new file with mode: 0644]
tests/zypp/base/CMakeLists.txt [new file with mode: 0644]
tests/zypp/base/Glob_test.cc [new file with mode: 0644]
tests/zypp/base/Glob_test.dat/file [new file with mode: 0644]
tests/zypp/base/Glob_test.dat/file.xml [new file with mode: 0644]
tests/zypp/base/Glob_test.dat/file.xml.gz [new file with mode: 0644]
tests/zypp/base/InterProcessMutex2_test.cc [new file with mode: 0644]
tests/zypp/base/InterProcessMutex_test.cc [new file with mode: 0644]
tests/zypp/base/String_test.cc [new file with mode: 0644]
tests/zypp/base/Sysconfig_test.cc [new file with mode: 0644]
tests/zypp/base/data/Sysconfig/proxy [new file with mode: 0644]
tests/zypp/data/Delta/repodata/deltainfo.xml [new file with mode: 0644]
tests/zypp/data/Delta/repodata/primary.xml.gz [new file with mode: 0644]
tests/zypp/data/Delta/repodata/repomd.xml [new file with mode: 0644]
tests/zypp/data/Delta/repodata/repomd.xml.asc [new file with mode: 0644]
tests/zypp/data/Delta/repodata/repomd.xml.key [new file with mode: 0644]
tests/zypp/data/Delta/repodata/updateinfo.xml.gz [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/subdir2-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/subdir2-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/subdir2-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/subdir2-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/content [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/content.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/content.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/subdir2-file1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff.gz [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff.gz [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff.gz [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff.gz [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/directory.yast [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-1.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-2.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-3.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-4.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-current.txt [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-current.txt.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/file-current.txt.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file-unsigned/content [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file-unsigned/images/images.xml [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file/content [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file/content.asc [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file/content.key [new file with mode: 0644]
tests/zypp/data/Fetcher/remote-site/images-file/images/images.xml [new file with mode: 0644]
tests/zypp/data/FileChecker/hello.txt [new file with mode: 0644]
tests/zypp/data/FileChecker/hello.txt.asc [new file with mode: 0644]
tests/zypp/data/FileChecker/hello.txt.key [new file with mode: 0644]
tests/zypp/data/FileChecker/hello2.txt [new file with mode: 0644]
tests/zypp/data/KeyRing/private.asc [new file with mode: 0644]
tests/zypp/data/KeyRing/public.asc [new file with mode: 0644]
tests/zypp/data/KeyRing/readme.txt [new file with mode: 0644]
tests/zypp/data/KeyRing/repomd.xml [new file with mode: 0644]
tests/zypp/data/KeyRing/repomd.xml.asc [new file with mode: 0644]
tests/zypp/data/KeyRing/repomd.xml.corrupted [new file with mode: 0644]
tests/zypp/data/Locks/locks [new file with mode: 0644]
tests/zypp/data/PoolQuery/savedqueries [new file with mode: 0644]
tests/zypp/data/RepoManager/plugin-service-lib-1/services/service [new file with mode: 0755]
tests/zypp/data/RepoManager/plugin-service-lib-2/services/service [new file with mode: 0755]
tests/zypp/data/RepoManager/proprietary.repo [new file with mode: 0644]
tests/zypp/data/RepoManager/repo/repoindex.xml [new file with mode: 0644]
tests/zypp/data/RepoManager/repos.d/filesharing.repo [new file with mode: 0644]
tests/zypp/data/RepoManager/repos.d/home:dmacvicar.repo [new file with mode: 0644]
tests/zypp/data/RepoManager/repos.d/proprietary.repo [new file with mode: 0644]
tests/zypp/data/RepoManager/repos.d/ruby.repo [new file with mode: 0644]
tests/zypp/data/RepoManager/second/repo/repoindex.xml [new file with mode: 0644]
tests/zypp/data/Target/product.prod [new file with mode: 0644]
tests/zypp/data/Vendor/vendors.d/ati [new file with mode: 0644]
tests/zypp/data/Vendor/vendors.d/nvidia [new file with mode: 0644]
tests/zypp/data/Vendor/zypp1.conf [new file with mode: 0644]
tests/zypp/data/Vendor/zypp2.conf.cmake [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd1/dir/file1 [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd1/dir/file2 [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd1/dir/subdir/file [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd1/test.txt [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd1/x.media1 [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd2/test.txt [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd2/x.mediabad [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd3/test.txt [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src1/cd3/x.media3 [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src2/test.txt [new file with mode: 0644]
tests/zypp/data/mediasetaccess/src2/x.media [new file with mode: 0644]
tools/CMakeLists.txt [new file with mode: 0644]
tools/DumpSelectable.cc [new file with mode: 0644]
tools/NameReqPrv.cc [new file with mode: 0644]
tools/ToolScanRepos.cc [new file with mode: 0644]
tools/migrate-sources/CMakeLists.txt [new file with mode: 0644]
tools/migrate-sources/migrate-sources.cc [new file with mode: 0644]
tools/notify-message [new file with mode: 0755]
tools/package-manager/CMakeLists.txt [new file with mode: 0644]
tools/package-manager/package-manager [new file with mode: 0755]
tools/package-manager/package-manager-su [new file with mode: 0755]
tools/package-manager/package-manager.desktop [new file with mode: 0644]
tools/patch_find_bug.cc [new file with mode: 0644]
tools/percent-encode.cc [new file with mode: 0644]
tools/zypp-CheckAccessDeleted.cc [new file with mode: 0644]
tools/zypp-list.cc [new file with mode: 0644]
tools/zypp-pubkey.cc [new file with mode: 0644]
vendor/CMakeLists.txt [new file with mode: 0644]
vendor/mongoose/CMakeLists.txt [new file with mode: 0644]
vendor/mongoose/mongoose.c [new file with mode: 0644]
vendor/mongoose/mongoose.h [new file with mode: 0644]
zypp-history.lr [new file with mode: 0644]
zypp.conf [new file with mode: 0644]
zypp/Arch.cc [new file with mode: 0644]
zypp/Arch.h [new file with mode: 0644]
zypp/AutoDispose.h [new file with mode: 0644]
zypp/Bit.h [new file with mode: 0644]
zypp/ByteCount.cc [new file with mode: 0644]
zypp/ByteCount.h [new file with mode: 0644]
zypp/CMakeLists.txt [new file with mode: 0644]
zypp/Callback.h [new file with mode: 0644]
zypp/CapMatch.cc [new file with mode: 0644]
zypp/CapMatch.h [new file with mode: 0644]
zypp/Capabilities.cc [new file with mode: 0644]
zypp/Capabilities.h [new file with mode: 0644]
zypp/Capability.cc [new file with mode: 0644]
zypp/Capability.h [new file with mode: 0644]
zypp/Changelog.cc [new file with mode: 0644]
zypp/Changelog.h [new file with mode: 0644]
zypp/CheckSum.cc [new file with mode: 0644]
zypp/CheckSum.h [new file with mode: 0644]
zypp/CountryCode.cc [new file with mode: 0644]
zypp/CountryCode.h [new file with mode: 0644]
zypp/Date.cc [new file with mode: 0644]
zypp/Date.h [new file with mode: 0644]
zypp/Dep.cc [new file with mode: 0644]
zypp/Dep.h [new file with mode: 0644]
zypp/Digest.cc [new file with mode: 0644]
zypp/Digest.h [new file with mode: 0644]
zypp/DiskUsage.cc [new file with mode: 0644]
zypp/DiskUsage.h [new file with mode: 0644]
zypp/DiskUsageCounter.cc [new file with mode: 0644]
zypp/DiskUsageCounter.h [new file with mode: 0644]
zypp/DownloadMode.cc [new file with mode: 0644]
zypp/DownloadMode.h [new file with mode: 0644]
zypp/Edition.cc [new file with mode: 0644]
zypp/Edition.h [new file with mode: 0644]
zypp/ExternalProgram.cc [new file with mode: 0644]
zypp/ExternalProgram.h [new file with mode: 0644]
zypp/Fetcher.cc [new file with mode: 0644]
zypp/Fetcher.h [new file with mode: 0644]
zypp/FileChecker.cc [new file with mode: 0644]
zypp/FileChecker.h [new file with mode: 0644]
zypp/Filter.h [new file with mode: 0644]
zypp/Glob.cc [new file with mode: 0644]
zypp/Glob.h [new file with mode: 0644]
zypp/HistoryLog.cc [new file with mode: 0644]
zypp/HistoryLog.h [new file with mode: 0644]
zypp/HistoryLogData.cc [new file with mode: 0644]
zypp/HistoryLogData.h [new file with mode: 0644]
zypp/IdString.cc [new file with mode: 0644]
zypp/IdString.h [new file with mode: 0644]
zypp/IdStringType.h [new file with mode: 0644]
zypp/InstanceId.cc [new file with mode: 0644]
zypp/InstanceId.h [new file with mode: 0644]
zypp/KVMap.h [new file with mode: 0644]
zypp/KeyContext.h [new file with mode: 0644]
zypp/KeyRing.cc [new file with mode: 0644]
zypp/KeyRing.h [new file with mode: 0644]
zypp/LanguageCode.cc [new file with mode: 0644]
zypp/LanguageCode.h [new file with mode: 0644]
zypp/Locale.cc [new file with mode: 0644]
zypp/Locale.h [new file with mode: 0644]
zypp/Locks.cc [new file with mode: 0644]
zypp/Locks.h [new file with mode: 0644]
zypp/ManagedFile.h [new file with mode: 0644]
zypp/MediaProducts.cc [new file with mode: 0644]
zypp/MediaProducts.h [new file with mode: 0644]
zypp/MediaSetAccess.cc [new file with mode: 0644]
zypp/MediaSetAccess.h [new file with mode: 0644]
zypp/Misc.h [new file with mode: 0644]
zypp/OnMediaLocation.cc [new file with mode: 0644]
zypp/OnMediaLocation.h [new file with mode: 0644]
zypp/Package.cc [new file with mode: 0644]
zypp/Package.h [new file with mode: 0644]
zypp/PackageKeyword.h [new file with mode: 0644]
zypp/Patch.cc [new file with mode: 0644]
zypp/Patch.h [new file with mode: 0644]
zypp/PathInfo.cc [new file with mode: 0644]
zypp/PathInfo.h [new file with mode: 0644]
zypp/Pathname.cc [new file with mode: 0644]
zypp/Pathname.h [new file with mode: 0644]
zypp/Pattern.cc [new file with mode: 0644]
zypp/Pattern.h [new file with mode: 0644]
zypp/PluginFrame.cc [new file with mode: 0644]
zypp/PluginFrame.h [new file with mode: 0644]
zypp/PluginFrameException.cc [new file with mode: 0644]
zypp/PluginFrameException.h [new file with mode: 0644]
zypp/PluginScript.cc [new file with mode: 0644]
zypp/PluginScript.h [new file with mode: 0644]
zypp/PluginScriptException.cc [new file with mode: 0644]
zypp/PluginScriptException.h [new file with mode: 0644]
zypp/PoolItem.cc [new file with mode: 0644]
zypp/PoolItem.h [new file with mode: 0644]
zypp/PoolItemBest.cc [new file with mode: 0644]
zypp/PoolItemBest.h [new file with mode: 0644]
zypp/PoolQuery.cc [new file with mode: 0644]
zypp/PoolQuery.h [new file with mode: 0644]
zypp/PoolQueryResult.cc [new file with mode: 0644]
zypp/PoolQueryResult.h [new file with mode: 0644]
zypp/PoolQueryUtil.tcc [new file with mode: 0644]
zypp/ProblemSolution.cc [new file with mode: 0644]
zypp/ProblemSolution.h [new file with mode: 0644]
zypp/ProblemTypes.h [new file with mode: 0644]
zypp/Product.cc [new file with mode: 0644]
zypp/Product.h [new file with mode: 0644]
zypp/ProgressData.cc [new file with mode: 0644]
zypp/ProgressData.h [new file with mode: 0644]
zypp/ProvideFilePolicy.cc [new file with mode: 0644]
zypp/ProvideFilePolicy.h [new file with mode: 0644]
zypp/PublicKey.cc [new file with mode: 0644]
zypp/PublicKey.h [new file with mode: 0644]
zypp/Range.cc [new file with mode: 0644]
zypp/Range.h [new file with mode: 0644]
zypp/Rel.cc [new file with mode: 0644]
zypp/Rel.h [new file with mode: 0644]
zypp/RelCompare.h [new file with mode: 0644]
zypp/RepoInfo.cc [new file with mode: 0644]
zypp/RepoInfo.h [new file with mode: 0644]
zypp/RepoManager.cc [new file with mode: 0644]
zypp/RepoManager.h [new file with mode: 0644]
zypp/RepoStatus.cc [new file with mode: 0644]
zypp/RepoStatus.h [new file with mode: 0644]
zypp/Repository.cc [new file with mode: 0644]
zypp/Repository.h [new file with mode: 0644]
zypp/ResFilters.h [new file with mode: 0644]
zypp/ResKind.cc [new file with mode: 0644]
zypp/ResKind.h [new file with mode: 0644]
zypp/ResObject.cc [new file with mode: 0644]
zypp/ResObject.h [new file with mode: 0644]
zypp/ResObjects.h [new file with mode: 0644]
zypp/ResPool.cc [new file with mode: 0644]
zypp/ResPool.h [new file with mode: 0644]
zypp/ResPoolProxy.cc [new file with mode: 0644]
zypp/ResPoolProxy.h [new file with mode: 0644]
zypp/ResStatus.cc [new file with mode: 0644]
zypp/ResStatus.h [new file with mode: 0644]
zypp/ResTraits.h [new file with mode: 0644]
zypp/Resolvable.cc [new file with mode: 0644]
zypp/Resolvable.h [new file with mode: 0644]
zypp/Resolver.cc [new file with mode: 0644]
zypp/Resolver.h [new file with mode: 0644]
zypp/ResolverProblem.cc [new file with mode: 0644]
zypp/ResolverProblem.h [new file with mode: 0644]
zypp/ServiceInfo.cc [new file with mode: 0644]
zypp/ServiceInfo.h [new file with mode: 0644]
zypp/Signature.cc [new file with mode: 0644]
zypp/Signature.h [new file with mode: 0644]
zypp/SrcPackage.cc [new file with mode: 0644]
zypp/SrcPackage.h [new file with mode: 0644]
zypp/SysContent.cc [new file with mode: 0644]
zypp/SysContent.h [new file with mode: 0644]
zypp/Target.cc [new file with mode: 0644]
zypp/Target.h [new file with mode: 0644]
zypp/TmpPath.cc [new file with mode: 0644]
zypp/TmpPath.h [new file with mode: 0644]
zypp/TriBool.h [new file with mode: 0644]
zypp/Url.cc [new file with mode: 0644]
zypp/Url.h [new file with mode: 0644]
zypp/Vendor.h [new file with mode: 0644]
zypp/VendorAttr.cc [new file with mode: 0644]
zypp/VendorAttr.h [new file with mode: 0644]
zypp/VendorSupportOptions.cc [new file with mode: 0644]
zypp/VendorSupportOptions.h [new file with mode: 0644]
zypp/ZConfig.cc [new file with mode: 0644]
zypp/ZConfig.h [new file with mode: 0644]
zypp/ZYpp.cc [new file with mode: 0644]
zypp/ZYpp.h [new file with mode: 0644]
zypp/ZYppCallbacks.h [new file with mode: 0644]
zypp/ZYppCommit.h [new file with mode: 0644]
zypp/ZYppCommitPolicy.cc [new file with mode: 0644]
zypp/ZYppCommitPolicy.h [new file with mode: 0644]
zypp/ZYppCommitResult.cc [new file with mode: 0644]
zypp/ZYppCommitResult.h [new file with mode: 0644]
zypp/ZYppFactory.cc [new file with mode: 0644]
zypp/ZYppFactory.h [new file with mode: 0644]
zypp/base/Algorithm.h [new file with mode: 0644]
zypp/base/Collector.h [new file with mode: 0644]
zypp/base/Counter.h [new file with mode: 0644]
zypp/base/Debug.h [new file with mode: 0644]
zypp/base/DefaultIntegral.h [new file with mode: 0644]
zypp/base/Deprecated.h [new file with mode: 0644]
zypp/base/DtorReset.h [new file with mode: 0644]
zypp/base/Easy.h [new file with mode: 0644]
zypp/base/Errno.h [new file with mode: 0644]
zypp/base/Exception.cc [new file with mode: 0644]
zypp/base/Exception.h [new file with mode: 0644]
zypp/base/ExternalDataSource.cc [new file with mode: 0644]
zypp/base/ExternalDataSource.h [new file with mode: 0644]
zypp/base/Fd.cc [new file with mode: 0644]
zypp/base/Fd.h [new file with mode: 0644]
zypp/base/Flags.h [new file with mode: 0644]
zypp/base/Function.h [new file with mode: 0644]
zypp/base/Functional.h [new file with mode: 0644]
zypp/base/Gettext.cc [new file with mode: 0644]
zypp/base/Gettext.h [new file with mode: 0644]
zypp/base/GzStream.cc [new file with mode: 0644]
zypp/base/GzStream.h [new file with mode: 0644]
zypp/base/IOStream.cc [new file with mode: 0644]
zypp/base/IOStream.h [new file with mode: 0644]
zypp/base/InputStream.cc [new file with mode: 0644]
zypp/base/InputStream.h [new file with mode: 0644]
zypp/base/InterProcessMutex.cc [new file with mode: 0644]
zypp/base/InterProcessMutex.h [new file with mode: 0644]
zypp/base/Iterator.h [new file with mode: 0644]
zypp/base/LogControl.cc [new file with mode: 0644]
zypp/base/LogControl.h [new file with mode: 0644]
zypp/base/LogTools.h [new file with mode: 0644]
zypp/base/Logger.h [new file with mode: 0644]
zypp/base/Measure.cc [new file with mode: 0644]
zypp/base/Measure.h [new file with mode: 0644]
zypp/base/NonCopyable.h [new file with mode: 0644]
zypp/base/ProfilingFormater.cc [new file with mode: 0644]
zypp/base/ProfilingFormater.h [new file with mode: 0644]
zypp/base/ProvideNumericId.h [new file with mode: 0644]
zypp/base/PtrTypes.h [new file with mode: 0644]
zypp/base/Random.cc [new file with mode: 0644]
zypp/base/Random.h [new file with mode: 0644]
zypp/base/ReferenceCounted.cc [new file with mode: 0644]
zypp/base/ReferenceCounted.h [new file with mode: 0644]
zypp/base/Regex.cc [new file with mode: 0644]
zypp/base/Regex.h [new file with mode: 0644]
zypp/base/SafeBool.h [new file with mode: 0644]
zypp/base/SerialNumber.cc [new file with mode: 0644]
zypp/base/SerialNumber.h [new file with mode: 0644]
zypp/base/Signal.h [new file with mode: 0644]
zypp/base/String.cc [new file with mode: 0644]
zypp/base/String.h [new file with mode: 0644]
zypp/base/Sysconfig.cc [new file with mode: 0644]
zypp/base/Sysconfig.h [new file with mode: 0644]
zypp/base/Tr1hash.h [new file with mode: 0644]
zypp/base/Unit.cc [new file with mode: 0644]
zypp/base/Unit.h [new file with mode: 0644]
zypp/base/UserRequestException.cc [new file with mode: 0644]
zypp/base/UserRequestException.h [new file with mode: 0644]
zypp/base/WatchFile.h [new file with mode: 0644]
zypp/media/CredentialFileReader.cc [new file with mode: 0644]
zypp/media/CredentialFileReader.h [new file with mode: 0644]
zypp/media/CredentialManager.cc [new file with mode: 0644]
zypp/media/CredentialManager.h [new file with mode: 0644]
zypp/media/CurlConfig.cc [new file with mode: 0644]
zypp/media/CurlConfig.h [new file with mode: 0644]
zypp/media/MediaAccess.cc [new file with mode: 0644]
zypp/media/MediaAccess.h [new file with mode: 0644]
zypp/media/MediaAria2c.cc [new file with mode: 0644]
zypp/media/MediaAria2c.h [new file with mode: 0644]
zypp/media/MediaBlockList.cc [new file with mode: 0644]
zypp/media/MediaBlockList.h [new file with mode: 0644]
zypp/media/MediaCD.cc [new file with mode: 0644]
zypp/media/MediaCD.h [new file with mode: 0644]
zypp/media/MediaCIFS.cc [new file with mode: 0644]
zypp/media/MediaCIFS.h [new file with mode: 0644]
zypp/media/MediaCurl.cc [new file with mode: 0644]
zypp/media/MediaCurl.h [new file with mode: 0644]
zypp/media/MediaDIR.cc [new file with mode: 0644]
zypp/media/MediaDIR.h [new file with mode: 0644]
zypp/media/MediaDISK.cc [new file with mode: 0644]
zypp/media/MediaDISK.h [new file with mode: 0644]
zypp/media/MediaException.cc [new file with mode: 0644]
zypp/media/MediaException.h [new file with mode: 0644]
zypp/media/MediaHandler.cc [new file with mode: 0644]
zypp/media/MediaHandler.h [new file with mode: 0644]
zypp/media/MediaISO.cc [new file with mode: 0644]
zypp/media/MediaISO.h [new file with mode: 0644]
zypp/media/MediaManager.cc [new file with mode: 0644]
zypp/media/MediaManager.h [new file with mode: 0644]
zypp/media/MediaMultiCurl.cc [new file with mode: 0644]
zypp/media/MediaMultiCurl.h [new file with mode: 0644]
zypp/media/MediaNFS.cc [new file with mode: 0644]
zypp/media/MediaNFS.h [new file with mode: 0644]
zypp/media/MediaPlugin.cc [new file with mode: 0644]
zypp/media/MediaPlugin.h [new file with mode: 0644]
zypp/media/MediaPriority.cc [new file with mode: 0644]
zypp/media/MediaPriority.h [new file with mode: 0644]
zypp/media/MediaSource.cc [new file with mode: 0644]
zypp/media/MediaSource.h [new file with mode: 0644]
zypp/media/MediaUserAuth.cc [new file with mode: 0644]
zypp/media/MediaUserAuth.h [new file with mode: 0644]
zypp/media/MetaLinkParser.cc [new file with mode: 0644]
zypp/media/MetaLinkParser.h [new file with mode: 0644]
zypp/media/Mount.cc [new file with mode: 0644]
zypp/media/Mount.h [new file with mode: 0644]
zypp/media/ProxyInfo.cc [new file with mode: 0644]
zypp/media/ProxyInfo.h [new file with mode: 0644]
zypp/media/TransferSettings.cc [new file with mode: 0644]
zypp/media/TransferSettings.h [new file with mode: 0644]
zypp/media/UrlResolverPlugin.cc [new file with mode: 0644]
zypp/media/UrlResolverPlugin.h [new file with mode: 0644]
zypp/media/ZsyncParser.cc [new file with mode: 0644]
zypp/media/ZsyncParser.h [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfoImpl.h [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfoLibproxy.cc [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfoLibproxy.h [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfoSysconfig.cc [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfoSysconfig.h [new file with mode: 0644]
zypp/media/proxyinfo/ProxyInfos.h [new file with mode: 0644]
zypp/misc/CheckAccessDeleted.cc [new file with mode: 0644]
zypp/misc/CheckAccessDeleted.h [new file with mode: 0644]
zypp/misc/DefaultLoadSystem.cc [new file with mode: 0644]
zypp/misc/DefaultLoadSystem.h [new file with mode: 0644]
zypp/parser/HistoryLogReader.cc [new file with mode: 0644]
zypp/parser/HistoryLogReader.h [new file with mode: 0644]
zypp/parser/IniDict.cc [new file with mode: 0644]
zypp/parser/IniDict.h [new file with mode: 0644]
zypp/parser/IniParser.cc [new file with mode: 0644]
zypp/parser/IniParser.h [new file with mode: 0644]
zypp/parser/ParseException.cc [new file with mode: 0644]
zypp/parser/ParseException.h [new file with mode: 0644]
zypp/parser/ParserProgress.h [new file with mode: 0644]
zypp/parser/ProductFileReader.cc [new file with mode: 0644]
zypp/parser/ProductFileReader.h [new file with mode: 0644]
zypp/parser/RepoFileReader.cc [new file with mode: 0644]
zypp/parser/RepoFileReader.h [new file with mode: 0644]
zypp/parser/RepoindexFileReader.cc [new file with mode: 0644]
zypp/parser/RepoindexFileReader.h [new file with mode: 0644]
zypp/parser/ServiceFileReader.cc [new file with mode: 0644]
zypp/parser/ServiceFileReader.h [new file with mode: 0644]
zypp/parser/plaindir/RepoParser.cc [new file with mode: 0644]
zypp/parser/plaindir/RepoParser.h [new file with mode: 0644]
zypp/parser/schema/repoindex.rnc [new file with mode: 0644]
zypp/parser/susetags/ContentFileReader.cc [new file with mode: 0644]
zypp/parser/susetags/ContentFileReader.h [new file with mode: 0644]
zypp/parser/susetags/RepoIndex.cc [new file with mode: 0644]
zypp/parser/susetags/RepoIndex.h [new file with mode: 0644]
zypp/parser/ws/WebpinResultFileReader.cc [new file with mode: 0644]
zypp/parser/ws/WebpinResultFileReader.h [new file with mode: 0644]
zypp/parser/xml/Node.cc [new file with mode: 0644]
zypp/parser/xml/Node.h [new file with mode: 0644]
zypp/parser/xml/Parse.h [new file with mode: 0644]
zypp/parser/xml/ParseDef.cc [new file with mode: 0644]
zypp/parser/xml/ParseDef.h [new file with mode: 0644]
zypp/parser/xml/ParseDefConsume.cc [new file with mode: 0644]
zypp/parser/xml/ParseDefConsume.h [new file with mode: 0644]
zypp/parser/xml/ParseDefException.cc [new file with mode: 0644]
zypp/parser/xml/ParseDefException.h [new file with mode: 0644]
zypp/parser/xml/ParseDefTraits.h [new file with mode: 0644]
zypp/parser/xml/Reader.cc [new file with mode: 0644]
zypp/parser/xml/Reader.h [new file with mode: 0644]
zypp/parser/xml/XmlEscape.h [new file with mode: 0644]
zypp/parser/xml/XmlString.cc [new file with mode: 0644]
zypp/parser/xml/XmlString.h [new file with mode: 0644]
zypp/parser/xml/libxmlfwd.cc [new file with mode: 0644]
zypp/parser/xml/libxmlfwd.h [new file with mode: 0644]
zypp/parser/xml/xml_escape_parser.cpp [new file with mode: 0644]
zypp/parser/xml/xml_escape_parser.hpp [new file with mode: 0644]
zypp/parser/xml_escape_parser.hpp [new file with mode: 0644]
zypp/parser/yum/PatchesFileReader.cc [new file with mode: 0644]
zypp/parser/yum/PatchesFileReader.h [new file with mode: 0644]
zypp/parser/yum/RepomdFileReader.cc [new file with mode: 0644]
zypp/parser/yum/RepomdFileReader.h [new file with mode: 0644]
zypp/parser/yum/schema/common-inc.rnc [new file with mode: 0644]
zypp/parser/yum/schema/common-inc.rng [new file with mode: 0644]
zypp/parser/yum/schema/deltainfo.rnc [new file with mode: 0644]
zypp/parser/yum/schema/deltainfo.rng [new file with mode: 0644]
zypp/parser/yum/schema/filelists.rnc [new file with mode: 0644]
zypp/parser/yum/schema/filelists.rng [new file with mode: 0644]
zypp/parser/yum/schema/other.rnc [new file with mode: 0644]
zypp/parser/yum/schema/other.rng [new file with mode: 0644]
zypp/parser/yum/schema/patch.rnc [new file with mode: 0644]
zypp/parser/yum/schema/patch.rng [new file with mode: 0644]
zypp/parser/yum/schema/patches.rnc [new file with mode: 0644]
zypp/parser/yum/schema/patches.rng [new file with mode: 0644]
zypp/parser/yum/schema/patterns.rnc [new file with mode: 0644]
zypp/parser/yum/schema/patterns.rng [new file with mode: 0644]
zypp/parser/yum/schema/primary.rnc [new file with mode: 0644]
zypp/parser/yum/schema/primary.rng [new file with mode: 0644]
zypp/parser/yum/schema/product.rnc [new file with mode: 0644]
zypp/parser/yum/schema/product.rng [new file with mode: 0644]
zypp/parser/yum/schema/products.rnc [new file with mode: 0644]
zypp/parser/yum/schema/products.rng [new file with mode: 0644]
zypp/parser/yum/schema/repomd.rnc [new file with mode: 0644]
zypp/parser/yum/schema/repomd.rng [new file with mode: 0644]
zypp/parser/yum/schema/rnc2rng [new file with mode: 0755]
zypp/parser/yum/schema/rpm-inc.rnc [new file with mode: 0644]
zypp/parser/yum/schema/rpm-inc.rng [new file with mode: 0644]
zypp/parser/yum/schema/susedata.rnc [new file with mode: 0644]
zypp/parser/yum/schema/susedata.rng [new file with mode: 0644]
zypp/parser/yum/schema/suseinfo.rnc [new file with mode: 0644]
zypp/parser/yum/schema/suseinfo.rng [new file with mode: 0644]
zypp/parser/yum/schema/updateinfo.rnc [new file with mode: 0644]
zypp/parser/yum/schema/updateinfo.rng [new file with mode: 0644]
zypp/parser/yum/schema/validate-all [new file with mode: 0755]
zypp/pool/ByIdent.h [new file with mode: 0644]
zypp/pool/GetResolvablesToInsDel.cc [new file with mode: 0644]
zypp/pool/GetResolvablesToInsDel.h [new file with mode: 0644]
zypp/pool/PoolImpl.cc [new file with mode: 0644]
zypp/pool/PoolImpl.h [new file with mode: 0644]
zypp/pool/PoolStats.cc [new file with mode: 0644]
zypp/pool/PoolStats.h [new file with mode: 0644]
zypp/pool/PoolTraits.h [new file with mode: 0644]
zypp/repo/Applydeltarpm.cc [new file with mode: 0644]
zypp/repo/Applydeltarpm.h [new file with mode: 0644]
zypp/repo/DeltaCandidates.cc [new file with mode: 0644]
zypp/repo/DeltaCandidates.h [new file with mode: 0644]
zypp/repo/Downloader.cc [new file with mode: 0644]
zypp/repo/Downloader.h [new file with mode: 0644]
zypp/repo/MediaInfoDownloader.cc [new file with mode: 0644]
zypp/repo/MediaInfoDownloader.h [new file with mode: 0644]
zypp/repo/PackageDelta.cc [new file with mode: 0644]
zypp/repo/PackageDelta.h [new file with mode: 0644]
zypp/repo/PackageProvider.cc [new file with mode: 0644]
zypp/repo/PackageProvider.h [new file with mode: 0644]
zypp/repo/PluginServices.cc [new file with mode: 0644]
zypp/repo/PluginServices.h [new file with mode: 0644]
zypp/repo/RepoException.cc [new file with mode: 0644]
zypp/repo/RepoException.h [new file with mode: 0644]
zypp/repo/RepoInfoBase.cc [new file with mode: 0644]
zypp/repo/RepoInfoBase.h [new file with mode: 0644]
zypp/repo/RepoInfoBaseImpl.h [new file with mode: 0644]
zypp/repo/RepoMirrorList.cc [new file with mode: 0644]
zypp/repo/RepoMirrorList.h [new file with mode: 0644]
zypp/repo/RepoProvideFile.cc [new file with mode: 0644]
zypp/repo/RepoProvideFile.h [new file with mode: 0644]
zypp/repo/RepoType.cc [new file with mode: 0644]
zypp/repo/RepoType.h [new file with mode: 0644]
zypp/repo/RepoVariables.cc [new file with mode: 0644]
zypp/repo/RepoVariables.h [new file with mode: 0644]
zypp/repo/SUSEMediaVerifier.cc [new file with mode: 0644]
zypp/repo/SUSEMediaVerifier.h [new file with mode: 0644]
zypp/repo/ServiceRepos.cc [new file with mode: 0644]
zypp/repo/ServiceRepos.h [new file with mode: 0644]
zypp/repo/ServiceType.cc [new file with mode: 0644]
zypp/repo/ServiceType.h [new file with mode: 0644]
zypp/repo/SrcPackageProvider.cc [new file with mode: 0644]
zypp/repo/SrcPackageProvider.h [new file with mode: 0644]
zypp/repo/susetags/Downloader.cc [new file with mode: 0644]
zypp/repo/susetags/Downloader.h [new file with mode: 0644]
zypp/repo/yum/Downloader.cc [new file with mode: 0644]
zypp/repo/yum/Downloader.h [new file with mode: 0644]
zypp/repo/yum/ResourceType.cc [new file with mode: 0644]
zypp/repo/yum/ResourceType.h [new file with mode: 0644]
zypp/sat/AttrMatcher.cc [new file with mode: 0644]
zypp/sat/AttrMatcher.h [new file with mode: 0644]
zypp/sat/LocaleSupport.cc [new file with mode: 0644]
zypp/sat/LocaleSupport.h [new file with mode: 0644]
zypp/sat/LookupAttr.cc [new file with mode: 0644]
zypp/sat/LookupAttr.h [new file with mode: 0644]
zypp/sat/LookupAttrTools.h [new file with mode: 0644]
zypp/sat/Pool.cc [new file with mode: 0644]
zypp/sat/Pool.h [new file with mode: 0644]
zypp/sat/Queue.cc [new file with mode: 0644]
zypp/sat/Queue.h [new file with mode: 0644]
zypp/sat/SolvAttr.cc [new file with mode: 0644]
zypp/sat/SolvAttr.h [new file with mode: 0644]
zypp/sat/SolvIterMixin.cc [new file with mode: 0644]
zypp/sat/SolvIterMixin.h [new file with mode: 0644]
zypp/sat/Solvable.cc [new file with mode: 0644]
zypp/sat/Solvable.h [new file with mode: 0644]
zypp/sat/SolvableSet.cc [new file with mode: 0644]
zypp/sat/SolvableSet.h [new file with mode: 0644]
zypp/sat/Transaction.cc [new file with mode: 0644]
zypp/sat/Transaction.h [new file with mode: 0644]
zypp/sat/WhatObsoletes.cc [new file with mode: 0644]
zypp/sat/WhatObsoletes.h [new file with mode: 0644]
zypp/sat/WhatProvides.cc [new file with mode: 0644]
zypp/sat/WhatProvides.h [new file with mode: 0644]
zypp/sat/detail/PoolImpl.cc [new file with mode: 0644]
zypp/sat/detail/PoolImpl.h [new file with mode: 0644]
zypp/sat/detail/PoolMember.h [new file with mode: 0644]
zypp/solver/detail/Helper.cc [new file with mode: 0644]
zypp/solver/detail/Helper.h [new file with mode: 0644]
zypp/solver/detail/InstallOrder.cc [new file with mode: 0644]
zypp/solver/detail/InstallOrder.h [new file with mode: 0644]
zypp/solver/detail/ProblemSolutionCombi.cc [new file with mode: 0644]
zypp/solver/detail/ProblemSolutionCombi.h [new file with mode: 0644]
zypp/solver/detail/ProblemSolutionIgnore.cc [new file with mode: 0644]
zypp/solver/detail/ProblemSolutionIgnore.h [new file with mode: 0644]
zypp/solver/detail/Resolver.cc [new file with mode: 0644]
zypp/solver/detail/Resolver.h [new file with mode: 0644]
zypp/solver/detail/ResolverUpgrade.cc [new file with mode: 0644]
zypp/solver/detail/Resolver_problems.cc [new file with mode: 0644]
zypp/solver/detail/SATResolver.cc [new file with mode: 0644]
zypp/solver/detail/SATResolver.h [new file with mode: 0644]
zypp/solver/detail/SolutionAction.cc [new file with mode: 0644]
zypp/solver/detail/SolutionAction.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItem.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItem.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemDelete.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemDelete.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemInstall.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemInstall.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemInstallOneOf.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemInstallOneOf.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemLock.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemLock.h [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemUpdate.cc [new file with mode: 0644]
zypp/solver/detail/SolverQueueItemUpdate.h [new file with mode: 0644]
zypp/solver/detail/SystemCheck.cc [new file with mode: 0644]
zypp/solver/detail/SystemCheck.h [new file with mode: 0644]
zypp/solver/detail/Testcase.cc [new file with mode: 0644]
zypp/solver/detail/Testcase.h [new file with mode: 0644]
zypp/solver/detail/Types.h [new file with mode: 0644]
zypp/solver/libzypp_solver.h [new file with mode: 0644]
zypp/target/CommitPackageCache.cc [new file with mode: 0644]
zypp/target/CommitPackageCache.h [new file with mode: 0644]
zypp/target/CommitPackageCacheImpl.cc [new file with mode: 0644]
zypp/target/CommitPackageCacheImpl.h [new file with mode: 0644]
zypp/target/CommitPackageCacheReadAhead.cc [new file with mode: 0644]
zypp/target/CommitPackageCacheReadAhead.h [new file with mode: 0644]
zypp/target/HardLocksFile.cc [new file with mode: 0644]
zypp/target/HardLocksFile.h [new file with mode: 0644]
zypp/target/RequestedLocalesFile.cc [new file with mode: 0644]
zypp/target/RequestedLocalesFile.h [new file with mode: 0644]
zypp/target/SoftLocksFile.cc [new file with mode: 0644]
zypp/target/SoftLocksFile.h [new file with mode: 0644]
zypp/target/TargetCallbackReceiver.cc [new file with mode: 0644]
zypp/target/TargetCallbackReceiver.h [new file with mode: 0644]
zypp/target/TargetException.cc [new file with mode: 0644]
zypp/target/TargetException.h [new file with mode: 0644]
zypp/target/TargetImpl.cc [new file with mode: 0644]
zypp/target/TargetImpl.h [new file with mode: 0644]
zypp/target/hal/HalContext.cc [new file with mode: 0644]
zypp/target/hal/HalContext.h [new file with mode: 0644]
zypp/target/hal/HalException.h [new file with mode: 0644]
zypp/target/modalias/Modalias.cc [new file with mode: 0644]
zypp/target/modalias/Modalias.h [new file with mode: 0644]
zypp/target/rpm/BinHeader.cc [new file with mode: 0644]
zypp/target/rpm/BinHeader.h [new file with mode: 0644]
zypp/target/rpm/RpmCallbacks.cc [new file with mode: 0644]
zypp/target/rpm/RpmCallbacks.h [new file with mode: 0644]
zypp/target/rpm/RpmDb.cc [new file with mode: 0644]
zypp/target/rpm/RpmDb.h [new file with mode: 0644]
zypp/target/rpm/RpmException.cc [new file with mode: 0644]
zypp/target/rpm/RpmException.h [new file with mode: 0644]
zypp/target/rpm/RpmFlags.h [new file with mode: 0644]
zypp/target/rpm/RpmHeader.cc [new file with mode: 0644]
zypp/target/rpm/RpmHeader.h [new file with mode: 0644]
zypp/target/rpm/librpm.h [new file with mode: 0644]
zypp/target/rpm/librpmDb.cc [new file with mode: 0644]
zypp/target/rpm/librpmDb.cv3.cc [new file with mode: 0644]
zypp/target/rpm/librpmDb.h [new file with mode: 0644]
zypp/thread/Mutex.cc [new file with mode: 0644]
zypp/thread/Mutex.h [new file with mode: 0644]
zypp/thread/MutexException.h [new file with mode: 0644]
zypp/thread/MutexLock.h [new file with mode: 0644]
zypp/thread/Once.h [new file with mode: 0644]
zypp/ui/SelFilters.h [new file with mode: 0644]
zypp/ui/Selectable.cc [new file with mode: 0644]
zypp/ui/Selectable.h [new file with mode: 0644]
zypp/ui/SelectableImpl.cc [new file with mode: 0644]
zypp/ui/SelectableImpl.h [new file with mode: 0644]
zypp/ui/SelectableTraits.h [new file with mode: 0644]
zypp/ui/Status.cc [new file with mode: 0644]
zypp/ui/Status.h [new file with mode: 0644]
zypp/ui/UserWantedPackages.cc [new file with mode: 0644]
zypp/ui/UserWantedPackages.h [new file with mode: 0644]
zypp/url/UrlBase.cc [new file with mode: 0644]
zypp/url/UrlBase.h [new file with mode: 0644]
zypp/url/UrlException.h [new file with mode: 0644]
zypp/url/UrlUtils.cc [new file with mode: 0644]
zypp/url/UrlUtils.h [new file with mode: 0644]
zypp/ws/WebpinResult.cc [new file with mode: 0644]
zypp/ws/WebpinResult.h [new file with mode: 0644]
zypp/zypp_detail/ZYppImpl.cc [new file with mode: 0644]
zypp/zypp_detail/ZYppImpl.h [new file with mode: 0644]
zypp/zypp_detail/ZYppReadOnlyHack.h [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f4d9a68
--- /dev/null
@@ -0,0 +1,212 @@
+PROJECT(LIBZYPP)
+SET( PACKAGE "libzypp" )
+# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
+SET( CMAKE_MODULE_PATH ${LIBZYPP_SOURCE_DIR}/cmake/modules )
+cmake_minimum_required(VERSION 2.6)
+
+# allow name libraries by name mixed with full
+# paths
+if(COMMAND cmake_policy)
+  cmake_policy(SET CMP0003 NEW)
+endif(COMMAND cmake_policy)
+
+INCLUDE(ZyppCommon)
+INCLUDE( ${LIBZYPP_SOURCE_DIR}/VERSION.cmake )
+
+MATH( EXPR LIBZYPP_CURRENT "${LIBZYPP_MAJOR} * 100 + ${LIBZYPP_MINOR}" )
+MATH( EXPR LIBZYPP_AGE     "${LIBZYPP_MINOR} - ${LIBZYPP_COMPATMINOR}" )
+# Libtool wanted current:patch:age
+# But cmake is not libtool, it wants the verbatim suffix to libzypp.so
+MATH( EXPR LIBZYPP_SO_FIRST  "${LIBZYPP_CURRENT}-${LIBZYPP_AGE}" )
+SET( VERSION "${LIBZYPP_MAJOR}.${LIBZYPP_MINOR}.${LIBZYPP_PATCH}" )
+
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+CHECK_C_COMPILER_FLAG("-Werror=format-security" CC_FORMAT_SECURITY)
+CHECK_CXX_COMPILER_FLAG("-Werror=format-security" CXX_FORMAT_SECURITY)
+
+SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -fPIC -g -Wall -Woverloaded-virtual -Wnon-virtual-dtor -Wl,-as-needed" )
+SET( CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fno-strict-aliasing -fPIC -g -Wall -Wl,-as-needed" )
+
+set( CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3" )
+set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3" )
+
+IF(${CC_FORMAT_SECURITY})
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=format-security")
+ENDIF(${CC_FORMAT_SECURITY})
+
+IF(${CXX_FORMAT_SECURITY})
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=format-security")
+ENDIF(${CXX_FORMAT_SECURITY})
+
+INCLUDE(CheckFunctionExists)
+CHECK_FUNCTION_EXISTS(pipe2 PIPE2_FOUND)
+IF(${PIPE2_FOUND})
+  ADD_DEFINITIONS(-DHAVE_PIPE2)
+ENDIF(${PIPE2_FOUND})
+
+ADD_DEFINITIONS( -D_FILE_OFFSET_BITS=64 )
+ADD_DEFINITIONS( -DVERSION="${VERSION}" )
+SET( LIBZYPP_VERSION_INFO "${LIBZYPP_SO_FIRST}.${LIBZYPP_AGE}.${LIBZYPP_PATCH}" )
+SET( LIBZYPP_SOVERSION_INFO "${LIBZYPP_SO_FIRST}" )
+
+GENERATE_PACKAGING(${PACKAGE} ${VERSION})
+
+INCLUDE(CPack)
+
+MACRO(ADD_TESTS)
+  FOREACH( loop_var ${ARGV} )
+    SET_SOURCE_FILES_PROPERTIES( ${loop_var}_test.cc COMPILE_FLAGS "-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN -DBOOST_AUTO_TEST_MAIN=\"\" " )
+    ADD_EXECUTABLE( ${loop_var}_test ${loop_var}_test.cc )
+    TARGET_LINK_LIBRARIES( ${loop_var}_test  zypp boost_unit_test_framework zypp_test_utils)
+    ADD_TEST( ${loop_var}_test ${CMAKE_CURRENT_BINARY_DIR}/${loop_var}_test --catch_system_errors=no)
+  ENDFOREACH( loop_var )
+ENDMACRO(ADD_TESTS)
+
+####################################################################
+
+FIND_PACKAGE(Rpm REQUIRED)
+IF ( NOT RPM_FOUND)
+  MESSAGE( FATAL_ERROR " rpm-devel not found" )
+ELSE ( NOT RPM_FOUND)
+  INCLUDE_DIRECTORIES(${RPM_INCLUDE_DIR})
+  # fix includes not relative to rpm
+  INCLUDE_DIRECTORIES(${RPM_INCLUDE_DIR}/rpm)
+  if ( RPM_SUSPECT_VERSION STREQUAL "5.x" )
+       MESSAGE( STATUS "rpm found: enable rpm-4 compat interface." )
+       ADD_DEFINITIONS(-D_RPM_5)
+  elseif ( RPM_SUSPECT_VERSION STREQUAL "4.x" )
+       MESSAGE( STATUS "rpm found: use rpm-4.x interface." )
+       ADD_DEFINITIONS(-D_RPM_4_X)
+  elseif ( RPM_SUSPECT_VERSION STREQUAL "4.4" )
+       MESSAGE( STATUS "rpm found: enable rpm-4.4 legacy interface." )
+       ADD_DEFINITIONS(-D_RPM_4_4)
+  endif ( RPM_SUSPECT_VERSION STREQUAL "5.x" )
+ENDIF( NOT RPM_FOUND)
+
+FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options unit_test_framework)
+IF (Boost_FOUND)
+  MESSAGE( STATUS "boost found: includes in ${Boost_INCLUDE_DIRS}, library in ${Boost_LIBRARY_DIRS}")
+  INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
+  LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
+ENDIF(Boost_FOUND)
+
+FIND_PACKAGE(Gettext REQUIRED)
+IF (GETTEXT_FOUND)
+  MESSAGE(STATUS "Found Gettext: ${GETTEXT_SOURCE}")
+  INCLUDE_DIRECTORIES(${GETTEXT_INCLUDE_DIR})
+ELSE (GETTEXT_FOUND)
+  MESSAGE( FATAL_ERROR "Gettext not found" )
+ENDIF (GETTEXT_FOUND)
+
+FIND_PACKAGE(Curl REQUIRED)
+IF ( NOT CURL_FOUND)
+  MESSAGE( FATAL_ERROR " curl not found" )
+ELSE ( NOT CURL_FOUND)
+  INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
+ENDIF( NOT CURL_FOUND)
+
+FIND_PACKAGE(Libxml REQUIRED)
+IF ( NOT LIBXML_FOUND)
+  MESSAGE( FATAL_ERROR " libxml not found" )
+ELSE ( NOT LIBXML_FOUND)
+  INCLUDE_DIRECTORIES(${LIBXML_INCLUDE_DIR})
+ENDIF( NOT LIBXML_FOUND)
+
+FIND_PACKAGE(ZLIB REQUIRED)
+IF ( NOT ZLIB_FOUND)
+  MESSAGE( FATAL_ERROR " zlib not found" )
+ELSE ( NOT ZLIB_FOUND)
+  INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
+ENDIF( NOT ZLIB_FOUND)
+
+FIND_PACKAGE(Satsolver REQUIRED)
+IF ( NOT SATSOLVER_FOUND )
+  MESSAGE( FATAL_ERROR " libsatsolver not found" )
+ELSE ( NOT SATSOLVER_FOUND )
+  INCLUDE_DIRECTORIES( ${SATSOLVER_INCLUDE_DIR} )
+ENDIF( NOT SATSOLVER_FOUND )
+
+# satsolver uses expat and has open references to it:
+FIND_PACKAGE(EXPAT REQUIRED)
+
+FIND_PACKAGE(OpenSSL REQUIRED)
+
+FIND_PACKAGE(Udev)
+IF ( NOT UDEV_FOUND )
+  FIND_PACKAGE(Hal)
+  IF ( NOT HAL_FOUND )
+    MESSAGE(WARNING "No udev or HAL. CD device detection will be poor")
+  ELSE ( NOT HAL_FOUND )
+      ADD_DEFINITIONS(-DHAVE_HAL)
+      INCLUDE_DIRECTORIES(${HAL_INCLUDE_DIR})
+      INCLUDE_DIRECTORIES(${HAL_INCLUDE_DIR}/hal)
+      # HAL requires working dbus
+      FIND_PACKAGE(Dbus REQUIRED)
+      IF(DBUS_FOUND)
+        INCLUDE_DIRECTORIES(${DBUS_INCLUDE_DIR})
+        INCLUDE_DIRECTORIES(${DBUS_ARCH_INCLUDE_DIR})
+      ENDIF(DBUS_FOUND)
+  ENDIF ( NOT HAL_FOUND )
+ELSE ( NOT UDEV_FOUND )
+  ADD_DEFINITIONS(-DHAVE_UDEV)
+ENDIF ( NOT UDEV_FOUND )
+
+FIND_PACKAGE(libproxy)
+IF ( NOT LIBPROXY_FOUND )
+  MESSAGE( STATUS " libproxy not found" )
+ELSE ( NOT LIBPROXY_FOUND )
+  INCLUDE_DIRECTORIES( ${LIBPROXY_INCLUDE_DIR} )
+  ADD_DEFINITIONS(-D_WITH_LIBPROXY_SUPPORT_)
+ENDIF( NOT LIBPROXY_FOUND )
+
+FIND_PROGRAM( DOXYGEN doxygen )
+IF ( NOT DOXYGEN )
+  MESSAGE( FATAL_ERROR "doxygen not found: install doxygen to build the documentation." )
+ELSE ( NOT DOXYGEN )
+  MESSAGE( STATUS "doxygen found: ${DOXYGEN}" )
+ENDIF ( NOT DOXYGEN )
+
+MESSAGE(STATUS "soname: ${LIBZYPP_VERSION_INFO}")
+MESSAGE(STATUS "version: ${VERSION}")
+
+MESSAGE(STATUS "Writing pkg-config file...")
+CONFIGURE_FILE(${LIBZYPP_SOURCE_DIR}/libzypp.pc.cmake ${LIBZYPP_BINARY_DIR}/libzypp.pc @ONLY)
+INSTALL( FILES ${LIBZYPP_BINARY_DIR}/libzypp.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+
+MESSAGE(STATUS "FindZypp.cmake will be installed in ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules")
+INSTALL( FILES ${LIBZYPP_SOURCE_DIR}/cmake/modules/FindZypp.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules )
+INSTALL( FILES ${LIBZYPP_SOURCE_DIR}/cmake/modules/ZyppCommon.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cmake/Modules )
+
+####################################################################
+# config templates
+# (don't forget to mention them in the .spec file)
+####################################################################
+
+MESSAGE(STATUS "zypp.conf will be installed in ${SYSCONFDIR}/zypp")
+INSTALL( FILES ${LIBZYPP_SOURCE_DIR}/zypp.conf DESTINATION ${SYSCONFDIR}/zypp )
+
+#install systemCheck
+MESSAGE(STATUS "systemCheck will be installed in ${SYSCONFDIR}/zypp")
+INSTALL( FILES ${LIBZYPP_SOURCE_DIR}/systemCheck DESTINATION ${SYSCONFDIR}/zypp )
+
+# logrotate config file
+INSTALL( FILES ${LIBZYPP_SOURCE_DIR}/zypp-history.lr DESTINATION ${SYSCONFDIR}/logrotate.d )
+
+####################################################################
+# SUBDIRECTORIES                                                   #
+####################################################################
+
+ADD_SUBDIRECTORY( zypp )
+#ADD_SUBDIRECTORY( zypp2 )
+# do not build devel by default
+ADD_SUBDIRECTORY( devel EXCLUDE_FROM_ALL )
+ADD_SUBDIRECTORY( tools )
+ADD_SUBDIRECTORY( examples )
+ADD_SUBDIRECTORY( po EXCLUDE_FROM_ALL )
+ADD_SUBDIRECTORY( doc )
+ADD_SUBDIRECTORY( vendor )
+ADD_SUBDIRECTORY( tests EXCLUDE_FROM_ALL )
+
+INCLUDE(CTest)
+ENABLE_TESTING()
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..9cfa7ad
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,21 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+Copyright (C) 2000-2002 Ximian, Inc.
+Copyright (C) 2005 SUSE Linux Products GmbH
+
+ZYpp is licensed under the GNU General Public License version 2
+or later. The text of the GNU General Public License can be viewed at
+http://www.gnu.org/licenses/gpl.html
+
+As a special exception, you have permission to link this program
+with the following libraries and distribute executables, as long as you
+follow the requirements of the GNU GPL in regard to all of the
+software in the executable aside from the following libraries:
+- OpenSSL (http://www.openssl.org)
\ No newline at end of file
diff --git a/Makefile.cvs b/Makefile.cvs
new file mode 100644 (file)
index 0000000..3dfffe4
--- /dev/null
@@ -0,0 +1,30 @@
+# Generic Makefile.cvs for CMake-based projects:
+#
+# Create a subdirecory build/ and call cmake from there with /usr prefix.
+#
+# Intentionally using /usr rather than the default /usr/local since this is for
+# internal use, and we are the distribution makers: /usr/local is off limits
+# for us.
+#
+# Author: Stefan Hundhammer <sh@suse.de>
+
+BUILD_SUBDIR           = build
+HERE_FROM_BUILD_SUBDIR = ..
+PREFIX                 = /usr
+CMAKE                  = /usr/bin/cmake
+
+all:   cmake
+
+
+cmake: create-build-subdir create-toplevel-makefile
+       ( cd $(BUILD_SUBDIR) && $(CMAKE) -DCMAKE_INSTALL_PREFIX=$(PREFIX) $(HERE_FROM_BUILD_SUBDIR) )
+
+
+create-build-subdir:
+       test -d $(BUILD_SUBDIR) || mkdir $(BUILD_SUBDIR)
+
+
+create-toplevel-makefile:
+       echo 'all:'                                              >Makefile
+       echo -e '\t$$(MAKE) $$(MAKEFLAGS) -C $(BUILD_SUBDIR)'   >>Makefile
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..c4378d6
--- /dev/null
+++ b/TODO
@@ -0,0 +1,9 @@
+
+API Cleanups:
+=============
+
+Source.h
+- remove const from return types:
+  ie: const Pathname providePackage( Package::constPtr package );
+- pass by reference
+  resolvables(zypp::Resolvable::Kind kind) const;
diff --git a/VERSION.cmake b/VERSION.cmake
new file mode 100644 (file)
index 0000000..45161a4
--- /dev/null
@@ -0,0 +1,68 @@
+# ==================================================
+# Versioning
+# ==========
+#
+# MAJOR        Major number for this branch.
+#
+# MINOR        The most recent interface number this
+#              library implements.
+#
+# COMPATMINOR  The latest binary compatible minor number
+#              this library implements.
+#
+# PATCH        The implementation number of the current interface.
+#
+#
+# - The package VERSION will be MAJOR.MINOR.PATCH.
+#
+# - Libtool's -version-info will be derived from MAJOR, MINOR, PATCH
+#   and COMPATMINOR (see configure.ac).
+#
+# - Changing MAJOR always breaks binary compatibility.
+#
+# - Changing MINOR doesn't break binary compatibility by default.
+#   Only if COMPATMINOR is changed as well.
+#
+#
+# 1) After branching from TRUNK increment TRUNKs MAJOR and
+#    start with version `MAJOR.0.0' and also set COMPATMINOR to 0.
+#
+# 2) Update the version information only immediately before a public release
+#    of your software. More frequent updates are unnecessary, and only guarantee
+#    that the current interface number gets larger faster.
+#
+# 3) If the library source code has changed at all since the last update,
+#    then increment PATCH.
+#
+# 4) If any interfaces have been added, removed, or changed since the last
+#    update, increment MINOR, and set PATCH to 0.
+#
+# 5) If any interfaces have been added since the last public release, then
+#    leave COMPATMINOR unchanged. (binary compatible change)
+#
+# 6) If any interfaces have been removed since the last public release, then
+#    set COMPATMINOR to MINOR. (binary incompatible change)
+# ==================================================
+
+#=======
+# - Update version according to your changes,
+#   but based on 'LAST RELEASED:' below. I.e
+#   there's no need to increase LIBZYPP_MINOR
+#   if it already differs from 'LAST RELEASED:'.
+#
+# - MOST IMPORTANT:
+#   Before you submitt to autobuild, rmember the
+#   new version in 'LAST RELEASED:', and add a
+#   note in the changes file.
+#
+# - Consider calling ./mkChangelog to edit the
+#   changes file. See './mkChangelog -h' for help.
+#
+SET(LIBZYPP_MAJOR "9")
+SET(LIBZYPP_COMPATMINOR "8")
+SET(LIBZYPP_MINOR "8")
+SET(LIBZYPP_PATCH "3")
+#
+# LAST RELEASED: 9.8.3 (8)
+# (The number in parenthesis is LIBZYPP_COMPATMINOR)
+#=======
diff --git a/cmake/modules/FindCurl.cmake b/cmake/modules/FindCurl.cmake
new file mode 100644 (file)
index 0000000..8576ddb
--- /dev/null
@@ -0,0 +1,28 @@
+
+if(CURL_INCLUDE_DIR AND CURL_LIBRARY)
+       # Already in cache, be silent
+       set(CURL_FIND_QUIETLY TRUE)     
+endif(CURL_INCLUDE_DIR AND CURL_LIBRARY)
+
+set(CURL_LIBRARY)
+set(CURL_INCLUDE_DIR)
+
+FIND_PATH(CURL_INCLUDE_DIR curl/curl.h
+       /usr/include
+       /usr/local/include
+)
+
+FIND_LIBRARY(CURL_LIBRARY NAMES curl
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+if(CURL_INCLUDE_DIR AND CURL_LIBRARY)
+   MESSAGE( STATUS "curl found: includes in ${CURL_INCLUDE_DIR}, library in ${CURL_LIBRARY}")
+   set(CURL_FOUND TRUE)
+else(CURL_INCLUDE_DIR AND CURL_LIBRARY)
+   MESSAGE( STATUS "curl not found")
+endif(CURL_INCLUDE_DIR AND CURL_LIBRARY)
+
+MARK_AS_ADVANCED(CURL_INCLUDE_DIR CURL_LIBRARY)
\ No newline at end of file
diff --git a/cmake/modules/FindDbus.cmake b/cmake/modules/FindDbus.cmake
new file mode 100644 (file)
index 0000000..d79cd9a
--- /dev/null
@@ -0,0 +1,37 @@
+
+if(DBUS_INCLUDE_DIR AND DBUS_LIBRARY AND DBUS_ARCH_INCLUDE_DIR)
+       # Already in cache, be silent
+       set(DBUS_FIND_QUIETLY TRUE)     
+endif(DBUS_INCLUDE_DIR AND DBUS_LIBRARY AND DBUS_ARCH_INCLUDE_DIR)
+
+set(DBUS_LIBRARY)
+set(DBUS_INCLUDE_DIR)
+set(DBUS_ARCH_INCLUDE_DIR)
+
+FIND_PATH(DBUS_INCLUDE_DIR dbus/dbus.h
+       /usr/include
+       /usr/include/dbus-1.0
+       /usr/local/include
+)
+
+FIND_PATH(DBUS_ARCH_INCLUDE_DIR dbus/dbus-arch-deps.h
+       /usr/lib/include
+       /usr/lib/dbus-1.0/include
+  /usr/lib64/include
+  /usr/lib64/dbus-1.0/include
+)
+
+FIND_LIBRARY(DBUS_LIBRARY NAMES dbus-1 dbus
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+if(DBUS_INCLUDE_DIR AND DBUS_LIBRARY AND DBUS_ARCH_INCLUDE_DIR)
+   MESSAGE( STATUS "dbus found: includes in ${DBUS_INCLUDE_DIR}, library in ${DBUS_LIBRARY}")
+   set(DBUS_FOUND TRUE)
+else(DBUS_INCLUDE_DIR AND DBUS_LIBRARY AND DBUS_ARCH_INCLUDE_DIR)
+   MESSAGE( STATUS "dbus not found")
+endif(DBUS_INCLUDE_DIR AND DBUS_LIBRARY AND DBUS_ARCH_INCLUDE_DIR)
+
+MARK_AS_ADVANCED(DBUS_INCLUDE_DIR DBUS_LIBRARY DBUS_ARCH_INCLUDE_DIR)
\ No newline at end of file
diff --git a/cmake/modules/FindGettext.cmake b/cmake/modules/FindGettext.cmake
new file mode 100644 (file)
index 0000000..a539dd2
--- /dev/null
@@ -0,0 +1,132 @@
+# - Find GNU gettext tools
+# This module looks for the GNU gettext tools. This module defines the
+# following values:
+#  GETTEXT_MSGMERGE_EXECUTABLE: the full path to the msgmerge tool.
+#  GETTEXT_MSGFMT_EXECUTABLE: the full path to the msgfmt tool.
+#  GETTEXT_FOUND: True if gettext has been found.
+#
+# Additionally it provides the following macros:
+# GETTEXT_CREATE_TRANSLATIONS ( _moBasename [ALL] file1 ... fileN )
+#    This will create a target "translations" which will convert the
+#    given input po files into the binary output mo file. If the
+#    ALL option is used, the translations will also be created when
+#    building the default target.
+
+FIND_PROGRAM(GETTEXT_MSGMERGE_EXECUTABLE msgmerge)
+
+FIND_PROGRAM(GETTEXT_MSGFMT_EXECUTABLE msgfmt)
+
+#
+# Macro to be called if .po files are shipped as tar ball.
+#
+# _translation_set_basename: Serves two purposes; a) the stem of the
+# default tarball %{_translation_set_basename}-po.tar.bz2 unless its over
+# riden by -DUSE_TRANSLATION_SET; b) the basename of the .gmo files.
+#
+# We expect a po-file tarball to unpack the .po file to the current
+# directory!
+#
+MACRO( GETTEXT_CREATE_TARBALL_TRANSLATIONS _translation_set_basename )
+
+        IF( NOT USE_TRANSLATION_SET )
+                SET( USE_TRANSLATION_SET ${_translation_set_basename} )
+        ENDIF( NOT USE_TRANSLATION_SET )
+
+        SET( TRANSLATION_SET "${USE_TRANSLATION_SET}-po.tar.bz2" )
+        MESSAGE( STATUS "Translation set: ${TRANSLATION_SET}" )
+
+        # For those not familiar with 'sed': the tarball might list './' and './*.po'.
+        # We process just the '*.po' lines and strip off any leading './'.
+        EXECUTE_PROCESS(
+                COMMAND tar tfj ${CMAKE_CURRENT_SOURCE_DIR}/${TRANSLATION_SET}
+                COMMAND sed -n "/\\.po$/s%.*/%%p"
+                COMMAND awk "{printf $1\";\"}"
+                OUTPUT_VARIABLE TRANSLATION_SET_CONTENT
+        )
+        MESSAGE( STATUS "Translations: ${TRANSLATION_SET_CONTENT}" )
+
+        # Create 'LANG.po's from po.tar.bz2
+        ADD_CUSTOM_COMMAND(
+                OUTPUT ${TRANSLATION_SET_CONTENT}
+                COMMAND tar xfj ${CMAKE_CURRENT_SOURCE_DIR}/${TRANSLATION_SET}
+                DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${TRANSLATION_SET}
+        )
+
+        # LANG.po ->msgfmt-> LANG.gmo
+        SET( _gmoFiles )
+        FOREACH( _currentPoFile ${TRANSLATION_SET_CONTENT} )
+
+                GET_FILENAME_COMPONENT( _lang ${_currentPoFile} NAME_WE )
+                SET( _gmoFile "${_lang}.gmo" )
+                SET( _gmoFiles ${_gmoFiles} ${_gmoFile} )
+
+                ADD_CUSTOM_COMMAND(
+                        OUTPUT ${_gmoFile}
+                        COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_currentPoFile}
+                        DEPENDS ${_currentPoFile}
+                )
+
+                INSTALL(
+                        FILES ${CMAKE_CURRENT_BINARY_DIR}/${_gmoFile}
+                        DESTINATION share/locale/${_lang}/LC_MESSAGES
+                        RENAME ${_translation_set_basename}.mo
+                )
+
+                # the docs claim it can handle a list, but
+                SET_DIRECTORY_PROPERTIES( PROPERTIES
+                        ADDITIONAL_MAKE_CLEAN_FILES ${_currentPoFile}
+                        ADDITIONAL_MAKE_CLEAN_FILES ${_gmoFile}
+                )
+
+        ENDFOREACH( _currentPoFile )
+
+        # build all .gmo files
+        ADD_CUSTOM_TARGET(
+                translations ALL
+                DEPENDS ${_gmoFiles}
+        )
+
+ENDMACRO( GETTEXT_CREATE_TARBALL_TRANSLATIONS )
+
+#
+# Macro to be called if .po files are part of the source tree.
+#
+MACRO(GETTEXT_CREATE_TRANSLATIONS _moBasename _firstPoFile)
+
+   SET(_gmoFiles)
+
+   SET(_addToAll)
+   IF(${_firstPoFile} STREQUAL "ALL")
+      SET(_addToAll "ALL")
+      SET(_firstPoFile)
+   ENDIF(${_firstPoFile} STREQUAL "ALL")
+
+   FOREACH (_currentPoFile ${_firstPoFile} ${ARGN})
+      GET_FILENAME_COMPONENT(_absFile ${_currentPoFile} ABSOLUTE)
+      GET_FILENAME_COMPONENT(_abs_PATH ${_absFile} PATH)
+      GET_FILENAME_COMPONENT(_lang ${_absFile} NAME_WE)
+      SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo)
+
+      ADD_CUSTOM_COMMAND(
+         OUTPUT ${_gmoFile}
+         COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_absFile}
+         DEPENDS ${_absFile}
+      )
+
+      INSTALL(FILES ${_gmoFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME ${_moBasename}.mo)
+      SET(_gmoFiles ${_gmoFiles} ${_gmoFile})
+
+   ENDFOREACH (_currentPoFile )
+
+   ADD_CUSTOM_TARGET(translations ${_addToAll} DEPENDS ${_gmoFiles})
+
+ENDMACRO(GETTEXT_CREATE_TRANSLATIONS )
+
+IF (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE )
+   SET(GETTEXT_FOUND TRUE)
+ELSE (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE )
+   SET(GETTEXT_FOUND FALSE)
+   IF (GetText_REQUIRED)
+      MESSAGE(FATAL_ERROR "GetText not found")
+   ENDIF (GetText_REQUIRED)
+ENDIF (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE )
diff --git a/cmake/modules/FindHal.cmake b/cmake/modules/FindHal.cmake
new file mode 100644 (file)
index 0000000..58c149a
--- /dev/null
@@ -0,0 +1,35 @@
+
+if(HAL_INCLUDE_DIR AND HAL_LIBRARY AND HAL_STORAGE_LIBRARY)
+       # Already in cache, be silent
+       set(HAL_FIND_QUIETLY TRUE)      
+endif(HAL_INCLUDE_DIR AND HAL_LIBRARY AND HAL_STORAGE_LIBRARY)
+
+set(HAL_LIBRARY)
+set(HAL_INCLUDE_DIR)
+set(HAL_STORAGE_LIBRARY)
+
+FIND_PATH(HAL_INCLUDE_DIR hal/libhal.h
+       /usr/include
+       /usr/local/include
+)
+
+FIND_LIBRARY(HAL_LIBRARY NAMES hal
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+FIND_LIBRARY(HAL_STORAGE_LIBRARY NAMES hal-storage
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+if(HAL_INCLUDE_DIR AND HAL_LIBRARY AND HAL_STORAGE_LIBRARY)
+   MESSAGE( STATUS "hal found: includes in ${HAL_INCLUDE_DIR}, library in ${HAL_LIBRARY}")
+   set(HAL_FOUND TRUE)
+else(HAL_INCLUDE_DIR AND HAL_LIBRARY AND HAL_STORAGE_LIBRARY)
+   MESSAGE( STATUS "hal not found")
+endif(HAL_INCLUDE_DIR AND HAL_LIBRARY AND HAL_STORAGE_LIBRARY)
+
+MARK_AS_ADVANCED(HAL_INCLUDE_DIR HAL_LIBRARY HAL_STORAGE_LIBRARY)
diff --git a/cmake/modules/FindLibxml.cmake b/cmake/modules/FindLibxml.cmake
new file mode 100644 (file)
index 0000000..cb2ae2a
--- /dev/null
@@ -0,0 +1,29 @@
+
+if(LIBXML_INCLUDE_DIR AND LIBXML_LIBRARY)
+       # Already in cache, be silent
+       set(LIBXML_FIND_QUIETLY TRUE)   
+endif(LIBXML_INCLUDE_DIR AND LIBXML_LIBRARY)
+
+set(LIBXML_LIBRARY)
+set(LIBXML_INCLUDE_DIR)
+
+FIND_PATH(LIBXML_INCLUDE_DIR libxml/parser.h
+       /usr/include
+       /usr/include/libxml2
+       /usr/local/include
+)
+
+FIND_LIBRARY(LIBXML_LIBRARY NAMES xml2
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+if(LIBXML_INCLUDE_DIR AND LIBXML_LIBRARY)
+   MESSAGE( STATUS "libxml found: includes in ${LIBXML_INCLUDE_DIR}, library in ${LIBXML_LIBRARY}")
+   set(LIBXML_FOUND TRUE)
+else(LIBXML_INCLUDE_DIR AND LIBXML_LIBRARY)
+   MESSAGE( STATUS "libxml not found")
+endif(LIBXML_INCLUDE_DIR AND LIBXML_LIBRARY)
+
+MARK_AS_ADVANCED(LIBXML_INCLUDE_DIR LIBXML_LIBRARY)
\ No newline at end of file
diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake
new file mode 100644 (file)
index 0000000..dd967d1
--- /dev/null
@@ -0,0 +1,100 @@
+# - Try to find the OpenSSL encryption library\r
+# Once done this will define\r
+#\r
+#  OPENSSL_FOUND - system has the OpenSSL library\r
+#  OPENSSL_INCLUDE_DIR - the OpenSSL include directory\r
+#  OPENSSL_LIBRARIES - The libraries needed to use OpenSSL\r
+\r
+if (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)\r
+\r
+  # in cache already\r
+  SET(OPENSSL_FOUND TRUE)\r
+\r
+else (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)\r
+\r
+  FIND_PATH(OPENSSL_INCLUDE_DIR openssl/ssl.h\r
+     /usr/include/\r
+     /usr/local/include/\r
+     $ENV{ProgramFiles}/OpenSSL/include/\r
+     $ENV{SystemDrive}/OpenSSL/include/\r
+  )\r
+\r
+  if(WIN32 AND MSVC)\r
+      # /MD and /MDd are the standard values - if somone wants to use\r
+      # others, the libnames have to change here too\r
+      # see http://www.openssl.org/support/faq.html#PROG2 for their meaning\r
+\r
+      FIND_LIBRARY(SSL_EAY_DEBUG NAMES ssleay32MDd\r
+        PATHS\r
+        $ENV{ProgramFiles}/OpenSSL/lib/VC/\r
+        $ENV{SystemDrive}/OpenSSL/lib/VC/\r
+      )\r
+      FIND_LIBRARY(SSL_EAY_RELEASE NAMES ssleay32MD\r
+        PATHS\r
+        $ENV{ProgramFiles}/OpenSSL/lib/VC/\r
+        $ENV{SystemDrive}/OpenSSL/lib/VC/\r
+      )\r
+      FIND_LIBRARY(LIB_EAY_DEBUG NAMES libeay32MDd\r
+        PATHS\r
+        $ENV{ProgramFiles}/OpenSSL/lib/VC/\r
+        $ENV{SystemDrive}/OpenSSL/lib/VC/\r
+      )\r
+      FIND_LIBRARY(LIB_EAY_RELEASE NAMES libeay32MD\r
+        PATHS\r
+        $ENV{ProgramFiles}/OpenSSL/lib/VC/\r
+        $ENV{SystemDrive}/OpenSSL/lib/VC/\r
+      )\r
+\r
+      IF(MSVC_IDE)\r
+        IF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)\r
+            SET(OPENSSL_LIBRARIES optimized ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} debug ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG})\r
+        ELSE(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)\r
+          MESSAGE(FATAL_ERROR "Could not find the debug and release version of openssl")\r
+        ENDIF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)\r
+      ELSE(MSVC_IDE)\r
+        STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER)\r
+        IF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)\r
+          SET(OPENSSL_LIBRARIES ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG})\r
+        ELSE(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)\r
+          SET(OPENSSL_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE})\r
+        ENDIF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)\r
+      ENDIF(MSVC_IDE)\r
+      MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE LIB_EAY_DEBUG LIB_EAY_RELEASE)\r
+  else(WIN32 AND MSVC)\r
+\r
+   FIND_LIBRARY(OPENSSL_LIBRARIES NAMES ssl ssleay32 ssleay32MD libeay32 libeay32MD\r
+      PATHS\r
+      /usr/lib\r
+      /usr/local/lib\r
+   )\r
+   FIND_LIBRARY(CRYPTO_LIBRARIES crypto\r
+      PATHS\r
+      /usr/lib\r
+      /usr/local/lib\r
+   )\r
+\r
+  endif(WIN32 AND MSVC)\r
+\r
+  if (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)\r
+     set(OPENSSL_FOUND TRUE)\r
+  endif (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)\r
+\r
+  if (CRYPTO_LIBRARIES)\r
+     set(CRYPTO_FOUND TRUE)\r
+     message(STATUS "Found libcrypto: ${CRYPTO_LIBRARIES}")\r
+  endif (CRYPTO_LIBRARIES)\r
+\r
+  if (OPENSSL_FOUND)\r
+     if (NOT OpenSSL_FIND_QUIETLY)\r
+        message(STATUS "Found OpenSSL: ${OPENSSL_LIBRARIES}")\r
+     endif (NOT OpenSSL_FIND_QUIETLY)\r
+  else (OPENSSL_FOUND)\r
+     if (OpenSSL_FIND_REQUIRED)\r
+        message(FATAL_ERROR "Could NOT find OpenSSL")\r
+     endif (OpenSSL_FIND_REQUIRED)\r
+  endif (OPENSSL_FOUND)\r
+\r
+  MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES)\r
+\r
+endif (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)\r
+\r
diff --git a/cmake/modules/FindRpm.cmake b/cmake/modules/FindRpm.cmake
new file mode 100644 (file)
index 0000000..29696e3
--- /dev/null
@@ -0,0 +1,50 @@
+
+if(RPM_INCLUDE_DIR AND RPM_LIBRARY)
+       # Already in cache, be silent
+       set(RPM_FIND_QUIETLY TRUE)
+endif(RPM_INCLUDE_DIR AND RPM_LIBRARY)
+
+set(RPM_LIBRARY)
+set(RPM_INCLUDE_DIR)
+
+FIND_PATH(RPM_INCLUDE_DIR rpm/rpmdb.h
+       /usr/include
+       /usr/local/include
+)
+
+set(RPM_SUSPECT_VERSION "RPM_SUSPECT_VERSION-NOTFOUND" )
+if ( RPM_INCLUDE_DIR )
+       FIND_PATH(RPM_SUSPECT_VERSION rpm/rpm4compat.h
+               ${RPM_INCLUDE_DIR}
+               NO_DEFAULT_PATH
+       )
+       if ( RPM_SUSPECT_VERSION )
+               set(RPM_SUSPECT_VERSION "5.x" )
+       else ( RPM_SUSPECT_VERSION )
+               FIND_PATH(RPM_SUSPECT_VERSION rpm/rpmlegacy.h
+                       ${RPM_INCLUDE_DIR}
+                       NO_DEFAULT_PATH
+               )
+               if ( RPM_SUSPECT_VERSION )
+                       set(RPM_SUSPECT_VERSION "4.x" )
+               else ( RPM_SUSPECT_VERSION )
+                       set(RPM_SUSPECT_VERSION "4.4" )
+               endif ( RPM_SUSPECT_VERSION )
+       endif ( RPM_SUSPECT_VERSION )
+endif ( RPM_INCLUDE_DIR )
+
+
+FIND_LIBRARY(RPM_LIBRARY NAMES rpm
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+if(RPM_INCLUDE_DIR AND RPM_LIBRARY)
+   MESSAGE( STATUS "rpm found: includes in ${RPM_INCLUDE_DIR}, library in ${RPM_LIBRARY} (suspect ${RPM_SUSPECT_VERSION})")
+   set(RPM_FOUND TRUE)
+else(RPM_INCLUDE_DIR AND RPM_LIBRARY)
+   MESSAGE( STATUS "rpm not found")
+endif(RPM_INCLUDE_DIR AND RPM_LIBRARY)
+
+MARK_AS_ADVANCED(RPM_INCLUDE_DIR RPM_LIBRARY)
\ No newline at end of file
diff --git a/cmake/modules/FindSatsolver.cmake b/cmake/modules/FindSatsolver.cmake
new file mode 100644 (file)
index 0000000..1c462bf
--- /dev/null
@@ -0,0 +1,40 @@
+
+if(SATSOLVER_INCLUDE_DIR AND SATSOLVER_LIBRARY AND SATSOLVER_EXT_LIBRARY)
+       # Already in cache, be silent
+       set(SATSOLVER_FIND_QUIETLY TRUE)
+endif(SATSOLVER_INCLUDE_DIR AND SATSOLVER_LIBRARY AND SATSOLVER_EXT_LIBRARY)
+
+set(SATSOLVER_LIBRARY)
+set(SATSOLVER_EXT_LIBRARY)
+set(SATSOLVER_INCLUDE_DIR)
+
+FIND_PATH(SATSOLVER_INCLUDE_DIR satsolver/solvable.h
+       ${CMAKE_INSTALL_PREFIX}/include
+       /usr/local/include
+       /usr/include
+)
+
+FIND_LIBRARY(SATSOLVER_LIBRARY NAMES satsolver
+       PATHS
+       ${CMAKE_INSTALL_PREFIX}/lib
+       /usr/local/lib
+       /usr/lib
+)
+
+FIND_LIBRARY(SATSOLVER_EXT_LIBRARY NAMES satsolverext
+       PATHS
+       ${CMAKE_INSTALL_PREFIX}/lib
+       /usr/local/lib
+       /usr/lib
+)
+
+if(SATSOLVER_INCLUDE_DIR AND SATSOLVER_LIBRARY)
+   MESSAGE( STATUS "satsolver found: includes in ${SATSOLVER_INCLUDE_DIR}, library in ${SATSOLVER_LIBRARY} ${SATSOLVER_EXT_LIBRARY} ")
+   set(SATSOLVER_FOUND TRUE)
+else(SATSOLVER_INCLUDE_DIR AND SATSOLVER_LIBRARY)
+   MESSAGE( STATUS "** satsolver not found")
+   MESSAGE( STATUS "** install package libsatsolver-devel")
+   MESSAGE( STATUS "** (http://svn.opensuse.org/svn/zypp/trunk/sat-solver)")
+endif(SATSOLVER_INCLUDE_DIR AND SATSOLVER_LIBRARY)
+
+MARK_AS_ADVANCED(SATSOLVER_INCLUDE_DIR SATSOLVER_LIBRARY)
diff --git a/cmake/modules/FindUdev.cmake b/cmake/modules/FindUdev.cmake
new file mode 100644 (file)
index 0000000..afc2854
--- /dev/null
@@ -0,0 +1,23 @@
+
+SET( UDEV_LIBRARY )
+SET( UDEV_INCLUDE_DIR )
+
+FIND_PATH( UDEV_INCLUDE_DIR libudev.h
+       /usr/include
+       /usr/local/include
+)
+
+FIND_LIBRARY( UDEV_LIBRARY NAMES udev
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+# check if udev is usable for us
+INCLUDE (CheckSymbolExists)
+SET(CMAKE_REQUIRED_LIBRARIES udev)
+CHECK_SYMBOL_EXISTS(udev_enumerate_new libudev.h USABLE_UDEV)
+SET(CMAKE_REQUIRED_LIBRARIES "")
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS( Udev DEFAULT_MSG UDEV_LIBRARY UDEV_INCLUDE_DIR USABLE_UDEV)
+MARK_AS_ADVANCED(  UDEV_LIBRARY UDEV_INCLUDE_DIR )
diff --git a/cmake/modules/FindZsync.cmake b/cmake/modules/FindZsync.cmake
new file mode 100644 (file)
index 0000000..abb3896
--- /dev/null
@@ -0,0 +1,34 @@
+
+if(ZSYNC_INCLUDE_DIR AND ZSYNC_LIBRARY)
+       # Already in cache, be silent
+       set(ZSYNC_FIND_QUIETLY TRUE)    
+endif(ZSYNC_INCLUDE_DIR AND ZSYNC_LIBRARY)
+
+set(ZSYNC_LIBRARY)
+set(ZSYNC_INCLUDE_DIR)
+
+FIND_PATH(ZSYNC_INCLUDE_DIR zsync.h
+       /usr/include
+       /usr/local/include
+)
+
+FIND_LIBRARY(ZSYNC_LIBRARY NAMES zsync
+       PATHS
+       /usr/lib
+       /usr/local/lib
+)
+
+FIND_LIBRARY(RCKSUM_LIBRARY NAMES rcksum
+  PATHS
+  /usr/lib
+  /usr/local/lib
+)
+
+if(ZSYNC_INCLUDE_DIR AND ZSYNC_LIBRARY AND RCKSUM_LIBRARY)
+   MESSAGE( STATUS "zsync found: includes in ${ZSYNC_INCLUDE_DIR}, library in ${ZSYNC_LIBRARY}")
+   set(ZSYNC_FOUND TRUE)
+else(ZSYNC_INCLUDE_DIR AND ZSYNC_LIBRARY)
+   MESSAGE( STATUS "zsync not found")
+endif(ZSYNC_INCLUDE_DIR AND ZSYNC_LIBRARY AND RCKSUM_LIBRARY)
+
+MARK_AS_ADVANCED(ZSYNC_INCLUDE_DIR ZSYNC_LIBRARY RCKSUM_LIBRARY)
\ No newline at end of file
diff --git a/cmake/modules/FindZypp.cmake b/cmake/modules/FindZypp.cmake
new file mode 100644 (file)
index 0000000..4091c4f
--- /dev/null
@@ -0,0 +1,51 @@
+
+IF (DEFINED ZYPP_PREFIX)
+  MESSAGE(STATUS "ZYpp library prefix set to ${ZYPP_PREFIX}")
+ELSE (DEFINED ZYPP_PREFIX)
+  MESSAGE(STATUS "ZYpp path not set. Looking for it.")
+ENDIF (DEFINED ZYPP_PREFIX)
+
+if(ZYPP_INCLUDE_DIR AND ZYPP_LIBRARY)
+       # Already in cache, be silent
+       SET(ZYPP_FIND_QUIETLY TRUE)     
+endif(ZYPP_INCLUDE_DIR AND ZYPP_LIBRARY)
+
+set(ZYPP_LIBRARY)
+set(ZYPP_INCLUDE_DIR)
+
+IF (DEFINED ZYPP_PREFIX)
+  MESSAGE( STATUS "Looking in ${ZYPP_PREFIX}")
+  FIND_PATH(ZYPP_INCLUDE_DIR zypp/ZYpp.h
+    ${ZYPP_PREFIX}/include
+    NO_DEFAULT_PATH
+    NO_SYSTEM_ENVIRONMENT_PATH
+    NO_CMAKE_SYSTEM_PATH
+  )
+  FIND_LIBRARY(ZYPP_LIBRARY NAMES zypp
+    PATHS
+    ${ZYPP_PREFIX}/lib
+    ${ZYPP_PREFIX}/lib64
+    NO_DEFAULT_PATH
+    NO_SYSTEM_ENVIRONMENT_PATH
+    NO_CMAKE_SYSTEM_PATH
+  )
+ELSE (DEFINED ZYPP_PREFIX)
+  FIND_PATH(ZYPP_INCLUDE_DIR zypp/ZYpp.h
+    /usr/include
+    /usr/local/include
+  )
+  FIND_LIBRARY(ZYPP_LIBRARY NAMES zypp
+    PATHS
+    /usr/lib
+    /usr/local/lib
+  )
+ENDIF (DEFINED ZYPP_PREFIX)
+
+if(ZYPP_INCLUDE_DIR AND ZYPP_LIBRARY)
+   MESSAGE( STATUS "ZYpp found: includes in ${ZYPP_INCLUDE_DIR}, library in ${ZYPP_LIBRARY}")
+   set(ZYPP_FOUND TRUE)
+else(ZYPP_INCLUDE_DIR AND ZYPP_LIBRARY)
+   MESSAGE( FATAL "ZYpp not found")
+endif(ZYPP_INCLUDE_DIR AND ZYPP_LIBRARY)
+
+MARK_AS_ADVANCED(ZYPP_INCLUDE_DIR ZYPP_LIBRARY)
diff --git a/cmake/modules/Findlibproxy.cmake b/cmake/modules/Findlibproxy.cmake
new file mode 100644 (file)
index 0000000..a5303df
--- /dev/null
@@ -0,0 +1,7 @@
+# The "real" libproxy provides its own Findlibproxy.cmake but saner, simpler
+# alternatives like the PacRunner replacement which *just* queries PacRunner
+# directly will only provide a .pc file. So use pkg-config to find it...
+
+INCLUDE ( FindPkgConfig )
+
+PKG_SEARCH_MODULE( LIBPROXY libproxy-1.0 )
diff --git a/cmake/modules/ZyppCommon.cmake b/cmake/modules/ZyppCommon.cmake
new file mode 100644 (file)
index 0000000..4c33e07
--- /dev/null
@@ -0,0 +1,139 @@
+# Library
+IF ( DEFINED LIB )
+  SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}" )
+ELSE ( DEFINED  LIB )
+  IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+    SET( LIB_SUFFIX "64" )
+  ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+  SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" )
+ENDIF ( DEFINED  LIB )
+MESSAGE(STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}" )
+
+# system configuration dir (etc)
+IF( NOT DEFINED SYSCONFDIR )
+  IF ( ${CMAKE_INSTALL_PREFIX} STREQUAL "/usr" )
+    # if installing in usr, set sysconfg to etc
+    SET( SYSCONFDIR /etc )
+  ELSE ( ${CMAKE_INSTALL_PREFIX} STREQUAL "/usr" )
+    SET ( SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc" )
+  ENDIF ( ${CMAKE_INSTALL_PREFIX} STREQUAL "/usr" )
+ENDIF( NOT DEFINED SYSCONFDIR )
+MESSAGE(STATUS "Config files will be installed in ${SYSCONFDIR}" )
+
+# usr INSTALL_PREFIX
+
+IF( DEFINED CMAKE_INSTALL_PREFIX )
+  SET( INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} )
+ELSE( DEFINED CMAKE_INSTALL_PREFIX )
+  SET( INSTALL_PREFIX /usr )
+ENDIF( DEFINED CMAKE_INSTALL_PREFIX )
+
+# system configuration dir (etc)
+IF( NOT DEFINED MANDIR )
+  SET( MANDIR ${INSTALL_PREFIX}/share/man )
+ENDIF( NOT DEFINED MANDIR )
+MESSAGE( "** Manual files will be installed in ${MANDIR}" )
+
+####################################################################
+# CONFIGURATION                                                    #
+####################################################################
+
+IF( NOT DEFINED DOC_INSTALL_DIR )
+  SET( DOC_INSTALL_DIR
+     "${CMAKE_INSTALL_PREFIX}/share/doc/packages/${PACKAGE}"
+     CACHE PATH "The install dir for documentation (default prefix/share/doc/packages/${PACKAGE})"
+     FORCE
+  )
+ENDIF( NOT DEFINED DOC_INSTALL_DIR )
+
+####################################################################
+# INCLUDES                                                         #
+####################################################################
+
+#SET (CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
+INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} SYSTEM )
+
+
+####################################################################
+# RPM SPEC                                                         #
+####################################################################
+
+MACRO(SPECFILE)
+  MESSAGE(STATUS "Writing spec file...")
+  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/${PACKAGE}.spec.cmake ${CMAKE_BINARY_DIR}/package/${PACKAGE}.spec @ONLY)
+  MESSAGE(STATUS "I hate you rpm-lint...!!!")
+  IF (EXISTS ${CMAKE_SOURCE_DIR}/package/${PACKAGE}-rpmlint.cmake)
+    CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/package/${PACKAGE}-rpmlint.cmake ${CMAKE_BINARY_DIR}/package/${PACKAGE}-rpmlintrc @ONLY)
+  ENDIF (EXISTS ${CMAKE_SOURCE_DIR}/package/${PACKAGE}-rpmlint.cmake)
+ENDMACRO(SPECFILE)
+
+MACRO(PKGCONFGFILE)
+  MESSAGE(STATUS "Writing pkg-config file...")
+  CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libzypp.pc.cmake ${CMAKE_BINARY_DIR}/libzypp.pc @ONLY)
+  INSTALL( FILES ${CMAKE_BINARY_DIR}/libzypp.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+ENDMACRO(PKGCONFGFILE)
+
+####################################################################
+# INSTALL                                                          #
+####################################################################
+
+MACRO(GENERATE_PACKAGING PACKAGE VERSION)
+
+  # The following components are regex's to match anywhere (unless anchored)
+  # in absolute path + filename to find files or directories to be excluded
+  # from source tarball.
+  SET (CPACK_SOURCE_IGNORE_FILES
+  # hidden files
+  "/\\\\..+$"
+  # temporary files
+  "\\\\.swp$"
+  # backup files
+  "~$"
+  # others
+  "\\\\.#"
+  "/#"
+  "/build/"
+  "/_build/"
+  # used before
+  "/CVS/"
+  "\\\\.o$"
+  "\\\\.lo$"
+  "\\\\.la$"
+  "Makefile\\\\.in$"
+  )
+
+  #SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Novell's package management core engine.")
+  SET(CPACK_PACKAGE_VENDOR "Novell Inc.")
+  #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt")
+  #SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt")
+  #SET(CPACK_PACKAGE_VERSION_MAJOR ${version_major})
+  #SET(CPACK_PACKAGE_VERSION_MINOR ${version_minor})
+  #SET(CPACK_PACKAGE_VERSION_PATCH ${version_patch})
+  SET( CPACK_GENERATOR "TBZ2")
+  SET( CPACK_SOURCE_GENERATOR "TBZ2")
+  SET( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" )
+  INCLUDE(CPack)
+
+  SPECFILE()
+
+  ADD_CUSTOM_TARGET( svncheck
+    COMMAND cd $(CMAKE_SOURCE_DIR) && LC_ALL=C git status | grep -q "nothing to commit .working directory clean."
+  )
+
+  SET( AUTOBUILD_COMMAND
+    COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/package/*.tar.bz2
+    COMMAND ${CMAKE_MAKE_PROGRAM} package_source
+    COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 ${CMAKE_BINARY_DIR}/package
+    COMMAND ${CMAKE_COMMAND} -E remove ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2
+    COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/package/${PACKAGE}.changes" "${CMAKE_BINARY_DIR}/package/${PACKAGE}.changes"
+  )
+
+  ADD_CUSTOM_TARGET( srcpackage_local
+    ${AUTOBUILD_COMMAND}
+  )
+
+  ADD_CUSTOM_TARGET( srcpackage
+    COMMAND ${CMAKE_MAKE_PROGRAM} svncheck
+    ${AUTOBUILD_COMMAND}
+  )
+ENDMACRO(GENERATE_PACKAGING)
diff --git a/devel/CMakeLists.txt b/devel/CMakeLists.txt
new file mode 100644 (file)
index 0000000..aecaaa8
--- /dev/null
@@ -0,0 +1,7 @@
+INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${LIBZYPP_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} )
+
+CONFIGURE_FILE(${LIBZYPP_SOURCE_DIR}/devel/genclass.in ${LIBZYPP_BINARY_DIR}/devel/genclass @ONLY)
+
+ADD_SUBDIRECTORY(devel.dmacvicar)
+ADD_SUBDIRECTORY(devel.ma)
+ADD_SUBDIRECTORY(devel.jkupec)
diff --git a/devel/devel.dmacvicar/CMakeLists.txt b/devel/devel.dmacvicar/CMakeLists.txt
new file mode 100644 (file)
index 0000000..88607e6
--- /dev/null
@@ -0,0 +1,18 @@
+ADD_DEFINITIONS(-DSRC_DIR=${CMAKE_CURRENT_SOURCE_DIR})
+
+ADD_EXECUTABLE(rpmbuilder rpmbuilder.cc)
+TARGET_LINK_LIBRARIES(rpmbuilder  zypp )
+
+ADD_EXECUTABLE(testbed testbed.cc)
+TARGET_LINK_LIBRARIES(testbed  zypp )
+
+ADD_EXECUTABLE(getfile getfile.cc)
+TARGET_LINK_LIBRARIES(getfile  zypp )
+
+FIND_PACKAGE(Zsync)
+IF(ZSYNC_FOUND)
+  ADD_EXECUTABLE(zsync zsync.cc)
+  TARGET_LINK_LIBRARIES(zsync  ${ZSYNC_LIBRARY} ${RCKSUM_LIBRARY})
+  TARGET_LINK_LIBRARIES(zsync  zypp )
+#  TARGET_LINK_LIBRARIES(zsync  zypp2 )
+ENDIF(ZSYNC_FOUND)
diff --git a/devel/devel.dmacvicar/CURLM_tp.cc b/devel/devel.dmacvicar/CURLM_tp.cc
new file mode 100644 (file)
index 0000000..5b1b32c
--- /dev/null
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" {
+#include <curl/curl.h>
+}
+#include <list>
+#include <sstream>
+#include "zypp/base/Exception.h"
+#include "zypp/base/Logger.h"
+#include "zypp/Pathname.h"
+#include "zypp/ExternalProgram.cc"
+//#include 
+
+using namespace zypp;
+using namespace std;
+
+size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
+{
+  MIL << "got data : " << size*nmemb << " bytes" << endl;
+  return size*nmemb;
+}
+
+int socket_callback(CURL *easy, curl_socket_t s, int what, void *userp,  void *socketp)
+{
+  MIL << "socket : " << s << " : " << what << endl;
+  return 0;
+}
+
+struct Range
+{
+  Range( off_t f, off_t t)
+    : from(t), to(t)
+  {}
+
+  off_t from;
+  off_t to;
+};
+
+int main()
+{
+   FILE *f = fopen("piece", "w" );
+  curl_global_init(CURL_GLOBAL_ALL);
+  CURLM *curlm;
+  curlm = curl_multi_init();
+  //curl_multi_setopt( curlm, CURLMOPT_PIPELINING, 1);
+  curl_multi_setopt( curlm, CURLMOPT_SOCKETFUNCTION, socket_callback);
+  
+//   0-1000
+//   1001-2000
+//   2001-3000
+//   
+//   3000-4000
+//   4001-5000
+
+  int i=1;
+  for ( ; i < 10; i++ ) {
+    CURL *curl;
+    curl = curl_easy_init();
+    CURLcode success;
+    // http://download.opensuse.org/distribution/10.2/repo/oss/suse/setup/descr/packages
+    if ( (success = curl_easy_setopt(curl, CURLOPT_URL, "http://ftp5.gwdg.de/pub/opensuse/distribution/10.2/repo/oss/suse/setup/descr/packages")) != CURLE_OK)
+      ZYPP_THROW(Exception("url"));
+
+     if ( (success = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data)) != CURLE_OK)
+       ZYPP_THROW(Exception("write data"));
+    //curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+    stringstream rs;
+    int k=1;
+    for(; k<2; k++) {
+      rs << ( k!=1 ? "," : "") << (i*k)*1000 << "-" << ((i*k)*1000 + 1000);
+    }
+    MIL << "range: " << rs.str() << endl;
+    if ( (success = curl_easy_setopt(curl, CURLOPT_RANGE, rs.str().c_str())) != CURLE_OK)
+      ZYPP_THROW(Exception("write data"));
+
+    CURLMcode code;
+    if ( (code = curl_multi_add_handle( curlm, curl)) != CURLM_OK)
+      ZYPP_THROW(Exception("write data"));
+    
+  }
+  int still_running = 0;
+  /* we start some action by calling perform right away */
+  while(CURLM_CALL_MULTI_PERFORM ==
+        curl_multi_perform(curlm, &still_running));
+
+  while(still_running) {
+    struct timeval timeout;
+    int rc; /* select() return code */
+
+    fd_set fdread;
+    fd_set fdwrite;
+    fd_set fdexcep;
+    int maxfd;
+
+    FD_ZERO(&fdread);
+    FD_ZERO(&fdwrite);
+    FD_ZERO(&fdexcep);
+
+    /* set a suitable timeout to play around with */
+    timeout.tv_sec = 1;
+    timeout.tv_usec = 0;
+
+    /* get file descriptors from the transfers */
+    curl_multi_fdset(curlm, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+    /* In a real-world program you OF COURSE check the return code of the
+       function calls, *and* you make sure that maxfd is bigger than -1 so
+       that the call to select() below makes sense! */
+
+    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+    switch(rc) {
+    case -1:
+      /* select error */
+      still_running = 0;
+      ERR << "select() returns error, this is badness" << endl;
+      break;
+    case 0:
+    default:
+      /* timeout or readable/writable sockets */
+      while(CURLM_CALL_MULTI_PERFORM ==
+            curl_multi_perform(curlm, &still_running));
+      break;
+    }
+  }
+
+  int c=999;
+  CURLMsg *m;
+  while ( m = curl_multi_info_read( curlm, &c) )
+  {
+    MIL<< m->msg << " : " << curl_easy_strerror(m->data.result) << endl;
+  }
+  Pathname root("/home/duncan/suse/metadata-diff");
+  
+
+ curl_multi_cleanup(curlm);
+ //curl_easy_cleanup(http_handle);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/devel/devel.dmacvicar/README b/devel/devel.dmacvicar/README
new file mode 100644 (file)
index 0000000..a6dc00d
--- /dev/null
@@ -0,0 +1,33 @@
+Running 'make' in this directory will create
+
+- cachedsource
+  Reads the database (from zypp.db) and creates all resolvables
+  in memory.
+  Used to time read and create
+
+- cachestore
+  Inserts capabilities (from external text file) into database.
+  Used to time insert.
+
+- mediaaccess
+  N/A
+
+- scansource
+  Downloads and parses a repository. To test repository integrity.
+
+- susetags-downloader
+  Used to download metadata from a 'susetags' source
+  See http://en.opensuse.org/Libzypp/Metadata/YaST
+
+  run as "susetags-downloader <uri> <cachedir>"
+
+- tagsparser
+  Parses 'susetags' metadata and writes it into database.
+
+- yum-downloader
+  Used to download metadata from a 'rpm-md' source
+  See http://en.opensuse.org/Standards/Rpm_Metadata
+
+  run as "yum-downloader <uri> <cachedir>"
+
+
diff --git a/devel/devel.dmacvicar/ScanSource.cc b/devel/devel.dmacvicar/ScanSource.cc
new file mode 100644 (file)
index 0000000..0ec1f14
--- /dev/null
@@ -0,0 +1,193 @@
+#include <iostream>
+
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Measure.h>
+#include <zypp/SourceFactory.h>
+#include <zypp/Source.h>
+#include <zypp/Product.h>
+#include <zypp/ResStore.h>
+#include <zypp/ResObject.h>
+#include <zypp/pool/PoolStats.h>
+#include <zypp/KeyRing.h>
+#include <zypp/Date.h>
+#include <zypp/SourceManager.h>
+
+using namespace std;
+using namespace zypp;
+
+static bool verbose = false;
+static bool debug_flag   = false;
+
+#define LOG (debug_flag ? USR : cout)
+
+struct KeyRingReceiver : public callback::ReceiveReport<KeyRingReport>
+{
+  KeyRingReceiver()
+  {
+    connect();
+  }
+
+  virtual bool askUserToAcceptUnsignedFile( const std::string & file )
+  {
+    LOG << "===[UnsignedFile " << file << "]" << endl;
+    return true;
+  }
+  virtual bool askUserToAcceptUnknownKey( const std::string &file,
+                                          const std::string &id )
+  {
+    LOG << "===[UnknownKey " << id << "]" << endl;
+    return true;
+  }
+  virtual bool askUserToTrustKey( const PublicKey &key)
+  {
+    LOG << "===[TrustKey" << key << "]" << endl;
+    return true;
+  }
+  virtual bool askUserToImportKey( const PublicKey &key)
+  {
+    LOG << "===[ImportKey " << key << "]" << endl;
+    return true;
+  }
+  virtual bool askUserToAcceptVerificationFailed( const std::string &file,
+                                                  const PublicKey &key )
+  {
+    LOG << "===[VerificationFailed " << file << " " << key << "]" << endl;
+    return true;
+  }
+};
+
+struct ResStoreStats : public pool::PoolStats
+{
+  void operator()( const ResObject::constPtr & obj )
+  {
+    if ( isKind<Product>( obj ) )
+      {
+        LOG << obj << endl;
+      }
+    pool::PoolStats::operator()( obj );
+  }
+};
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  //zypp::base::LogControl::instance().logfile( "" );
+  INT << "===[START]==========================================" << endl;
+  --argc;
+  ++argv;
+
+  if ( ! argc )
+    {
+      LOG << "Usage: ScanSource [options] url [[options] url...]" << endl;
+      LOG << "  Display summary of Sources found at 'url'. " << endl;
+      LOG << "  " << endl;
+      LOG << "  " << endl;
+      LOG << "  options:" << endl;
+      LOG << "  +/-l    enable/disable detailed listing of Source content" << endl;
+      LOG << "  +/-d    enable/disable debug output" << endl;
+      return 0;
+    }
+
+  KeyRingReceiver accept;
+
+  for ( ; argc; --argc, ++argv )
+    {
+      if ( *argv == string("+l") )
+        {
+          verbose = true;
+          continue;
+        }
+      if ( *argv == string("-l") )
+        {
+          verbose = false;
+          continue;
+        }
+      if ( *argv == string("+d") )
+        {
+          zypp::base::LogControl::instance().logfile( "-" );
+          debug_flag = true;
+          continue;
+        }
+      if ( *argv == string("-d") )
+        {
+          zypp::base::LogControl::instance().logfile( "" );
+          debug_flag = false;
+          continue;
+        }
+
+      LOG << "====================================================" << endl;
+      LOG << "===Search Source at Url(" << *argv << ")..." << endl;
+      Source_Ref src;
+      try
+        {
+          debug::Measure m( "Create" );
+          Url url(*argv);
+          try
+            {
+              src = SourceFactory().createFrom( url, "/", Date::now().asSeconds() );
+            }
+          catch ( const source::SourceUnknownTypeException & )
+            {
+              src = SourceFactory().createFrom( "Plaindir", url, "/", Date::now().asSeconds(), "", false, true );
+            }
+            m.elapsed();
+            //LOG << m.asString() << endl;
+        }
+      catch ( const Exception & except_r )
+        {
+          LOG << "***Failed: " << except_r << endl;
+          continue;
+        }
+      LOG << "type:           " << src.type() << endl;
+      LOG << "numberOfMedia:  " << src.numberOfMedia() << endl;
+      LOG << "alias:          " << src.alias() << endl;
+      LOG << "vendor:         " << src.vendor() << endl;
+      LOG << "unique_id:      " << src.unique_id() << endl;
+      LOG << "baseSource:     " << src.baseSource() << endl;
+      LOG << "autorefresh:    " << src.autorefresh() << endl;
+      LOG << "publicKeys:     " << src.publicKeys() << endl;
+
+      LOG << "===Parse content..." << endl;
+      try
+      {
+        debug::Measure m( "Parse" );
+        src.resolvables();
+        m.elapsed();
+        //LOG << m.asString() << endl;
+      }
+      catch ( const Exception & except_r )
+      {
+        LOG << "***Failed: " << except_r << endl;
+        continue;
+      }
+      LOG << for_each( src.resolvables().begin(), src.resolvables().end(),
+                       ResStoreStats() ) << endl;
+      if ( verbose )
+      {
+        dumpRange( LOG, src.resolvables().begin(), src.resolvables().end() ) << endl;
+      }
+#define TestKind Product
+
+      for (ResStore::const_iterator it = src.resolvables().begin(); it != src.resolvables().end(); ++it)
+      {
+        if ( isKind<TestKind>(*it) )
+        {
+          zypp::TestKind::constPtr res = asKind<TestKind>( *it );
+          cout << res->name() << " | " << res->edition() << std::endl;
+          cout << res->distributionName() << " | " << res->distributionEdition() << std::endl;
+        }
+      }
+      
+      //SourceManager::sourceManager()->addSource( src );
+      //SourceManager::sourceManager()->store( "/", true );
+    }
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
diff --git a/devel/devel.dmacvicar/YUMReader_tp.cc b/devel/devel.dmacvicar/YUMReader_tp.cc
new file mode 100644 (file)
index 0000000..38f8c84
--- /dev/null
@@ -0,0 +1,139 @@
+#include "zypp/ZYpp.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/CapFactory.h"
+#include "zypp/data/ResolvableDataConsumer.h"
+#include "zypp/base/Measure.h"
+#include "zypp/detail/ResObjectFactory.h"
+#include "zypp/parser/yum/RepoParser.h"
+#include "zypp/repo/memory/PackageImpl.h"
+
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "yumparsertest"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::parser::yum;
+using zypp::debug::Measure;
+using namespace zypp::repo::memory;
+
+bool progress_function(ProgressData::value_type p)
+{
+  cout << "Parsing YUM source [" << p << "%]" << endl;
+//  cout << "\rParsing YUM source [" << p << "%]" << flush;
+  return true;
+}
+
+class ResolvableConsumer : public data::ResolvableDataConsumer
+{
+  public:
+    
+  typedef detail::ResImplTraits<PackageImpl>::Ptr PkgImplPtr;
+  typedef detail::ResImplTraits<PackageImpl>::Ptr SrcPkgImplPtr;
+  
+  ResolvableConsumer()
+  {
+  
+  }
+  
+  void collectDeps( Dependencies &deps, const data::Dependencies &data_deps)
+  {
+    CapFactory factory;
+    for ( data::Dependencies::const_iterator i = data_deps.begin(); i != data_deps.end(); ++i )
+    {
+      data::DependencyList list(i->second);
+      zypp::Dep deptype(i->first);
+      for ( data::DependencyList::const_iterator it = list.begin(); it != list.end(); ++it )
+      {
+        deps[deptype].insert(factory.fromImpl(*it));
+      }
+    }
+  }
+  
+  virtual ~ResolvableConsumer()
+  {
+  
+  }
+
+  virtual void consumePackage( const data::RecordId &repository_id, data::Package_Ptr ptr )
+  {
+    PkgImplPtr impl = PkgImplPtr( new PackageImpl(ptr) );
+    Dependencies deps;
+    collectDeps( deps, ptr->deps );
+    
+    Package::Ptr pkg = detail::makeResolvableFromImpl( NVRAD( ptr->name, ptr->edition, ptr->arch, deps), impl );
+    _store.insert(pkg);
+  }
+  virtual void consumeProduct( const data::RecordId &repository_id, data::Product_Ptr )
+  {
+  }
+  virtual void consumePatch( const data::RecordId &repository_id, data::Patch_Ptr )
+  {
+  }
+  virtual void consumeMessage( const data::RecordId &repository_id, data::Message_Ptr )
+  {
+  
+  }
+  
+  virtual void consumeScript( const data::RecordId &repository_id, data::Script_Ptr )
+  {
+  
+  }
+
+  virtual void consumeChangelog( const data::RecordId & repository_id, const data::Resolvable_Ptr &, const Changelog & )
+  {
+  
+  }
+  
+  virtual void consumeFilelist( const data::RecordId & repository_id, const data::Resolvable_Ptr &, const data::Filenames & )
+  {}
+
+  
+  virtual void consumeSourcePackage(const zypp::data::RecordId&, zypp::data::SrcPackage_Ptr)
+  {}
+  
+  virtual void consumePackageAtom(const zypp::data::RecordId&, const zypp::data::PackageAtom_Ptr&)
+  {}
+  
+  virtual void consumePattern(const zypp::data::RecordId&, zypp::data::Pattern_Ptr)
+  {}
+
+    //virtual void consumeSourcePackage( const data::SrcPackage_Ptr ) = 0;
+  ResStore _store;
+};
+
+int main(int argc, char **argv)
+{
+  base::LogControl::instance().logfile("yumparsertest.log");
+  
+  if (argc < 2)
+  {
+    cout << "usage: yumparsertest path/to/yumsourcedir" << endl << endl;
+    return 1;
+  }
+
+  try
+  {
+    ZYpp::Ptr z = getZYpp();
+
+    MIL << "creating PrimaryFileParser" << endl;
+    Measure parse_primary_timer("primary.xml.gz parsing");
+    ResolvableConsumer store;
+    parser::yum::RepoParser parser( 0, store, &progress_function);
+    parser.parse(argv[1]);
+    parse_primary_timer.stop();
+
+    cout << endl;
+  }
+  catch ( const Exception &e )
+  {
+    cout << "Oops! " << e.msg() << std::endl;
+  }
+
+  return 0;
+}
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/devel/devel.dmacvicar/getfile.cc b/devel/devel.dmacvicar/getfile.cc
new file mode 100644 (file)
index 0000000..364abd3
--- /dev/null
@@ -0,0 +1,115 @@
+#include <sys/time.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <zypp/base/Logger.h>
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+
+#include "zypp/Product.h"
+#include "zypp/Package.h"
+#include "zypp/Fetcher.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ProgressData.h"
+
+#include "zypp/sat/Pool.h"
+
+#include "zypp/ZYppCallbacks.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+using zypp::media::MediaChangeReport;
+using zypp::media::DownloadProgressReport;
+
+
+bool result_cb( const ResObject::Ptr &r )
+{
+  cout << r << endl;
+}
+
+struct MediaChangeReportReceiver : public zypp::callback::ReceiveReport<MediaChangeReport>
+  {
+    virtual MediaChangeReport::Action
+    requestMedia(zypp::Url & url,
+                 unsigned                         mediumNr,
+                 const std::string &              label,
+                 MediaChangeReport::Error         error,
+                 const std::string &              description,
+                 const std::vector<std::string> & devices,
+                 unsigned int &                   index)
+    {
+      cout << label << " " <<description << std::endl;
+      MIL << "media problem, url: " << url.asString() << std::endl;
+      return MediaChangeReport::IGNORE;
+    }
+  };
+
+struct DownloadProgressReportReceiver : public zypp::callback::ReceiveReport<DownloadProgressReport>
+{
+
+    virtual void start( const Url &/*file*/, Pathname /*localfile*/ )
+    {
+    }
+    
+    virtual bool progress(int value, const Url &file,
+                          double dbps_avg,
+                          double dbps_current)
+    { 
+        cout << file << " " << value << "% speed:" << dbps_current << " avg:" << dbps_avg << endl;
+        return true; 
+    }
+    
+    virtual Action problem( const Url &/*file*/
+                            , Error /*error*/
+                            , const std::string &description )
+    {
+        cout << "PROBLEM: " << description << endl;
+        return ABORT; 
+    }
+    
+    virtual void finish(
+        const Url &/*file*/
+        , Error /*error*/
+        , const std::string &reason
+        )
+        {
+            cout << "finish:" << endl;            
+            cout << reason << endl;
+        }
+};
+
+int main(int argc, char **argv)
+{
+    try
+    {
+      ZYpp::Ptr z = getZYpp();
+    
+      MediaChangeReportReceiver change_report;
+      DownloadProgressReportReceiver progress_report;
+      change_report.connect();
+      progress_report.connect();
+      
+      MediaSetAccess access(Url("http://download.opensuse.org/update/11.1/rpm/x86_64"));
+      OnMediaLocation loc;
+      loc.setLocation("java-1_5_0-sun-1.5.0_update17-1.1.x86_64.rpm");
+      //loc.setOptional(true);
+
+      Fetcher fetcher;
+      fetcher.enqueue(loc);
+      fetcher.start("./", access);
+      
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      cout << e.msg() << endl;
+      cout << e.historyAsString();
+    }
+    
+    return 0;
+}
+
+
+
diff --git a/devel/devel.dmacvicar/multiple-download.cc b/devel/devel.dmacvicar/multiple-download.cc
new file mode 100644 (file)
index 0000000..dd13dc6
--- /dev/null
@@ -0,0 +1,89 @@
+#include <sys/time.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <zypp/base/Logger.h>
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+
+#include "zypp/Product.h"
+#include "zypp/Package.h"
+
+#include "zypp/TmpPath.h"
+
+#include "zypp/sat/Pool.h"
+
+#include "zypp/PoolQuery.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+
+bool result_cb( const ResObject::Ptr &r )
+{
+  cout << r << endl;
+}
+
+boost::mutex io_mutex;
+
+struct Counter
+{
+  Counter(int id) : id(id) { }
+  void operator()()
+  {
+    for (int i = 0; i < 10; ++i)
+    {
+      //boost::mutex::scoped_lock lock(io_mutex);
+      std::cout << id << ": " << i << std::endl;
+      if ( i == 4 )
+      {
+        boost::thread thrd2(Counter(3));
+      }
+    }
+  }
+  int id;
+};
+
+int main(int argc, char **argv)
+{
+    try
+    {
+      ZYpp::Ptr z = getZYpp();
+    
+      boost::thread thrd1(Counter(1));
+      boost::thread thrd2(Counter(2));
+      thrd1.join();
+      thrd2.join();
+      return 0;
+      
+      //z->initializeTarget("/");
+      //z->target()->load();
+
+//      sat::Pool::instance().addRepoSolv("./foo.solv");
+
+//       for ( ResPool::const_iterator it = z->pool().begin(); it != z->pool().end(); ++it )
+//       {
+//         ResObject::constPtr res = it->resolvable();
+//         if ( res->name() == "kde4-kcolorchooser")
+//         {
+//           cout << res << endl;
+//           cout << res->summary() << " | " << res->size() << endl;
+//         }
+//       }
+
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      cout << e.msg() << endl;
+    }
+    
+    return 0;
+}
+
+
+
diff --git a/devel/devel.dmacvicar/repodata/filelists.xml b/devel/devel.dmacvicar/repodata/filelists.xml
new file mode 100644 (file)
index 0000000..ec8b307
--- /dev/null
@@ -0,0 +1,2903 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="47">
+<package pkgid="d401b0253012e812a395ddb043f5b0ab02eff59e" name="opera" arch="i586">
+<version epoch="0" ver="8.54" rel="0.1"/>
+        <file type="dir">/usr/lib/opera</file>
+        <file type="dir">/usr/lib/opera/8.54-20060330.6</file>
+        <file type="dir">/usr/lib/opera/plugins</file>
+        <file type="dir">/usr/share/doc/packages/opera</file>
+        <file type="dir">/usr/share/icons/opera.xpm</file>
+        <file type="dir">/usr/share/opera</file>
+        <file type="dir">/usr/share/opera/images</file>
+        <file type="dir">/usr/share/opera/ini</file>
+        <file type="dir">/usr/share/opera/java</file>
+        <file type="dir">/usr/share/opera/locale</file>
+        <file type="dir">/usr/share/opera/locale/en</file>
+        <file type="dir">/usr/share/opera/skin</file>
+        <file type="dir">/usr/share/opera/styles</file>
+        <file type="dir">/usr/share/opera/styles/user</file>
+        <file type="dir">/usr/share/pixmaps/opera.xpm</file>
+        <file>/etc/opera6rc</file>
+        <file>/etc/opera6rc.fixed</file>
+        <file>/etc/profile.d/opera.sh</file>
+        <file>/usr/bin/opera</file>
+        <file>/usr/lib/opera/8.54-20060330.6/missingsyms.so</file>
+        <file>/usr/lib/opera/8.54-20060330.6/opera</file>
+        <file>/usr/lib/opera/8.54-20060330.6/spellcheck.so</file>
+        <file>/usr/lib/opera/8.54-20060330.6/works</file>
+        <file>/usr/lib/opera/plugins/libnpp.so</file>
+        <file>/usr/lib/opera/plugins/operamotifwrapper-3</file>
+        <file>/usr/lib/opera/plugins/operaplugincleaner</file>
+        <file>/usr/share/applications/opera.desktop</file>
+        <file>/usr/share/doc/packages/opera/LICENSE</file>
+        <file>/usr/share/icons/opera.xpm/opera.xpm</file>
+        <file>/usr/share/man/man1/opera.1.gz</file>
+        <file>/usr/share/opera/chartables.bin</file>
+        <file>/usr/share/opera/html40_entities.dtd</file>
+        <file>/usr/share/opera/images/blank.gif</file>
+        <file>/usr/share/opera/images/drive.gif</file>
+        <file>/usr/share/opera/images/file.gif</file>
+        <file>/usr/share/opera/images/folder.gif</file>
+        <file>/usr/share/opera/images/link.gif</file>
+        <file>/usr/share/opera/images/opera.xpm</file>
+        <file>/usr/share/opera/images/opera_16x16.png</file>
+        <file>/usr/share/opera/images/opera_22x22.png</file>
+        <file>/usr/share/opera/images/opera_32x32.png</file>
+        <file>/usr/share/opera/images/opera_48x48.png</file>
+        <file>/usr/share/opera/images/operabanner.png</file>
+        <file>/usr/share/opera/ini/dialog.ini</file>
+        <file>/usr/share/opera/ini/fastforward.ini</file>
+        <file>/usr/share/opera/ini/filehandler.ini</file>
+        <file>/usr/share/opera/ini/pluginpath.ini</file>
+        <file>/usr/share/opera/ini/spellcheck.ini</file>
+        <file>/usr/share/opera/ini/standard_keyboard.ini</file>
+        <file>/usr/share/opera/ini/standard_menu.ini</file>
+        <file>/usr/share/opera/ini/standard_mouse.ini</file>
+        <file>/usr/share/opera/ini/standard_toolbar.ini</file>
+        <file>/usr/share/opera/ini/unix_keyboard.ini</file>
+        <file>/usr/share/opera/java/opera.jar</file>
+        <file>/usr/share/opera/java/opera.policy</file>
+        <file>/usr/share/opera/jsconsole.html</file>
+        <file>/usr/share/opera/lngcode.txt</file>
+        <file>/usr/share/opera/locale/british_english.lng</file>
+        <file>/usr/share/opera/locale/bulgarian.lng</file>
+        <file>/usr/share/opera/locale/catala.lng</file>
+        <file>/usr/share/opera/locale/cesky.lng</file>
+        <file>/usr/share/opera/locale/dansk.lng</file>
+        <file>/usr/share/opera/locale/deutsch.lng</file>
+        <file>/usr/share/opera/locale/en/default.adr</file>
+        <file>/usr/share/opera/locale/en/license.txt</file>
+        <file>/usr/share/opera/locale/en/lngcode.txt</file>
+        <file>/usr/share/opera/locale/en/search.ini</file>
+        <file>/usr/share/opera/locale/english.lng</file>
+        <file>/usr/share/opera/locale/espanol.lng</file>
+        <file>/usr/share/opera/locale/espanol_castellano.lng</file>
+        <file>/usr/share/opera/locale/finnish.lng</file>
+        <file>/usr/share/opera/locale/francais.lng</file>
+        <file>/usr/share/opera/locale/italiano.lng</file>
+        <file>/usr/share/opera/locale/japanese.lng</file>
+        <file>/usr/share/opera/locale/nederlands.lng</file>
+        <file>/usr/share/opera/locale/norsk_bokmal.lng</file>
+        <file>/usr/share/opera/locale/norsk_nynorsk.lng</file>
+        <file>/usr/share/opera/locale/polski.lng</file>
+        <file>/usr/share/opera/locale/portugues_do_brasil.lng</file>
+        <file>/usr/share/opera/locale/russian.lng</file>
+        <file>/usr/share/opera/locale/svenska.lng</file>
+        <file>/usr/share/opera/opera.reg</file>
+        <file>/usr/share/opera/opera6.adr</file>
+        <file>/usr/share/opera/search.ini</file>
+        <file>/usr/share/opera/skin/standard_skin.zip</file>
+        <file>/usr/share/opera/skin/windows_skin.zip</file>
+        <file>/usr/share/opera/styles/OPF.css</file>
+        <file>/usr/share/opera/styles/about.css</file>
+        <file>/usr/share/opera/styles/cache.css</file>
+        <file>/usr/share/opera/styles/certinfo.css</file>
+        <file>/usr/share/opera/styles/csr.css</file>
+        <file>/usr/share/opera/styles/dir.css</file>
+        <file>/usr/share/opera/styles/drives.css</file>
+        <file>/usr/share/opera/styles/email.css</file>
+        <file>/usr/share/opera/styles/history.css</file>
+        <file>/usr/share/opera/styles/im.css</file>
+        <file>/usr/share/opera/styles/info.css</file>
+        <file>/usr/share/opera/styles/mime.css</file>
+        <file>/usr/share/opera/styles/mimehead.css</file>
+        <file>/usr/share/opera/styles/opf.css</file>
+        <file>/usr/share/opera/styles/plugins.css</file>
+        <file>/usr/share/opera/styles/user/accessibility.css</file>
+        <file>/usr/share/opera/styles/user/contrastbw.css</file>
+        <file>/usr/share/opera/styles/user/contrastwb.css</file>
+        <file>/usr/share/opera/styles/user/debugwithoutline.css</file>
+        <file>/usr/share/opera/styles/user/disabletables.css</file>
+        <file>/usr/share/opera/styles/user/hidecertainsizes.css</file>
+        <file>/usr/share/opera/styles/user/hidenonlinkimages.css</file>
+        <file>/usr/share/opera/styles/user/imageandlinkonly.css</file>
+        <file>/usr/share/opera/styles/user/nostalgia.css</file>
+        <file>/usr/share/opera/styles/user/showstructure.css</file>
+        <file>/usr/share/opera/styles/user/textonly.css</file>
+        <file>/usr/share/opera/styles/user/userstyle.ini</file>
+        <file>/usr/share/opera/styles/wml.css</file>
+        <file>/usr/share/opera/svg-mo.dat</file>
+        <file>/usr/share/opera/svg-mobd.dat</file>
+        <file>/usr/share/opera/svg-sa.dat</file>
+        <file>/usr/share/opera/svg-sabd.dat</file>
+        <file>/usr/share/opera/svg-se.dat</file>
+        <file>/usr/share/opera/svg-sebd.dat</file>
+        <file>/usr/share/pixmaps/opera.png</file>
+        <file>/usr/share/pixmaps/opera.xpm/opera.xpm</file>
+</package>
+
+
+
+
+
+
+
+
+
+
+
+<package pkgid="2b8a73c02d8d6b57096be23eed26d5733d67ae80" name="opera" arch="src">
+<version epoch="0" ver="8.54" rel="0.1"/>
+        <file>british_english.lng.bz2</file>
+        <file>bulgarian.lng.bz2</file>
+        <file>catala.lng.bz2</file>
+        <file>cesky.lng.bz2</file>
+        <file>dansk.lng.bz2</file>
+        <file>deutsch.lng.bz2</file>
+        <file>espanol.lng.bz2</file>
+        <file>espanol_castellano.lng.bz2</file>
+        <file>filehandler.ini</file>
+        <file>finnish.lng.bz2</file>
+        <file>francais.lng.bz2</file>
+        <file>italiano.lng.bz2</file>
+        <file>japanese.lng.bz2</file>
+        <file>nederlands.lng.bz2</file>
+        <file>norsk_bokmal.lng.bz2</file>
+        <file>norsk_nynorsk.lng.bz2</file>
+        <file>opera-8.54-20060330.1-static-qt.sparc-en.tar.bz2</file>
+        <file>opera-8.54-20060330.3-shared-qt.ppc-en.tar.bz2</file>
+        <file>opera-8.54-20060330.6-shared-qt.i386-en.tar.bz2</file>
+        <file>opera.reg</file>
+        <file>opera.spec</file>
+        <file>polski.lng.bz2</file>
+        <file>portugues_do_brasil.lng.bz2</file>
+        <file>russian.lng.bz2</file>
+        <file>search.ini.gz</file>
+        <file>svenska.lng.bz2</file>
+</package>
+
+
+
+
+
+
+
+
+
+
+
+<package pkgid="c67d4bea93ba82482aaff57cae548fb0da1e8f9d" name="opera" arch="x86_64">
+<version epoch="0" ver="8.54" rel="0.1"/>
+        <file type="dir">/usr/lib/opera</file>
+        <file type="dir">/usr/lib/opera/8.54-20060330.6</file>
+        <file type="dir">/usr/lib/opera/plugins</file>
+        <file type="dir">/usr/share/doc/packages/opera</file>
+        <file type="dir">/usr/share/icons/opera.xpm</file>
+        <file type="dir">/usr/share/opera</file>
+        <file type="dir">/usr/share/opera/images</file>
+        <file type="dir">/usr/share/opera/ini</file>
+        <file type="dir">/usr/share/opera/java</file>
+        <file type="dir">/usr/share/opera/locale</file>
+        <file type="dir">/usr/share/opera/locale/en</file>
+        <file type="dir">/usr/share/opera/skin</file>
+        <file type="dir">/usr/share/opera/styles</file>
+        <file type="dir">/usr/share/opera/styles/user</file>
+        <file type="dir">/usr/share/pixmaps/opera.xpm</file>
+        <file>/etc/opera6rc</file>
+        <file>/etc/opera6rc.fixed</file>
+        <file>/etc/profile.d/opera.sh</file>
+        <file>/usr/bin/opera</file>
+        <file>/usr/lib/opera/8.54-20060330.6/missingsyms.so</file>
+        <file>/usr/lib/opera/8.54-20060330.6/opera</file>
+        <file>/usr/lib/opera/8.54-20060330.6/spellcheck.so</file>
+        <file>/usr/lib/opera/8.54-20060330.6/works</file>
+        <file>/usr/lib/opera/plugins/libnpp.so</file>
+        <file>/usr/lib/opera/plugins/operamotifwrapper-3</file>
+        <file>/usr/lib/opera/plugins/operaplugincleaner</file>
+        <file>/usr/share/applications/opera.desktop</file>
+        <file>/usr/share/doc/packages/opera/LICENSE</file>
+        <file>/usr/share/icons/opera.xpm/opera.xpm</file>
+        <file>/usr/share/man/man1/opera.1.gz</file>
+        <file>/usr/share/opera/chartables.bin</file>
+        <file>/usr/share/opera/html40_entities.dtd</file>
+        <file>/usr/share/opera/images/blank.gif</file>
+        <file>/usr/share/opera/images/drive.gif</file>
+        <file>/usr/share/opera/images/file.gif</file>
+        <file>/usr/share/opera/images/folder.gif</file>
+        <file>/usr/share/opera/images/link.gif</file>
+        <file>/usr/share/opera/images/opera.xpm</file>
+        <file>/usr/share/opera/images/opera_16x16.png</file>
+        <file>/usr/share/opera/images/opera_22x22.png</file>
+        <file>/usr/share/opera/images/opera_32x32.png</file>
+        <file>/usr/share/opera/images/opera_48x48.png</file>
+        <file>/usr/share/opera/images/operabanner.png</file>
+        <file>/usr/share/opera/ini/dialog.ini</file>
+        <file>/usr/share/opera/ini/fastforward.ini</file>
+        <file>/usr/share/opera/ini/filehandler.ini</file>
+        <file>/usr/share/opera/ini/pluginpath.ini</file>
+        <file>/usr/share/opera/ini/spellcheck.ini</file>
+        <file>/usr/share/opera/ini/standard_keyboard.ini</file>
+        <file>/usr/share/opera/ini/standard_menu.ini</file>
+        <file>/usr/share/opera/ini/standard_mouse.ini</file>
+        <file>/usr/share/opera/ini/standard_toolbar.ini</file>
+        <file>/usr/share/opera/ini/unix_keyboard.ini</file>
+        <file>/usr/share/opera/java/opera.jar</file>
+        <file>/usr/share/opera/java/opera.policy</file>
+        <file>/usr/share/opera/jsconsole.html</file>
+        <file>/usr/share/opera/lngcode.txt</file>
+        <file>/usr/share/opera/locale/british_english.lng</file>
+        <file>/usr/share/opera/locale/bulgarian.lng</file>
+        <file>/usr/share/opera/locale/catala.lng</file>
+        <file>/usr/share/opera/locale/cesky.lng</file>
+        <file>/usr/share/opera/locale/dansk.lng</file>
+        <file>/usr/share/opera/locale/deutsch.lng</file>
+        <file>/usr/share/opera/locale/en/default.adr</file>
+        <file>/usr/share/opera/locale/en/license.txt</file>
+        <file>/usr/share/opera/locale/en/lngcode.txt</file>
+        <file>/usr/share/opera/locale/en/search.ini</file>
+        <file>/usr/share/opera/locale/english.lng</file>
+        <file>/usr/share/opera/locale/espanol.lng</file>
+        <file>/usr/share/opera/locale/espanol_castellano.lng</file>
+        <file>/usr/share/opera/locale/finnish.lng</file>
+        <file>/usr/share/opera/locale/francais.lng</file>
+        <file>/usr/share/opera/locale/italiano.lng</file>
+        <file>/usr/share/opera/locale/japanese.lng</file>
+        <file>/usr/share/opera/locale/nederlands.lng</file>
+        <file>/usr/share/opera/locale/norsk_bokmal.lng</file>
+        <file>/usr/share/opera/locale/norsk_nynorsk.lng</file>
+        <file>/usr/share/opera/locale/polski.lng</file>
+        <file>/usr/share/opera/locale/portugues_do_brasil.lng</file>
+        <file>/usr/share/opera/locale/russian.lng</file>
+        <file>/usr/share/opera/locale/svenska.lng</file>
+        <file>/usr/share/opera/opera.reg</file>
+        <file>/usr/share/opera/opera6.adr</file>
+        <file>/usr/share/opera/search.ini</file>
+        <file>/usr/share/opera/skin/standard_skin.zip</file>
+        <file>/usr/share/opera/skin/windows_skin.zip</file>
+        <file>/usr/share/opera/styles/OPF.css</file>
+        <file>/usr/share/opera/styles/about.css</file>
+        <file>/usr/share/opera/styles/cache.css</file>
+        <file>/usr/share/opera/styles/certinfo.css</file>
+        <file>/usr/share/opera/styles/csr.css</file>
+        <file>/usr/share/opera/styles/dir.css</file>
+        <file>/usr/share/opera/styles/drives.css</file>
+        <file>/usr/share/opera/styles/email.css</file>
+        <file>/usr/share/opera/styles/history.css</file>
+        <file>/usr/share/opera/styles/im.css</file>
+        <file>/usr/share/opera/styles/info.css</file>
+        <file>/usr/share/opera/styles/mime.css</file>
+        <file>/usr/share/opera/styles/mimehead.css</file>
+        <file>/usr/share/opera/styles/opf.css</file>
+        <file>/usr/share/opera/styles/plugins.css</file>
+        <file>/usr/share/opera/styles/user/accessibility.css</file>
+        <file>/usr/share/opera/styles/user/contrastbw.css</file>
+        <file>/usr/share/opera/styles/user/contrastwb.css</file>
+        <file>/usr/share/opera/styles/user/debugwithoutline.css</file>
+        <file>/usr/share/opera/styles/user/disabletables.css</file>
+        <file>/usr/share/opera/styles/user/hidecertainsizes.css</file>
+        <file>/usr/share/opera/styles/user/hidenonlinkimages.css</file>
+        <file>/usr/share/opera/styles/user/imageandlinkonly.css</file>
+        <file>/usr/share/opera/styles/user/nostalgia.css</file>
+        <file>/usr/share/opera/styles/user/showstructure.css</file>
+        <file>/usr/share/opera/styles/user/textonly.css</file>
+        <file>/usr/share/opera/styles/user/userstyle.ini</file>
+        <file>/usr/share/opera/styles/wml.css</file>
+        <file>/usr/share/opera/svg-mo.dat</file>
+        <file>/usr/share/opera/svg-mobd.dat</file>
+        <file>/usr/share/opera/svg-sa.dat</file>
+        <file>/usr/share/opera/svg-sabd.dat</file>
+        <file>/usr/share/opera/svg-se.dat</file>
+        <file>/usr/share/opera/svg-sebd.dat</file>
+        <file>/usr/share/pixmaps/opera.png</file>
+        <file>/usr/share/pixmaps/opera.xpm/opera.xpm</file>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="47aa34762bfe1b236dc8f97ddbc6c71e1ae1184b" name="pdns" arch="i586">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+        <file type="dir">/usr/lib/pdns</file>
+        <file type="dir">/usr/share/doc/packages/pdns</file>
+        <file type="dir">/usr/share/doc/packages/pdns/html</file>
+        <file type="dir">/usr/share/doc/packages/pdns/html/stylesheet-images</file>
+        <file>/etc/init.d/pdns</file>
+        <file>/etc/pdns.conf</file>
+        <file>/usr/bin/pdns_control</file>
+        <file>/usr/bin/zone2ldap</file>
+        <file>/usr/bin/zone2sql</file>
+        <file>/usr/lib/pdns/libgeobackend.la</file>
+        <file>/usr/lib/pdns/libgeobackend.so</file>
+        <file>/usr/lib/pdns/libgeobackend.so.0</file>
+        <file>/usr/lib/pdns/libgeobackend.so.0.0.0</file>
+        <file>/usr/lib/pdns/libgmysqlbackend.la</file>
+        <file>/usr/lib/pdns/libgmysqlbackend.so</file>
+        <file>/usr/lib/pdns/libgmysqlbackend.so.0</file>
+        <file>/usr/lib/pdns/libgmysqlbackend.so.0.0.0</file>
+        <file>/usr/lib/pdns/libldapbackend.la</file>
+        <file>/usr/lib/pdns/libldapbackend.so</file>
+        <file>/usr/lib/pdns/libldapbackend.so.0</file>
+        <file>/usr/lib/pdns/libldapbackend.so.0.0.0</file>
+        <file>/usr/lib/pdns/libpdnsbackend.la</file>
+        <file>/usr/lib/pdns/libpdnsbackend.so</file>
+        <file>/usr/lib/pdns/libpdnsbackend.so.0</file>
+        <file>/usr/lib/pdns/libpdnsbackend.so.0.0.0</file>
+        <file>/usr/lib/pdns/libpipebackend.la</file>
+        <file>/usr/lib/pdns/libpipebackend.so</file>
+        <file>/usr/lib/pdns/libpipebackend.so.0</file>
+        <file>/usr/lib/pdns/libpipebackend.so.0.0.0</file>
+        <file>/usr/sbin/pdns_recursor</file>
+        <file>/usr/sbin/pdns_server</file>
+        <file>/usr/sbin/rcpdns</file>
+        <file>/usr/share/doc/packages/pdns/ChangeLog</file>
+        <file>/usr/share/doc/packages/pdns/HACKING</file>
+        <file>/usr/share/doc/packages/pdns/INSTALL</file>
+        <file>/usr/share/doc/packages/pdns/README</file>
+        <file>/usr/share/doc/packages/pdns/TODO</file>
+        <file>/usr/share/doc/packages/pdns/html/HTML.manifest</file>
+        <file>/usr/share/doc/packages/pdns/html/about.html</file>
+        <file>/usr/share/doc/packages/pdns/html/all-settings.html</file>
+        <file>/usr/share/doc/packages/pdns/html/analysis.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-configuration-details.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-error-reporting.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-writers-guide.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backends-detail.html</file>
+        <file>/usr/share/doc/packages/pdns/html/bindbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/book1.html</file>
+        <file>/usr/share/doc/packages/pdns/html/built-in-recursor.html</file>
+        <file>/usr/share/doc/packages/pdns/html/changelog.html</file>
+        <file>/usr/share/doc/packages/pdns/html/compiling-powerdns.html</file>
+        <file>/usr/share/doc/packages/pdns/html/configuring-db-connection.html</file>
+        <file>/usr/share/doc/packages/pdns/html/considerations.html</file>
+        <file>/usr/share/doc/packages/pdns/html/db2.html</file>
+        <file>/usr/share/doc/packages/pdns/html/dns-to-query.html</file>
+        <file>/usr/share/doc/packages/pdns/html/docbook.css</file>
+        <file>/usr/share/doc/packages/pdns/html/fancy-records.html</file>
+        <file>/usr/share/doc/packages/pdns/html/faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/generic-mypgsql-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/gsqlite.html</file>
+        <file>/usr/share/doc/packages/pdns/html/guardian.html</file>
+        <file>/usr/share/doc/packages/pdns/html/index.html</file>
+        <file>/usr/share/doc/packages/pdns/html/init-d-commands.html</file>
+        <file>/usr/share/doc/packages/pdns/html/installing-on-unix.html</file>
+        <file>/usr/share/doc/packages/pdns/html/ldap.html</file>
+        <file>/usr/share/doc/packages/pdns/html/license.html</file>
+        <file>/usr/share/doc/packages/pdns/html/master-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/master.html</file>
+        <file>/usr/share/doc/packages/pdns/html/metrics.html</file>
+        <file>/usr/share/doc/packages/pdns/html/migration.html</file>
+        <file>/usr/share/doc/packages/pdns/html/modules.html</file>
+        <file>/usr/share/doc/packages/pdns/html/monitoring.html</file>
+        <file>/usr/share/doc/packages/pdns/html/mysqlbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/nptl.html</file>
+        <file>/usr/share/doc/packages/pdns/html/odbc.html</file>
+        <file>/usr/share/doc/packages/pdns/html/on-windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/oracle.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-devel-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-internals.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-on-unix.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-users-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdnsbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/performance-settings.html</file>
+        <file>/usr/share/doc/packages/pdns/html/performance.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pipebackend-dynamic-resolution.html</file>
+        <file>/usr/share/doc/packages/pdns/html/powerdns-company-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/powerdns.html</file>
+        <file>/usr/share/doc/packages/pdns/html/querycache.html</file>
+        <file>/usr/share/doc/packages/pdns/html/randombackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/recursion.html</file>
+        <file>/usr/share/doc/packages/pdns/html/replication.html</file>
+        <file>/usr/share/doc/packages/pdns/html/running-on-windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/rw-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/security-policy.html</file>
+        <file>/usr/share/doc/packages/pdns/html/security.html</file>
+        <file>/usr/share/doc/packages/pdns/html/slave.html</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/caution.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/home.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/important.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/next.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/note.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/prev.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/tip.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-blank.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-minus.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-plus.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/up.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/warning.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/syslog.html</file>
+        <file>/usr/share/doc/packages/pdns/html/testing.html</file>
+        <file>/usr/share/doc/packages/pdns/html/thanks-to.html</file>
+        <file>/usr/share/doc/packages/pdns/html/types.html</file>
+        <file>/usr/share/doc/packages/pdns/html/virtual.html</file>
+        <file>/usr/share/doc/packages/pdns/html/windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/x2460.html</file>
+        <file>/usr/share/doc/packages/pdns/html/x2472.html</file>
+        <file>/usr/share/doc/packages/pdns/html/xdbbackend.html</file>
+        <file>/usr/share/man/man8/pdns_control.8.gz</file>
+        <file>/usr/share/man/man8/pdns_server.8.gz</file>
+        <file>/usr/share/man/man8/zone2sql.8.gz</file>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="57c94864f94bbed8681d3498958189a426de15a9" name="pdns" arch="src">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+        <file>pdns-2.9.19-CVE-2006-2069.patch</file>
+        <file>pdns-2.9.19-fix.patch</file>
+        <file>pdns-2.9.19.tar.bz2</file>
+        <file>pdns.spec</file>
+        <file>rcpdns</file>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="cc316a1be4793728b58aa0d4f8d74cc334bf1cd9" name="pdns" arch="x86_64">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+        <file type="dir">/usr/lib64/pdns</file>
+        <file type="dir">/usr/share/doc/packages/pdns</file>
+        <file type="dir">/usr/share/doc/packages/pdns/html</file>
+        <file type="dir">/usr/share/doc/packages/pdns/html/stylesheet-images</file>
+        <file>/etc/init.d/pdns</file>
+        <file>/etc/pdns.conf</file>
+        <file>/usr/bin/pdns_control</file>
+        <file>/usr/bin/zone2ldap</file>
+        <file>/usr/bin/zone2sql</file>
+        <file>/usr/lib64/pdns/libgeobackend.la</file>
+        <file>/usr/lib64/pdns/libgeobackend.so</file>
+        <file>/usr/lib64/pdns/libgeobackend.so.0</file>
+        <file>/usr/lib64/pdns/libgeobackend.so.0.0.0</file>
+        <file>/usr/lib64/pdns/libgmysqlbackend.la</file>
+        <file>/usr/lib64/pdns/libgmysqlbackend.so</file>
+        <file>/usr/lib64/pdns/libgmysqlbackend.so.0</file>
+        <file>/usr/lib64/pdns/libgmysqlbackend.so.0.0.0</file>
+        <file>/usr/lib64/pdns/libldapbackend.la</file>
+        <file>/usr/lib64/pdns/libldapbackend.so</file>
+        <file>/usr/lib64/pdns/libldapbackend.so.0</file>
+        <file>/usr/lib64/pdns/libldapbackend.so.0.0.0</file>
+        <file>/usr/lib64/pdns/libpdnsbackend.la</file>
+        <file>/usr/lib64/pdns/libpdnsbackend.so</file>
+        <file>/usr/lib64/pdns/libpdnsbackend.so.0</file>
+        <file>/usr/lib64/pdns/libpdnsbackend.so.0.0.0</file>
+        <file>/usr/lib64/pdns/libpipebackend.la</file>
+        <file>/usr/lib64/pdns/libpipebackend.so</file>
+        <file>/usr/lib64/pdns/libpipebackend.so.0</file>
+        <file>/usr/lib64/pdns/libpipebackend.so.0.0.0</file>
+        <file>/usr/sbin/pdns_recursor</file>
+        <file>/usr/sbin/pdns_server</file>
+        <file>/usr/sbin/rcpdns</file>
+        <file>/usr/share/doc/packages/pdns/ChangeLog</file>
+        <file>/usr/share/doc/packages/pdns/HACKING</file>
+        <file>/usr/share/doc/packages/pdns/INSTALL</file>
+        <file>/usr/share/doc/packages/pdns/README</file>
+        <file>/usr/share/doc/packages/pdns/TODO</file>
+        <file>/usr/share/doc/packages/pdns/html/HTML.manifest</file>
+        <file>/usr/share/doc/packages/pdns/html/about.html</file>
+        <file>/usr/share/doc/packages/pdns/html/all-settings.html</file>
+        <file>/usr/share/doc/packages/pdns/html/analysis.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-configuration-details.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-error-reporting.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backend-writers-guide.html</file>
+        <file>/usr/share/doc/packages/pdns/html/backends-detail.html</file>
+        <file>/usr/share/doc/packages/pdns/html/bindbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/book1.html</file>
+        <file>/usr/share/doc/packages/pdns/html/built-in-recursor.html</file>
+        <file>/usr/share/doc/packages/pdns/html/changelog.html</file>
+        <file>/usr/share/doc/packages/pdns/html/compiling-powerdns.html</file>
+        <file>/usr/share/doc/packages/pdns/html/configuring-db-connection.html</file>
+        <file>/usr/share/doc/packages/pdns/html/considerations.html</file>
+        <file>/usr/share/doc/packages/pdns/html/db2.html</file>
+        <file>/usr/share/doc/packages/pdns/html/dns-to-query.html</file>
+        <file>/usr/share/doc/packages/pdns/html/docbook.css</file>
+        <file>/usr/share/doc/packages/pdns/html/fancy-records.html</file>
+        <file>/usr/share/doc/packages/pdns/html/faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/generic-mypgsql-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/gsqlite.html</file>
+        <file>/usr/share/doc/packages/pdns/html/guardian.html</file>
+        <file>/usr/share/doc/packages/pdns/html/index.html</file>
+        <file>/usr/share/doc/packages/pdns/html/init-d-commands.html</file>
+        <file>/usr/share/doc/packages/pdns/html/installing-on-unix.html</file>
+        <file>/usr/share/doc/packages/pdns/html/ldap.html</file>
+        <file>/usr/share/doc/packages/pdns/html/license.html</file>
+        <file>/usr/share/doc/packages/pdns/html/master-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/master.html</file>
+        <file>/usr/share/doc/packages/pdns/html/metrics.html</file>
+        <file>/usr/share/doc/packages/pdns/html/migration.html</file>
+        <file>/usr/share/doc/packages/pdns/html/modules.html</file>
+        <file>/usr/share/doc/packages/pdns/html/monitoring.html</file>
+        <file>/usr/share/doc/packages/pdns/html/mysqlbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/nptl.html</file>
+        <file>/usr/share/doc/packages/pdns/html/odbc.html</file>
+        <file>/usr/share/doc/packages/pdns/html/on-windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/oracle.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-devel-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-internals.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-on-unix.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdns-users-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pdnsbackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/performance-settings.html</file>
+        <file>/usr/share/doc/packages/pdns/html/performance.html</file>
+        <file>/usr/share/doc/packages/pdns/html/pipebackend-dynamic-resolution.html</file>
+        <file>/usr/share/doc/packages/pdns/html/powerdns-company-faq.html</file>
+        <file>/usr/share/doc/packages/pdns/html/powerdns.html</file>
+        <file>/usr/share/doc/packages/pdns/html/querycache.html</file>
+        <file>/usr/share/doc/packages/pdns/html/randombackend.html</file>
+        <file>/usr/share/doc/packages/pdns/html/recursion.html</file>
+        <file>/usr/share/doc/packages/pdns/html/replication.html</file>
+        <file>/usr/share/doc/packages/pdns/html/running-on-windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/rw-backends.html</file>
+        <file>/usr/share/doc/packages/pdns/html/security-policy.html</file>
+        <file>/usr/share/doc/packages/pdns/html/security.html</file>
+        <file>/usr/share/doc/packages/pdns/html/slave.html</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/caution.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/home.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/important.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/next.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/note.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/prev.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/tip.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-blank.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-minus.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/toc-plus.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/up.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/stylesheet-images/warning.gif</file>
+        <file>/usr/share/doc/packages/pdns/html/syslog.html</file>
+        <file>/usr/share/doc/packages/pdns/html/testing.html</file>
+        <file>/usr/share/doc/packages/pdns/html/thanks-to.html</file>
+        <file>/usr/share/doc/packages/pdns/html/types.html</file>
+        <file>/usr/share/doc/packages/pdns/html/virtual.html</file>
+        <file>/usr/share/doc/packages/pdns/html/windows.html</file>
+        <file>/usr/share/doc/packages/pdns/html/x2460.html</file>
+        <file>/usr/share/doc/packages/pdns/html/x2472.html</file>
+        <file>/usr/share/doc/packages/pdns/html/xdbbackend.html</file>
+        <file>/usr/share/man/man8/pdns_control.8.gz</file>
+        <file>/usr/share/man/man8/pdns_server.8.gz</file>
+        <file>/usr/share/man/man8/zone2sql.8.gz</file>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="4d7988178018b1d5af490ac899d1cdfd2419ff52" name="dovecot" arch="i586">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+        <file type="dir">/etc/dovecot</file>
+        <file type="dir">/usr/lib/dovecot</file>
+        <file type="dir">/usr/lib/dovecot/modules</file>
+        <file type="dir">/usr/lib/dovecot/modules/imap</file>
+        <file type="dir">/usr/lib/dovecot/modules/lda</file>
+        <file type="dir">/usr/lib/dovecot/modules/pop3</file>
+        <file type="dir">/usr/share/doc/packages/dovecot</file>
+        <file type="dir">/var/run/dovecot</file>
+        <file type="dir">/var/run/dovecot/login</file>
+        <file>/etc/dovecot/dovecot-ldap.conf</file>
+        <file>/etc/dovecot/dovecot-sql.conf</file>
+        <file>/etc/dovecot/dovecot.conf</file>
+        <file>/etc/init.d/dovecot</file>
+        <file>/etc/pam.d/dovecot</file>
+        <file>/usr/lib/dovecot/checkpassword-reply</file>
+        <file>/usr/lib/dovecot/deliver</file>
+        <file>/usr/lib/dovecot/dict</file>
+        <file>/usr/lib/dovecot/dovecot-auth</file>
+        <file>/usr/lib/dovecot/gdbhelper</file>
+        <file>/usr/lib/dovecot/imap</file>
+        <file>/usr/lib/dovecot/imap-login</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_zlib_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib02_imap_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/pop3/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/pop3</file>
+        <file>/usr/lib/dovecot/pop3-login</file>
+        <file>/usr/lib/dovecot/rawlog</file>
+        <file>/usr/lib/dovecot/ssl-build-param</file>
+        <file>/usr/sbin/dovecot</file>
+        <file>/usr/sbin/dovecotpw</file>
+        <file>/usr/sbin/rcdovecot</file>
+        <file>/usr/share/doc/packages/dovecot/ABOUT-NLS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.LGPL</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.MIT</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.gz</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.lda</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/README</file>
+        <file>/usr/share/doc/packages/dovecot/README.SuSE</file>
+        <file>/usr/share/doc/packages/dovecot/README.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/TODO</file>
+        <file>/usr/share/doc/packages/dovecot/USE-WIKI-INSTEAD</file>
+        <file>/usr/share/doc/packages/dovecot/auth-protocol.txt</file>
+        <file>/usr/share/doc/packages/dovecot/auth.txt</file>
+        <file>/usr/share/doc/packages/dovecot/configuration.txt</file>
+        <file>/usr/share/doc/packages/dovecot/design.txt</file>
+        <file>/usr/share/doc/packages/dovecot/dovecot-openssl.cnf</file>
+        <file>/usr/share/doc/packages/dovecot/index.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mail-storages.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mkcert.sh</file>
+        <file>/usr/share/doc/packages/dovecot/multiaccess.txt</file>
+        <file>/usr/share/doc/packages/dovecot/nfs.txt</file>
+        <file>/usr/share/doc/packages/dovecot/securecoding.txt</file>
+        <file>/usr/share/doc/packages/dovecot/variables.txt</file>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="1c550fddb834bce56275ba603276b27296e03cf7" name="dovecot" arch="ppc">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+        <file type="dir">/etc/dovecot</file>
+        <file type="dir">/usr/lib/dovecot</file>
+        <file type="dir">/usr/lib/dovecot/modules</file>
+        <file type="dir">/usr/lib/dovecot/modules/imap</file>
+        <file type="dir">/usr/lib/dovecot/modules/lda</file>
+        <file type="dir">/usr/lib/dovecot/modules/pop3</file>
+        <file type="dir">/usr/share/doc/packages/dovecot</file>
+        <file type="dir">/var/run/dovecot</file>
+        <file type="dir">/var/run/dovecot/login</file>
+        <file>/etc/dovecot/dovecot-ldap.conf</file>
+        <file>/etc/dovecot/dovecot-sql.conf</file>
+        <file>/etc/dovecot/dovecot.conf</file>
+        <file>/etc/init.d/dovecot</file>
+        <file>/etc/pam.d/dovecot</file>
+        <file>/usr/lib/dovecot/checkpassword-reply</file>
+        <file>/usr/lib/dovecot/deliver</file>
+        <file>/usr/lib/dovecot/dict</file>
+        <file>/usr/lib/dovecot/dovecot-auth</file>
+        <file>/usr/lib/dovecot/gdbhelper</file>
+        <file>/usr/lib/dovecot/imap</file>
+        <file>/usr/lib/dovecot/imap-login</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib01_zlib_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib02_imap_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/imap/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lda/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib01_quota_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/lib02_trash_plugin.so</file>
+        <file>/usr/lib/dovecot/modules/pop3/lib01_convert_plugin.so</file>
+        <file>/usr/lib/dovecot/pop3</file>
+        <file>/usr/lib/dovecot/pop3-login</file>
+        <file>/usr/lib/dovecot/rawlog</file>
+        <file>/usr/lib/dovecot/ssl-build-param</file>
+        <file>/usr/sbin/dovecot</file>
+        <file>/usr/sbin/dovecotpw</file>
+        <file>/usr/sbin/rcdovecot</file>
+        <file>/usr/share/doc/packages/dovecot/ABOUT-NLS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.LGPL</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.MIT</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.gz</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.lda</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/README</file>
+        <file>/usr/share/doc/packages/dovecot/README.SuSE</file>
+        <file>/usr/share/doc/packages/dovecot/README.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/TODO</file>
+        <file>/usr/share/doc/packages/dovecot/USE-WIKI-INSTEAD</file>
+        <file>/usr/share/doc/packages/dovecot/auth-protocol.txt</file>
+        <file>/usr/share/doc/packages/dovecot/auth.txt</file>
+        <file>/usr/share/doc/packages/dovecot/configuration.txt</file>
+        <file>/usr/share/doc/packages/dovecot/design.txt</file>
+        <file>/usr/share/doc/packages/dovecot/dovecot-openssl.cnf</file>
+        <file>/usr/share/doc/packages/dovecot/index.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mail-storages.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mkcert.sh</file>
+        <file>/usr/share/doc/packages/dovecot/multiaccess.txt</file>
+        <file>/usr/share/doc/packages/dovecot/nfs.txt</file>
+        <file>/usr/share/doc/packages/dovecot/securecoding.txt</file>
+        <file>/usr/share/doc/packages/dovecot/variables.txt</file>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="a4998429399faa5919990fdbc786a114d7a79d40" name="dovecot" arch="src">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+        <file>dovecot-1.0.beta1_pie.patch</file>
+        <file>dovecot-1.0.beta3.tar.bz2</file>
+        <file>dovecot-1.0.beta3_indexfixes.patch</file>
+        <file>dovecot-1.0.beta7_directory-traversal.patch</file>
+        <file>dovecot-lda.tar.bz2</file>
+        <file>dovecot.README.SuSE</file>
+        <file>dovecot.init</file>
+        <file>dovecot.pam</file>
+        <file>dovecot.spec</file>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="57cd740de9f629fae4c0f6b665634ccfd40845fc" name="dovecot" arch="x86_64">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+        <file type="dir">/etc/dovecot</file>
+        <file type="dir">/usr/lib64/dovecot</file>
+        <file type="dir">/usr/lib64/dovecot/modules</file>
+        <file type="dir">/usr/lib64/dovecot/modules/imap</file>
+        <file type="dir">/usr/lib64/dovecot/modules/lda</file>
+        <file type="dir">/usr/lib64/dovecot/modules/pop3</file>
+        <file type="dir">/usr/share/doc/packages/dovecot</file>
+        <file type="dir">/var/run/dovecot</file>
+        <file type="dir">/var/run/dovecot/login</file>
+        <file>/etc/dovecot/dovecot-ldap.conf</file>
+        <file>/etc/dovecot/dovecot-sql.conf</file>
+        <file>/etc/dovecot/dovecot.conf</file>
+        <file>/etc/init.d/dovecot</file>
+        <file>/etc/pam.d/dovecot</file>
+        <file>/usr/lib64/dovecot/checkpassword-reply</file>
+        <file>/usr/lib64/dovecot/deliver</file>
+        <file>/usr/lib64/dovecot/dict</file>
+        <file>/usr/lib64/dovecot/dovecot-auth</file>
+        <file>/usr/lib64/dovecot/gdbhelper</file>
+        <file>/usr/lib64/dovecot/imap</file>
+        <file>/usr/lib64/dovecot/imap-login</file>
+        <file>/usr/lib64/dovecot/modules/imap/lib01_convert_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/imap/lib01_quota_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/imap/lib01_zlib_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/imap/lib02_imap_quota_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/imap/lib02_trash_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lda/lib01_convert_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lda/lib01_quota_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lda/lib02_trash_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lib01_convert_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lib01_quota_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/lib02_trash_plugin.so</file>
+        <file>/usr/lib64/dovecot/modules/pop3/lib01_convert_plugin.so</file>
+        <file>/usr/lib64/dovecot/pop3</file>
+        <file>/usr/lib64/dovecot/pop3-login</file>
+        <file>/usr/lib64/dovecot/rawlog</file>
+        <file>/usr/lib64/dovecot/ssl-build-param</file>
+        <file>/usr/sbin/dovecot</file>
+        <file>/usr/sbin/dovecotpw</file>
+        <file>/usr/sbin/rcdovecot</file>
+        <file>/usr/share/doc/packages/dovecot/ABOUT-NLS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS</file>
+        <file>/usr/share/doc/packages/dovecot/AUTHORS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.LGPL</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.MIT</file>
+        <file>/usr/share/doc/packages/dovecot/COPYING.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.gz</file>
+        <file>/usr/share/doc/packages/dovecot/ChangeLog.lda</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS</file>
+        <file>/usr/share/doc/packages/dovecot/NEWS.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/README</file>
+        <file>/usr/share/doc/packages/dovecot/README.SuSE</file>
+        <file>/usr/share/doc/packages/dovecot/README.sieve</file>
+        <file>/usr/share/doc/packages/dovecot/TODO</file>
+        <file>/usr/share/doc/packages/dovecot/USE-WIKI-INSTEAD</file>
+        <file>/usr/share/doc/packages/dovecot/auth-protocol.txt</file>
+        <file>/usr/share/doc/packages/dovecot/auth.txt</file>
+        <file>/usr/share/doc/packages/dovecot/configuration.txt</file>
+        <file>/usr/share/doc/packages/dovecot/design.txt</file>
+        <file>/usr/share/doc/packages/dovecot/dovecot-openssl.cnf</file>
+        <file>/usr/share/doc/packages/dovecot/index.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mail-storages.txt</file>
+        <file>/usr/share/doc/packages/dovecot/mkcert.sh</file>
+        <file>/usr/share/doc/packages/dovecot/multiaccess.txt</file>
+        <file>/usr/share/doc/packages/dovecot/nfs.txt</file>
+        <file>/usr/share/doc/packages/dovecot/securecoding.txt</file>
+        <file>/usr/share/doc/packages/dovecot/variables.txt</file>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="0591487b293027292fb55d3fd3402e5dd2cb4184" name="avahi" arch="i586">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+        <file type="dir">/etc/avahi</file>
+        <file type="dir">/etc/avahi/services</file>
+        <file type="dir">/etc/dbus-1</file>
+        <file type="dir">/etc/dbus-1/system.d</file>
+        <file type="dir">/usr/lib/python2.4/site-packages/avahi</file>
+        <file type="dir">/usr/share/avahi</file>
+        <file type="dir">/usr/share/avahi/interfaces</file>
+        <file type="dir">/usr/share/avahi/introspection</file>
+        <file type="dir">/usr/share/doc/packages/avahi</file>
+        <file type="dir">/var/run/avahi-daemon</file>
+        <file>/etc/avahi/avahi-daemon.conf</file>
+        <file>/etc/avahi/avahi-dnsconfd.action</file>
+        <file>/etc/avahi/services/ssh.service</file>
+        <file>/etc/dbus-1/system.d/avahi-dbus.conf</file>
+        <file>/etc/init.d/avahi-daemon</file>
+        <file>/etc/init.d/avahi-dnsconfd</file>
+        <file>/usr/bin/avahi-bookmarks</file>
+        <file>/usr/bin/avahi-browse</file>
+        <file>/usr/bin/avahi-browse-domains</file>
+        <file>/usr/bin/avahi-publish</file>
+        <file>/usr/bin/avahi-publish-address</file>
+        <file>/usr/bin/avahi-publish-service</file>
+        <file>/usr/bin/avahi-resolve</file>
+        <file>/usr/bin/avahi-resolve-address</file>
+        <file>/usr/bin/avahi-resolve-host-name</file>
+        <file>/usr/lib/libavahi-client.so.3</file>
+        <file>/usr/lib/libavahi-client.so.3.1.0</file>
+        <file>/usr/lib/libavahi-common.so.3</file>
+        <file>/usr/lib/libavahi-common.so.3.2.0</file>
+        <file>/usr/lib/libavahi-core.so.3</file>
+        <file>/usr/lib/libavahi-core.so.3.0.3</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/ServiceTypeDatabase.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.pyc</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.pyo</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.pyc</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.pyo</file>
+        <file>/usr/sbin/avahi-daemon</file>
+        <file>/usr/sbin/avahi-dnsconfd</file>
+        <file>/usr/share/avahi/avahi-service.dtd</file>
+        <file>/usr/share/avahi/interfaces/avahi-discover.glade</file>
+        <file>/usr/share/avahi/introspection/AddressResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/DomainBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/EntryGroup.introspect</file>
+        <file>/usr/share/avahi/introspection/HostNameResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/RecordBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/Server.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceTypeBrowser.introspect</file>
+        <file>/usr/share/avahi/service-types</file>
+        <file>/usr/share/avahi/service-types.db</file>
+        <file>/usr/share/doc/packages/avahi/API-CHANGES-0.6</file>
+        <file>/usr/share/doc/packages/avahi/AUTHORS</file>
+        <file>/usr/share/doc/packages/avahi/COMPAT-LAYERS</file>
+        <file>/usr/share/doc/packages/avahi/DBUS-API</file>
+        <file>/usr/share/doc/packages/avahi/HACKING</file>
+        <file>/usr/share/doc/packages/avahi/INSTALL</file>
+        <file>/usr/share/doc/packages/avahi/LICENSE</file>
+        <file>/usr/share/doc/packages/avahi/MALLOC</file>
+        <file>/usr/share/doc/packages/avahi/NEWS</file>
+        <file>/usr/share/doc/packages/avahi/README</file>
+        <file>/usr/share/doc/packages/avahi/TODO</file>
+        <file>/usr/share/doc/packages/avahi/avahi-poll.dia</file>
+        <file>/usr/share/doc/packages/avahi/overview.dia</file>
+        <file>/usr/share/doc/packages/avahi/server-states.dia</file>
+        <file>/usr/share/man/man1/avahi-bookmarks.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse-domains.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse.1.gz</file>
+        <file>/usr/share/man/man1/avahi-discover.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-service.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-host-name.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve.1.gz</file>
+        <file>/usr/share/man/man5/avahi-daemon.conf.5.gz</file>
+        <file>/usr/share/man/man5/avahi.service.5.gz</file>
+        <file>/usr/share/man/man8/avahi-daemon.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.action.8.gz</file>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="4af6068aa9c160924eba7014a5885c2767b51fb0" name="avahi" arch="ppc">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+        <file type="dir">/etc/avahi</file>
+        <file type="dir">/etc/avahi/services</file>
+        <file type="dir">/etc/dbus-1</file>
+        <file type="dir">/etc/dbus-1/system.d</file>
+        <file type="dir">/usr/lib/python2.4/site-packages/avahi</file>
+        <file type="dir">/usr/share/avahi</file>
+        <file type="dir">/usr/share/avahi/interfaces</file>
+        <file type="dir">/usr/share/avahi/introspection</file>
+        <file type="dir">/usr/share/doc/packages/avahi</file>
+        <file type="dir">/var/run/avahi-daemon</file>
+        <file>/etc/avahi/avahi-daemon.conf</file>
+        <file>/etc/avahi/avahi-dnsconfd.action</file>
+        <file>/etc/avahi/services/ssh.service</file>
+        <file>/etc/dbus-1/system.d/avahi-dbus.conf</file>
+        <file>/etc/init.d/avahi-daemon</file>
+        <file>/etc/init.d/avahi-dnsconfd</file>
+        <file>/usr/bin/avahi-bookmarks</file>
+        <file>/usr/bin/avahi-browse</file>
+        <file>/usr/bin/avahi-browse-domains</file>
+        <file>/usr/bin/avahi-publish</file>
+        <file>/usr/bin/avahi-publish-address</file>
+        <file>/usr/bin/avahi-publish-service</file>
+        <file>/usr/bin/avahi-resolve</file>
+        <file>/usr/bin/avahi-resolve-address</file>
+        <file>/usr/bin/avahi-resolve-host-name</file>
+        <file>/usr/lib/libavahi-client.so.3</file>
+        <file>/usr/lib/libavahi-client.so.3.1.0</file>
+        <file>/usr/lib/libavahi-common.so.3</file>
+        <file>/usr/lib/libavahi-common.so.3.2.0</file>
+        <file>/usr/lib/libavahi-core.so.3</file>
+        <file>/usr/lib/libavahi-core.so.3.0.3</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/ServiceTypeDatabase.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.pyc</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/SimpleGladeApp.pyo</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.py</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.pyc</file>
+        <file>/usr/lib/python2.4/site-packages/avahi/__init__.pyo</file>
+        <file>/usr/sbin/avahi-daemon</file>
+        <file>/usr/sbin/avahi-dnsconfd</file>
+        <file>/usr/share/avahi/avahi-service.dtd</file>
+        <file>/usr/share/avahi/interfaces/avahi-discover.glade</file>
+        <file>/usr/share/avahi/introspection/AddressResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/DomainBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/EntryGroup.introspect</file>
+        <file>/usr/share/avahi/introspection/HostNameResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/RecordBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/Server.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceTypeBrowser.introspect</file>
+        <file>/usr/share/avahi/service-types</file>
+        <file>/usr/share/avahi/service-types.db</file>
+        <file>/usr/share/doc/packages/avahi/API-CHANGES-0.6</file>
+        <file>/usr/share/doc/packages/avahi/AUTHORS</file>
+        <file>/usr/share/doc/packages/avahi/COMPAT-LAYERS</file>
+        <file>/usr/share/doc/packages/avahi/DBUS-API</file>
+        <file>/usr/share/doc/packages/avahi/HACKING</file>
+        <file>/usr/share/doc/packages/avahi/INSTALL</file>
+        <file>/usr/share/doc/packages/avahi/LICENSE</file>
+        <file>/usr/share/doc/packages/avahi/MALLOC</file>
+        <file>/usr/share/doc/packages/avahi/NEWS</file>
+        <file>/usr/share/doc/packages/avahi/README</file>
+        <file>/usr/share/doc/packages/avahi/TODO</file>
+        <file>/usr/share/doc/packages/avahi/avahi-poll.dia</file>
+        <file>/usr/share/doc/packages/avahi/overview.dia</file>
+        <file>/usr/share/doc/packages/avahi/server-states.dia</file>
+        <file>/usr/share/man/man1/avahi-bookmarks.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse-domains.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse.1.gz</file>
+        <file>/usr/share/man/man1/avahi-discover.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-service.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-host-name.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve.1.gz</file>
+        <file>/usr/share/man/man5/avahi-daemon.conf.5.gz</file>
+        <file>/usr/share/man/man5/avahi.service.5.gz</file>
+        <file>/usr/share/man/man8/avahi-daemon.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.action.8.gz</file>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="404c792eaaef7fec29a175f2d06fd73de7b727fa" name="avahi" arch="src">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+        <file>avahi-0.6.5.tar.bz2</file>
+        <file>avahi-biarch.patch</file>
+        <file>avahi-compat-error-return.patch</file>
+        <file>avahi-compat-txt-buffer-size.patch</file>
+        <file>avahi-gacdir.patch</file>
+        <file>avahi-init-sleep.patch</file>
+        <file>avahi-utf8-CVE-2006-2288.patch</file>
+        <file>avahi.spec</file>
+        <file>avahi_record_to_string-CVE-2006-2289.patch</file>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="a46d02c6fcf43387c9424c35933f9c0476d35a4b" name="avahi" arch="x86_64">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+        <file type="dir">/etc/avahi</file>
+        <file type="dir">/etc/avahi/services</file>
+        <file type="dir">/etc/dbus-1</file>
+        <file type="dir">/etc/dbus-1/system.d</file>
+        <file type="dir">/usr/lib64/python2.4/site-packages/avahi</file>
+        <file type="dir">/usr/share/avahi</file>
+        <file type="dir">/usr/share/avahi/interfaces</file>
+        <file type="dir">/usr/share/avahi/introspection</file>
+        <file type="dir">/usr/share/doc/packages/avahi</file>
+        <file type="dir">/var/run/avahi-daemon</file>
+        <file>/etc/avahi/avahi-daemon.conf</file>
+        <file>/etc/avahi/avahi-dnsconfd.action</file>
+        <file>/etc/avahi/services/ssh.service</file>
+        <file>/etc/dbus-1/system.d/avahi-dbus.conf</file>
+        <file>/etc/init.d/avahi-daemon</file>
+        <file>/etc/init.d/avahi-dnsconfd</file>
+        <file>/usr/bin/avahi-bookmarks</file>
+        <file>/usr/bin/avahi-browse</file>
+        <file>/usr/bin/avahi-browse-domains</file>
+        <file>/usr/bin/avahi-publish</file>
+        <file>/usr/bin/avahi-publish-address</file>
+        <file>/usr/bin/avahi-publish-service</file>
+        <file>/usr/bin/avahi-resolve</file>
+        <file>/usr/bin/avahi-resolve-address</file>
+        <file>/usr/bin/avahi-resolve-host-name</file>
+        <file>/usr/lib64/libavahi-client.so.3</file>
+        <file>/usr/lib64/libavahi-client.so.3.1.0</file>
+        <file>/usr/lib64/libavahi-common.so.3</file>
+        <file>/usr/lib64/libavahi-common.so.3.2.0</file>
+        <file>/usr/lib64/libavahi-core.so.3</file>
+        <file>/usr/lib64/libavahi-core.so.3.0.3</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/ServiceTypeDatabase.py</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/SimpleGladeApp.py</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/SimpleGladeApp.pyc</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/SimpleGladeApp.pyo</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/__init__.py</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/__init__.pyc</file>
+        <file>/usr/lib64/python2.4/site-packages/avahi/__init__.pyo</file>
+        <file>/usr/sbin/avahi-daemon</file>
+        <file>/usr/sbin/avahi-dnsconfd</file>
+        <file>/usr/share/avahi/avahi-service.dtd</file>
+        <file>/usr/share/avahi/interfaces/avahi-discover.glade</file>
+        <file>/usr/share/avahi/introspection/AddressResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/DomainBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/EntryGroup.introspect</file>
+        <file>/usr/share/avahi/introspection/HostNameResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/RecordBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/Server.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceBrowser.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceResolver.introspect</file>
+        <file>/usr/share/avahi/introspection/ServiceTypeBrowser.introspect</file>
+        <file>/usr/share/avahi/service-types</file>
+        <file>/usr/share/avahi/service-types.db</file>
+        <file>/usr/share/doc/packages/avahi/API-CHANGES-0.6</file>
+        <file>/usr/share/doc/packages/avahi/AUTHORS</file>
+        <file>/usr/share/doc/packages/avahi/COMPAT-LAYERS</file>
+        <file>/usr/share/doc/packages/avahi/DBUS-API</file>
+        <file>/usr/share/doc/packages/avahi/HACKING</file>
+        <file>/usr/share/doc/packages/avahi/INSTALL</file>
+        <file>/usr/share/doc/packages/avahi/LICENSE</file>
+        <file>/usr/share/doc/packages/avahi/MALLOC</file>
+        <file>/usr/share/doc/packages/avahi/NEWS</file>
+        <file>/usr/share/doc/packages/avahi/README</file>
+        <file>/usr/share/doc/packages/avahi/TODO</file>
+        <file>/usr/share/doc/packages/avahi/avahi-poll.dia</file>
+        <file>/usr/share/doc/packages/avahi/overview.dia</file>
+        <file>/usr/share/doc/packages/avahi/server-states.dia</file>
+        <file>/usr/share/man/man1/avahi-bookmarks.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse-domains.1.gz</file>
+        <file>/usr/share/man/man1/avahi-browse.1.gz</file>
+        <file>/usr/share/man/man1/avahi-discover.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish-service.1.gz</file>
+        <file>/usr/share/man/man1/avahi-publish.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-address.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve-host-name.1.gz</file>
+        <file>/usr/share/man/man1/avahi-resolve.1.gz</file>
+        <file>/usr/share/man/man5/avahi-daemon.conf.5.gz</file>
+        <file>/usr/share/man/man5/avahi.service.5.gz</file>
+        <file>/usr/share/man/man8/avahi-daemon.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.8.gz</file>
+        <file>/usr/share/man/man8/avahi-dnsconfd.action.8.gz</file>
+</package>
+
+
+
+
+
+
+
+<package pkgid="52f27233cfb8fc172c9660f1c6b5dadebdede30e" name="nagios-www" arch="i586">
+<version epoch="0" ver="1.3" rel="14.1"/>
+        <file type="dir">/etc/apache2/conf.d</file>
+        <file type="dir">/usr/lib/nagios/cgi</file>
+        <file type="dir">/usr/share/nagios</file>
+        <file type="dir">/usr/share/nagios/contexthelp</file>
+        <file type="dir">/usr/share/nagios/docs</file>
+        <file type="dir">/usr/share/nagios/docs/images</file>
+        <file type="dir">/usr/share/nagios/images</file>
+        <file type="dir">/usr/share/nagios/images/logos</file>
+        <file type="dir">/usr/share/nagios/media</file>
+        <file type="dir">/usr/share/nagios/ssi</file>
+        <file type="dir">/usr/share/nagios/stylesheets</file>
+        <file>/etc/apache2/conf.d/nagios.conf</file>
+        <file>/usr/lib/nagios/cgi/avail.cgi</file>
+        <file>/usr/lib/nagios/cgi/cmd.cgi</file>
+        <file>/usr/lib/nagios/cgi/config.cgi</file>
+        <file>/usr/lib/nagios/cgi/convertcfg</file>
+        <file>/usr/lib/nagios/cgi/daemonchk.cgi</file>
+        <file>/usr/lib/nagios/cgi/extinfo.cgi</file>
+        <file>/usr/lib/nagios/cgi/histogram.cgi</file>
+        <file>/usr/lib/nagios/cgi/history.cgi</file>
+        <file>/usr/lib/nagios/cgi/mini_epn</file>
+        <file>/usr/lib/nagios/cgi/notifications.cgi</file>
+        <file>/usr/lib/nagios/cgi/outages.cgi</file>
+        <file>/usr/lib/nagios/cgi/sap_log.cgi</file>
+        <file>/usr/lib/nagios/cgi/sap_view_log.cgi</file>
+        <file>/usr/lib/nagios/cgi/showlog.cgi</file>
+        <file>/usr/lib/nagios/cgi/status.cgi</file>
+        <file>/usr/lib/nagios/cgi/statusmap.cgi</file>
+        <file>/usr/lib/nagios/cgi/statuswml.cgi</file>
+        <file>/usr/lib/nagios/cgi/statuswrl.cgi</file>
+        <file>/usr/lib/nagios/cgi/summary.cgi</file>
+        <file>/usr/lib/nagios/cgi/tac.cgi</file>
+        <file>/usr/lib/nagios/cgi/traceroute.cgi</file>
+        <file>/usr/lib/nagios/cgi/trends.cgi</file>
+        <file>/usr/share/nagios/contexthelp/A1.html</file>
+        <file>/usr/share/nagios/contexthelp/A2.html</file>
+        <file>/usr/share/nagios/contexthelp/A3.html</file>
+        <file>/usr/share/nagios/contexthelp/A4.html</file>
+        <file>/usr/share/nagios/contexthelp/A5.html</file>
+        <file>/usr/share/nagios/contexthelp/A6.html</file>
+        <file>/usr/share/nagios/contexthelp/A7.html</file>
+        <file>/usr/share/nagios/contexthelp/B1.html</file>
+        <file>/usr/share/nagios/contexthelp/C1.html</file>
+        <file>/usr/share/nagios/contexthelp/D1.html</file>
+        <file>/usr/share/nagios/contexthelp/E1.html</file>
+        <file>/usr/share/nagios/contexthelp/F1.html</file>
+        <file>/usr/share/nagios/contexthelp/G1.html</file>
+        <file>/usr/share/nagios/contexthelp/G2.html</file>
+        <file>/usr/share/nagios/contexthelp/G3.html</file>
+        <file>/usr/share/nagios/contexthelp/G4.html</file>
+        <file>/usr/share/nagios/contexthelp/G5.html</file>
+        <file>/usr/share/nagios/contexthelp/G6.html</file>
+        <file>/usr/share/nagios/contexthelp/H1.html</file>
+        <file>/usr/share/nagios/contexthelp/H2.html</file>
+        <file>/usr/share/nagios/contexthelp/H3.html</file>
+        <file>/usr/share/nagios/contexthelp/H4.html</file>
+        <file>/usr/share/nagios/contexthelp/H5.html</file>
+        <file>/usr/share/nagios/contexthelp/H6.html</file>
+        <file>/usr/share/nagios/contexthelp/H7.html</file>
+        <file>/usr/share/nagios/contexthelp/H8.html</file>
+        <file>/usr/share/nagios/contexthelp/I1.html</file>
+        <file>/usr/share/nagios/contexthelp/I2.html</file>
+        <file>/usr/share/nagios/contexthelp/I3.html</file>
+        <file>/usr/share/nagios/contexthelp/I4.html</file>
+        <file>/usr/share/nagios/contexthelp/I5.html</file>
+        <file>/usr/share/nagios/contexthelp/I6.html</file>
+        <file>/usr/share/nagios/contexthelp/I7.html</file>
+        <file>/usr/share/nagios/contexthelp/I8.html</file>
+        <file>/usr/share/nagios/contexthelp/J1.html</file>
+        <file>/usr/share/nagios/contexthelp/K1.html</file>
+        <file>/usr/share/nagios/contexthelp/L1.html</file>
+        <file>/usr/share/nagios/contexthelp/L10.html</file>
+        <file>/usr/share/nagios/contexthelp/L11.html</file>
+        <file>/usr/share/nagios/contexthelp/L12.html</file>
+        <file>/usr/share/nagios/contexthelp/L13.html</file>
+        <file>/usr/share/nagios/contexthelp/L2.html</file>
+        <file>/usr/share/nagios/contexthelp/L3.html</file>
+        <file>/usr/share/nagios/contexthelp/L4.html</file>
+        <file>/usr/share/nagios/contexthelp/L5.html</file>
+        <file>/usr/share/nagios/contexthelp/L6.html</file>
+        <file>/usr/share/nagios/contexthelp/L7.html</file>
+        <file>/usr/share/nagios/contexthelp/L8.html</file>
+        <file>/usr/share/nagios/contexthelp/L9.html</file>
+        <file>/usr/share/nagios/contexthelp/M1.html</file>
+        <file>/usr/share/nagios/contexthelp/M2.html</file>
+        <file>/usr/share/nagios/contexthelp/M3.html</file>
+        <file>/usr/share/nagios/contexthelp/M4.html</file>
+        <file>/usr/share/nagios/contexthelp/M5.html</file>
+        <file>/usr/share/nagios/contexthelp/M6.html</file>
+        <file>/usr/share/nagios/contexthelp/N1.html</file>
+        <file>/usr/share/nagios/contexthelp/N2.html</file>
+        <file>/usr/share/nagios/contexthelp/N3.html</file>
+        <file>/usr/share/nagios/contexthelp/N4.html</file>
+        <file>/usr/share/nagios/contexthelp/N5.html</file>
+        <file>/usr/share/nagios/contexthelp/N6.html</file>
+        <file>/usr/share/nagios/contexthelp/N7.html</file>
+        <file>/usr/share/nagios/docs/about.html</file>
+        <file>/usr/share/nagios/docs/addons.html</file>
+        <file>/usr/share/nagios/docs/beginners.html</file>
+        <file>/usr/share/nagios/docs/cgiauth.html</file>
+        <file>/usr/share/nagios/docs/cgiincludes.html</file>
+        <file>/usr/share/nagios/docs/cgis.html</file>
+        <file>/usr/share/nagios/docs/checkscheduling.html</file>
+        <file>/usr/share/nagios/docs/clusters.html</file>
+        <file>/usr/share/nagios/docs/commandfile.html</file>
+        <file>/usr/share/nagios/docs/config.html</file>
+        <file>/usr/share/nagios/docs/configcgi.html</file>
+        <file>/usr/share/nagios/docs/configextinfo.html</file>
+        <file>/usr/share/nagios/docs/configmain.html</file>
+        <file>/usr/share/nagios/docs/configobject.html</file>
+        <file>/usr/share/nagios/docs/dependencies.html</file>
+        <file>/usr/share/nagios/docs/distributed.html</file>
+        <file>/usr/share/nagios/docs/downtime.html</file>
+        <file>/usr/share/nagios/docs/embeddedperl.html</file>
+        <file>/usr/share/nagios/docs/escalations.html</file>
+        <file>/usr/share/nagios/docs/eventhandlers.html</file>
+        <file>/usr/share/nagios/docs/extcommands.html</file>
+        <file>/usr/share/nagios/docs/faqs.html</file>
+        <file>/usr/share/nagios/docs/flapping.html</file>
+        <file>/usr/share/nagios/docs/freshness.html</file>
+        <file>/usr/share/nagios/docs/funstuff.html</file>
+        <file>/usr/share/nagios/docs/images/activepassive.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-avail-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-avail-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-cmd.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-config.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-c.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-d.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-histogram.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-history.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-notifications.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-outages.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-showlog.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-c.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-d.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statusmap.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statuswml.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statuswrl.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-summary.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-tac.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-trends.png</file>
+        <file>/usr/share/nagios/docs/images/checktiming.png</file>
+        <file>/usr/share/nagios/docs/images/distributed.png</file>
+        <file>/usr/share/nagios/docs/images/host-dependencies.png</file>
+        <file>/usr/share/nagios/docs/images/indirecthostcheck.png</file>
+        <file>/usr/share/nagios/docs/images/indirectsvccheck.png</file>
+        <file>/usr/share/nagios/docs/images/indirectsvccheck2.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved1.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved2.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved3.png</file>
+        <file>/usr/share/nagios/docs/images/logofullsize.png</file>
+        <file>/usr/share/nagios/docs/images/network-heirarchy.png</file>
+        <file>/usr/share/nagios/docs/images/network-outage1.png</file>
+        <file>/usr/share/nagios/docs/images/network-outage2.png</file>
+        <file>/usr/share/nagios/docs/images/noninterleaved1.png</file>
+        <file>/usr/share/nagios/docs/images/noninterleaved2.png</file>
+        <file>/usr/share/nagios/docs/images/physical-network.png</file>
+        <file>/usr/share/nagios/docs/images/plugintheory.png</file>
+        <file>/usr/share/nagios/docs/images/redudancy.png</file>
+        <file>/usr/share/nagios/docs/images/redundancy.png</file>
+        <file>/usr/share/nagios/docs/images/service-dependencies.png</file>
+        <file>/usr/share/nagios/docs/images/statetransitions.png</file>
+        <file>/usr/share/nagios/docs/images/statetransitions2.png</file>
+        <file>/usr/share/nagios/docs/index.html</file>
+        <file>/usr/share/nagios/docs/indirectchecks.html</file>
+        <file>/usr/share/nagios/docs/installing.html</file>
+        <file>/usr/share/nagios/docs/installweb.html</file>
+        <file>/usr/share/nagios/docs/int-portsentry.html</file>
+        <file>/usr/share/nagios/docs/int-snmptrap.html</file>
+        <file>/usr/share/nagios/docs/int-tcpwrappers.html</file>
+        <file>/usr/share/nagios/docs/macros.html</file>
+        <file>/usr/share/nagios/docs/networkoutages.html</file>
+        <file>/usr/share/nagios/docs/networkreachability.html</file>
+        <file>/usr/share/nagios/docs/notifications.html</file>
+        <file>/usr/share/nagios/docs/parallelization.html</file>
+        <file>/usr/share/nagios/docs/passivechecks.html</file>
+        <file>/usr/share/nagios/docs/perfdata.html</file>
+        <file>/usr/share/nagios/docs/plugins.html</file>
+        <file>/usr/share/nagios/docs/plugins_sap.html</file>
+        <file>/usr/share/nagios/docs/plugintheory.html</file>
+        <file>/usr/share/nagios/docs/redundancy.html</file>
+        <file>/usr/share/nagios/docs/robots.txt</file>
+        <file>/usr/share/nagios/docs/security.html</file>
+        <file>/usr/share/nagios/docs/stalking.html</file>
+        <file>/usr/share/nagios/docs/starting.html</file>
+        <file>/usr/share/nagios/docs/statetypes.html</file>
+        <file>/usr/share/nagios/docs/stoprestart.html</file>
+        <file>/usr/share/nagios/docs/templaterecursion.html</file>
+        <file>/usr/share/nagios/docs/templatetricks.html</file>
+        <file>/usr/share/nagios/docs/timeperiods.html</file>
+        <file>/usr/share/nagios/docs/toc.html</file>
+        <file>/usr/share/nagios/docs/tuning.html</file>
+        <file>/usr/share/nagios/docs/verifyconfig.html</file>
+        <file>/usr/share/nagios/docs/volatileservices.html</file>
+        <file>/usr/share/nagios/docs/whatsnew.html</file>
+        <file>/usr/share/nagios/docs/xdata-db.html</file>
+        <file>/usr/share/nagios/docs/xeddefault.html</file>
+        <file>/usr/share/nagios/docs/xedtemplate.html</file>
+        <file>/usr/share/nagios/docs/xoddefault.html</file>
+        <file>/usr/share/nagios/docs/xodtemplate.html</file>
+        <file>/usr/share/nagios/docs/xpddefault.html</file>
+        <file>/usr/share/nagios/docs/xpdfile.html</file>
+        <file>/usr/share/nagios/images/ack.gif</file>
+        <file>/usr/share/nagios/images/command.png</file>
+        <file>/usr/share/nagios/images/comment.gif</file>
+        <file>/usr/share/nagios/images/contexthelp1.gif</file>
+        <file>/usr/share/nagios/images/contexthelp2.gif</file>
+        <file>/usr/share/nagios/images/critical.png</file>
+        <file>/usr/share/nagios/images/delay.gif</file>
+        <file>/usr/share/nagios/images/delete.gif</file>
+        <file>/usr/share/nagios/images/disabled.gif</file>
+        <file>/usr/share/nagios/images/down.gif</file>
+        <file>/usr/share/nagios/images/downtime.gif</file>
+        <file>/usr/share/nagios/images/empty.gif</file>
+        <file>/usr/share/nagios/images/enabled.gif</file>
+        <file>/usr/share/nagios/images/extinfo.gif</file>
+        <file>/usr/share/nagios/images/flapping.gif</file>
+        <file>/usr/share/nagios/images/greendot.gif</file>
+        <file>/usr/share/nagios/images/histogram.png</file>
+        <file>/usr/share/nagios/images/history.gif</file>
+        <file>/usr/share/nagios/images/hostevent.gif</file>
+        <file>/usr/share/nagios/images/info.png</file>
+        <file>/usr/share/nagios/images/left.gif</file>
+        <file>/usr/share/nagios/images/logofullsize.jpg</file>
+        <file>/usr/share/nagios/images/logos/nagios.gd2</file>
+        <file>/usr/share/nagios/images/logos/nagios.gif</file>
+        <file>/usr/share/nagios/images/logos/nagiosvrml.png</file>
+        <file>/usr/share/nagios/images/logos/unknown.gd2</file>
+        <file>/usr/share/nagios/images/logos/unknown.gif</file>
+        <file>/usr/share/nagios/images/logrotate.png</file>
+        <file>/usr/share/nagios/images/ndisabled.gif</file>
+        <file>/usr/share/nagios/images/noack.gif</file>
+        <file>/usr/share/nagios/images/notes.gif</file>
+        <file>/usr/share/nagios/images/notify.gif</file>
+        <file>/usr/share/nagios/images/orangedot.gif</file>
+        <file>/usr/share/nagios/images/passiveonly.gif</file>
+        <file>/usr/share/nagios/images/recovery.png</file>
+        <file>/usr/share/nagios/images/redudancy.png</file>
+        <file>/usr/share/nagios/images/redundancy.png</file>
+        <file>/usr/share/nagios/images/restart.gif</file>
+        <file>/usr/share/nagios/images/right.gif</file>
+        <file>/usr/share/nagios/images/sbconfig.png</file>
+        <file>/usr/share/nagios/images/sbgeneral.png</file>
+        <file>/usr/share/nagios/images/sblogo.jpg</file>
+        <file>/usr/share/nagios/images/sbmonitor.png</file>
+        <file>/usr/share/nagios/images/sbreport.png</file>
+        <file>/usr/share/nagios/images/serviceevent.gif</file>
+        <file>/usr/share/nagios/images/start.gif</file>
+        <file>/usr/share/nagios/images/status.gif</file>
+        <file>/usr/share/nagios/images/status2.gif</file>
+        <file>/usr/share/nagios/images/status3.gif</file>
+        <file>/usr/share/nagios/images/status4.gif</file>
+        <file>/usr/share/nagios/images/stop.gif</file>
+        <file>/usr/share/nagios/images/tacdisabled.jpg</file>
+        <file>/usr/share/nagios/images/tacdisabled.png</file>
+        <file>/usr/share/nagios/images/tacenabled.jpg</file>
+        <file>/usr/share/nagios/images/tacenabled.png</file>
+        <file>/usr/share/nagios/images/thermcrit.png</file>
+        <file>/usr/share/nagios/images/thermok.png</file>
+        <file>/usr/share/nagios/images/thermwarn.png</file>
+        <file>/usr/share/nagios/images/trends.gif</file>
+        <file>/usr/share/nagios/images/trendshost.png</file>
+        <file>/usr/share/nagios/images/trendssvc.png</file>
+        <file>/usr/share/nagios/images/unknown.png</file>
+        <file>/usr/share/nagios/images/up.gif</file>
+        <file>/usr/share/nagios/images/warning.png</file>
+        <file>/usr/share/nagios/images/weblogo1.png</file>
+        <file>/usr/share/nagios/images/zoom1.gif</file>
+        <file>/usr/share/nagios/images/zoom2.gif</file>
+        <file>/usr/share/nagios/index.html</file>
+        <file>/usr/share/nagios/main.html</file>
+        <file>/usr/share/nagios/media/critical.wav</file>
+        <file>/usr/share/nagios/media/hostdown.wav</file>
+        <file>/usr/share/nagios/media/warning.wav</file>
+        <file>/usr/share/nagios/robots.txt</file>
+        <file>/usr/share/nagios/side.html</file>
+        <file>/usr/share/nagios/stylesheets/avail.css</file>
+        <file>/usr/share/nagios/stylesheets/checksanity.css</file>
+        <file>/usr/share/nagios/stylesheets/cmd.css</file>
+        <file>/usr/share/nagios/stylesheets/config.css</file>
+        <file>/usr/share/nagios/stylesheets/extinfo.css</file>
+        <file>/usr/share/nagios/stylesheets/histogram.css</file>
+        <file>/usr/share/nagios/stylesheets/history.css</file>
+        <file>/usr/share/nagios/stylesheets/ministatus.css</file>
+        <file>/usr/share/nagios/stylesheets/notifications.css</file>
+        <file>/usr/share/nagios/stylesheets/outages.css</file>
+        <file>/usr/share/nagios/stylesheets/showlog.css</file>
+        <file>/usr/share/nagios/stylesheets/status.css</file>
+        <file>/usr/share/nagios/stylesheets/statusmap.css</file>
+        <file>/usr/share/nagios/stylesheets/summary.css</file>
+        <file>/usr/share/nagios/stylesheets/tac.css</file>
+        <file>/usr/share/nagios/stylesheets/trends.css</file>
+</package>
+
+
+
+
+
+
+
+<package pkgid="3dc9c17696ba449e5f7252881bb11f956f6b893a" name="nagios" arch="src">
+<version epoch="0" ver="1.3" rel="14.1"/>
+        <file>content_length.patch</file>
+        <file>logrotate</file>
+        <file>nagios-1.3.tar.bz2</file>
+        <file>nagios-httpd.conf</file>
+        <file>nagios-perl58.dif</file>
+        <file>nagios.spec</file>
+        <file>rcnagios</file>
+        <file>sapmoni.dif</file>
+        <file>size.patch</file>
+</package>
+
+
+
+
+
+
+
+<package pkgid="3bfcce7e5a0d0daf60bc1a89eb2d8e0a4efe6b8a" name="nagios-www" arch="x86_64">
+<version epoch="0" ver="1.3" rel="14.1"/>
+        <file type="dir">/etc/apache2/conf.d</file>
+        <file type="dir">/usr/lib/nagios/cgi</file>
+        <file type="dir">/usr/share/nagios</file>
+        <file type="dir">/usr/share/nagios/contexthelp</file>
+        <file type="dir">/usr/share/nagios/docs</file>
+        <file type="dir">/usr/share/nagios/docs/images</file>
+        <file type="dir">/usr/share/nagios/images</file>
+        <file type="dir">/usr/share/nagios/images/logos</file>
+        <file type="dir">/usr/share/nagios/media</file>
+        <file type="dir">/usr/share/nagios/ssi</file>
+        <file type="dir">/usr/share/nagios/stylesheets</file>
+        <file>/etc/apache2/conf.d/nagios.conf</file>
+        <file>/usr/lib/nagios/cgi/avail.cgi</file>
+        <file>/usr/lib/nagios/cgi/cmd.cgi</file>
+        <file>/usr/lib/nagios/cgi/config.cgi</file>
+        <file>/usr/lib/nagios/cgi/convertcfg</file>
+        <file>/usr/lib/nagios/cgi/daemonchk.cgi</file>
+        <file>/usr/lib/nagios/cgi/extinfo.cgi</file>
+        <file>/usr/lib/nagios/cgi/histogram.cgi</file>
+        <file>/usr/lib/nagios/cgi/history.cgi</file>
+        <file>/usr/lib/nagios/cgi/mini_epn</file>
+        <file>/usr/lib/nagios/cgi/notifications.cgi</file>
+        <file>/usr/lib/nagios/cgi/outages.cgi</file>
+        <file>/usr/lib/nagios/cgi/sap_log.cgi</file>
+        <file>/usr/lib/nagios/cgi/sap_view_log.cgi</file>
+        <file>/usr/lib/nagios/cgi/showlog.cgi</file>
+        <file>/usr/lib/nagios/cgi/status.cgi</file>
+        <file>/usr/lib/nagios/cgi/statusmap.cgi</file>
+        <file>/usr/lib/nagios/cgi/statuswml.cgi</file>
+        <file>/usr/lib/nagios/cgi/statuswrl.cgi</file>
+        <file>/usr/lib/nagios/cgi/summary.cgi</file>
+        <file>/usr/lib/nagios/cgi/tac.cgi</file>
+        <file>/usr/lib/nagios/cgi/traceroute.cgi</file>
+        <file>/usr/lib/nagios/cgi/trends.cgi</file>
+        <file>/usr/share/nagios/contexthelp/A1.html</file>
+        <file>/usr/share/nagios/contexthelp/A2.html</file>
+        <file>/usr/share/nagios/contexthelp/A3.html</file>
+        <file>/usr/share/nagios/contexthelp/A4.html</file>
+        <file>/usr/share/nagios/contexthelp/A5.html</file>
+        <file>/usr/share/nagios/contexthelp/A6.html</file>
+        <file>/usr/share/nagios/contexthelp/A7.html</file>
+        <file>/usr/share/nagios/contexthelp/B1.html</file>
+        <file>/usr/share/nagios/contexthelp/C1.html</file>
+        <file>/usr/share/nagios/contexthelp/D1.html</file>
+        <file>/usr/share/nagios/contexthelp/E1.html</file>
+        <file>/usr/share/nagios/contexthelp/F1.html</file>
+        <file>/usr/share/nagios/contexthelp/G1.html</file>
+        <file>/usr/share/nagios/contexthelp/G2.html</file>
+        <file>/usr/share/nagios/contexthelp/G3.html</file>
+        <file>/usr/share/nagios/contexthelp/G4.html</file>
+        <file>/usr/share/nagios/contexthelp/G5.html</file>
+        <file>/usr/share/nagios/contexthelp/G6.html</file>
+        <file>/usr/share/nagios/contexthelp/H1.html</file>
+        <file>/usr/share/nagios/contexthelp/H2.html</file>
+        <file>/usr/share/nagios/contexthelp/H3.html</file>
+        <file>/usr/share/nagios/contexthelp/H4.html</file>
+        <file>/usr/share/nagios/contexthelp/H5.html</file>
+        <file>/usr/share/nagios/contexthelp/H6.html</file>
+        <file>/usr/share/nagios/contexthelp/H7.html</file>
+        <file>/usr/share/nagios/contexthelp/H8.html</file>
+        <file>/usr/share/nagios/contexthelp/I1.html</file>
+        <file>/usr/share/nagios/contexthelp/I2.html</file>
+        <file>/usr/share/nagios/contexthelp/I3.html</file>
+        <file>/usr/share/nagios/contexthelp/I4.html</file>
+        <file>/usr/share/nagios/contexthelp/I5.html</file>
+        <file>/usr/share/nagios/contexthelp/I6.html</file>
+        <file>/usr/share/nagios/contexthelp/I7.html</file>
+        <file>/usr/share/nagios/contexthelp/I8.html</file>
+        <file>/usr/share/nagios/contexthelp/J1.html</file>
+        <file>/usr/share/nagios/contexthelp/K1.html</file>
+        <file>/usr/share/nagios/contexthelp/L1.html</file>
+        <file>/usr/share/nagios/contexthelp/L10.html</file>
+        <file>/usr/share/nagios/contexthelp/L11.html</file>
+        <file>/usr/share/nagios/contexthelp/L12.html</file>
+        <file>/usr/share/nagios/contexthelp/L13.html</file>
+        <file>/usr/share/nagios/contexthelp/L2.html</file>
+        <file>/usr/share/nagios/contexthelp/L3.html</file>
+        <file>/usr/share/nagios/contexthelp/L4.html</file>
+        <file>/usr/share/nagios/contexthelp/L5.html</file>
+        <file>/usr/share/nagios/contexthelp/L6.html</file>
+        <file>/usr/share/nagios/contexthelp/L7.html</file>
+        <file>/usr/share/nagios/contexthelp/L8.html</file>
+        <file>/usr/share/nagios/contexthelp/L9.html</file>
+        <file>/usr/share/nagios/contexthelp/M1.html</file>
+        <file>/usr/share/nagios/contexthelp/M2.html</file>
+        <file>/usr/share/nagios/contexthelp/M3.html</file>
+        <file>/usr/share/nagios/contexthelp/M4.html</file>
+        <file>/usr/share/nagios/contexthelp/M5.html</file>
+        <file>/usr/share/nagios/contexthelp/M6.html</file>
+        <file>/usr/share/nagios/contexthelp/N1.html</file>
+        <file>/usr/share/nagios/contexthelp/N2.html</file>
+        <file>/usr/share/nagios/contexthelp/N3.html</file>
+        <file>/usr/share/nagios/contexthelp/N4.html</file>
+        <file>/usr/share/nagios/contexthelp/N5.html</file>
+        <file>/usr/share/nagios/contexthelp/N6.html</file>
+        <file>/usr/share/nagios/contexthelp/N7.html</file>
+        <file>/usr/share/nagios/docs/about.html</file>
+        <file>/usr/share/nagios/docs/addons.html</file>
+        <file>/usr/share/nagios/docs/beginners.html</file>
+        <file>/usr/share/nagios/docs/cgiauth.html</file>
+        <file>/usr/share/nagios/docs/cgiincludes.html</file>
+        <file>/usr/share/nagios/docs/cgis.html</file>
+        <file>/usr/share/nagios/docs/checkscheduling.html</file>
+        <file>/usr/share/nagios/docs/clusters.html</file>
+        <file>/usr/share/nagios/docs/commandfile.html</file>
+        <file>/usr/share/nagios/docs/config.html</file>
+        <file>/usr/share/nagios/docs/configcgi.html</file>
+        <file>/usr/share/nagios/docs/configextinfo.html</file>
+        <file>/usr/share/nagios/docs/configmain.html</file>
+        <file>/usr/share/nagios/docs/configobject.html</file>
+        <file>/usr/share/nagios/docs/dependencies.html</file>
+        <file>/usr/share/nagios/docs/distributed.html</file>
+        <file>/usr/share/nagios/docs/downtime.html</file>
+        <file>/usr/share/nagios/docs/embeddedperl.html</file>
+        <file>/usr/share/nagios/docs/escalations.html</file>
+        <file>/usr/share/nagios/docs/eventhandlers.html</file>
+        <file>/usr/share/nagios/docs/extcommands.html</file>
+        <file>/usr/share/nagios/docs/faqs.html</file>
+        <file>/usr/share/nagios/docs/flapping.html</file>
+        <file>/usr/share/nagios/docs/freshness.html</file>
+        <file>/usr/share/nagios/docs/funstuff.html</file>
+        <file>/usr/share/nagios/docs/images/activepassive.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-avail-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-avail-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-cmd.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-config.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-c.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-extinfo-d.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-histogram.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-history.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-notifications.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-outages.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-showlog.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-a.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-b.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-c.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-status-d.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statusmap.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statuswml.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-statuswrl.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-summary.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-tac.png</file>
+        <file>/usr/share/nagios/docs/images/cgi-trends.png</file>
+        <file>/usr/share/nagios/docs/images/checktiming.png</file>
+        <file>/usr/share/nagios/docs/images/distributed.png</file>
+        <file>/usr/share/nagios/docs/images/host-dependencies.png</file>
+        <file>/usr/share/nagios/docs/images/indirecthostcheck.png</file>
+        <file>/usr/share/nagios/docs/images/indirectsvccheck.png</file>
+        <file>/usr/share/nagios/docs/images/indirectsvccheck2.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved1.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved2.png</file>
+        <file>/usr/share/nagios/docs/images/interleaved3.png</file>
+        <file>/usr/share/nagios/docs/images/logofullsize.png</file>
+        <file>/usr/share/nagios/docs/images/network-heirarchy.png</file>
+        <file>/usr/share/nagios/docs/images/network-outage1.png</file>
+        <file>/usr/share/nagios/docs/images/network-outage2.png</file>
+        <file>/usr/share/nagios/docs/images/noninterleaved1.png</file>
+        <file>/usr/share/nagios/docs/images/noninterleaved2.png</file>
+        <file>/usr/share/nagios/docs/images/physical-network.png</file>
+        <file>/usr/share/nagios/docs/images/plugintheory.png</file>
+        <file>/usr/share/nagios/docs/images/redudancy.png</file>
+        <file>/usr/share/nagios/docs/images/redundancy.png</file>
+        <file>/usr/share/nagios/docs/images/service-dependencies.png</file>
+        <file>/usr/share/nagios/docs/images/statetransitions.png</file>
+        <file>/usr/share/nagios/docs/images/statetransitions2.png</file>
+        <file>/usr/share/nagios/docs/index.html</file>
+        <file>/usr/share/nagios/docs/indirectchecks.html</file>
+        <file>/usr/share/nagios/docs/installing.html</file>
+        <file>/usr/share/nagios/docs/installweb.html</file>
+        <file>/usr/share/nagios/docs/int-portsentry.html</file>
+        <file>/usr/share/nagios/docs/int-snmptrap.html</file>
+        <file>/usr/share/nagios/docs/int-tcpwrappers.html</file>
+        <file>/usr/share/nagios/docs/macros.html</file>
+        <file>/usr/share/nagios/docs/networkoutages.html</file>
+        <file>/usr/share/nagios/docs/networkreachability.html</file>
+        <file>/usr/share/nagios/docs/notifications.html</file>
+        <file>/usr/share/nagios/docs/parallelization.html</file>
+        <file>/usr/share/nagios/docs/passivechecks.html</file>
+        <file>/usr/share/nagios/docs/perfdata.html</file>
+        <file>/usr/share/nagios/docs/plugins.html</file>
+        <file>/usr/share/nagios/docs/plugins_sap.html</file>
+        <file>/usr/share/nagios/docs/plugintheory.html</file>
+        <file>/usr/share/nagios/docs/redundancy.html</file>
+        <file>/usr/share/nagios/docs/robots.txt</file>
+        <file>/usr/share/nagios/docs/security.html</file>
+        <file>/usr/share/nagios/docs/stalking.html</file>
+        <file>/usr/share/nagios/docs/starting.html</file>
+        <file>/usr/share/nagios/docs/statetypes.html</file>
+        <file>/usr/share/nagios/docs/stoprestart.html</file>
+        <file>/usr/share/nagios/docs/templaterecursion.html</file>
+        <file>/usr/share/nagios/docs/templatetricks.html</file>
+        <file>/usr/share/nagios/docs/timeperiods.html</file>
+        <file>/usr/share/nagios/docs/toc.html</file>
+        <file>/usr/share/nagios/docs/tuning.html</file>
+        <file>/usr/share/nagios/docs/verifyconfig.html</file>
+        <file>/usr/share/nagios/docs/volatileservices.html</file>
+        <file>/usr/share/nagios/docs/whatsnew.html</file>
+        <file>/usr/share/nagios/docs/xdata-db.html</file>
+        <file>/usr/share/nagios/docs/xeddefault.html</file>
+        <file>/usr/share/nagios/docs/xedtemplate.html</file>
+        <file>/usr/share/nagios/docs/xoddefault.html</file>
+        <file>/usr/share/nagios/docs/xodtemplate.html</file>
+        <file>/usr/share/nagios/docs/xpddefault.html</file>
+        <file>/usr/share/nagios/docs/xpdfile.html</file>
+        <file>/usr/share/nagios/images/ack.gif</file>
+        <file>/usr/share/nagios/images/command.png</file>
+        <file>/usr/share/nagios/images/comment.gif</file>
+        <file>/usr/share/nagios/images/contexthelp1.gif</file>
+        <file>/usr/share/nagios/images/contexthelp2.gif</file>
+        <file>/usr/share/nagios/images/critical.png</file>
+        <file>/usr/share/nagios/images/delay.gif</file>
+        <file>/usr/share/nagios/images/delete.gif</file>
+        <file>/usr/share/nagios/images/disabled.gif</file>
+        <file>/usr/share/nagios/images/down.gif</file>
+        <file>/usr/share/nagios/images/downtime.gif</file>
+        <file>/usr/share/nagios/images/empty.gif</file>
+        <file>/usr/share/nagios/images/enabled.gif</file>
+        <file>/usr/share/nagios/images/extinfo.gif</file>
+        <file>/usr/share/nagios/images/flapping.gif</file>
+        <file>/usr/share/nagios/images/greendot.gif</file>
+        <file>/usr/share/nagios/images/histogram.png</file>
+        <file>/usr/share/nagios/images/history.gif</file>
+        <file>/usr/share/nagios/images/hostevent.gif</file>
+        <file>/usr/share/nagios/images/info.png</file>
+        <file>/usr/share/nagios/images/left.gif</file>
+        <file>/usr/share/nagios/images/logofullsize.jpg</file>
+        <file>/usr/share/nagios/images/logos/nagios.gd2</file>
+        <file>/usr/share/nagios/images/logos/nagios.gif</file>
+        <file>/usr/share/nagios/images/logos/nagiosvrml.png</file>
+        <file>/usr/share/nagios/images/logos/unknown.gd2</file>
+        <file>/usr/share/nagios/images/logos/unknown.gif</file>
+        <file>/usr/share/nagios/images/logrotate.png</file>
+        <file>/usr/share/nagios/images/ndisabled.gif</file>
+        <file>/usr/share/nagios/images/noack.gif</file>
+        <file>/usr/share/nagios/images/notes.gif</file>
+        <file>/usr/share/nagios/images/notify.gif</file>
+        <file>/usr/share/nagios/images/orangedot.gif</file>
+        <file>/usr/share/nagios/images/passiveonly.gif</file>
+        <file>/usr/share/nagios/images/recovery.png</file>
+        <file>/usr/share/nagios/images/redudancy.png</file>
+        <file>/usr/share/nagios/images/redundancy.png</file>
+        <file>/usr/share/nagios/images/restart.gif</file>
+        <file>/usr/share/nagios/images/right.gif</file>
+        <file>/usr/share/nagios/images/sbconfig.png</file>
+        <file>/usr/share/nagios/images/sbgeneral.png</file>
+        <file>/usr/share/nagios/images/sblogo.jpg</file>
+        <file>/usr/share/nagios/images/sbmonitor.png</file>
+        <file>/usr/share/nagios/images/sbreport.png</file>
+        <file>/usr/share/nagios/images/serviceevent.gif</file>
+        <file>/usr/share/nagios/images/start.gif</file>
+        <file>/usr/share/nagios/images/status.gif</file>
+        <file>/usr/share/nagios/images/status2.gif</file>
+        <file>/usr/share/nagios/images/status3.gif</file>
+        <file>/usr/share/nagios/images/status4.gif</file>
+        <file>/usr/share/nagios/images/stop.gif</file>
+        <file>/usr/share/nagios/images/tacdisabled.jpg</file>
+        <file>/usr/share/nagios/images/tacdisabled.png</file>
+        <file>/usr/share/nagios/images/tacenabled.jpg</file>
+        <file>/usr/share/nagios/images/tacenabled.png</file>
+        <file>/usr/share/nagios/images/thermcrit.png</file>
+        <file>/usr/share/nagios/images/thermok.png</file>
+        <file>/usr/share/nagios/images/thermwarn.png</file>
+        <file>/usr/share/nagios/images/trends.gif</file>
+        <file>/usr/share/nagios/images/trendshost.png</file>
+        <file>/usr/share/nagios/images/trendssvc.png</file>
+        <file>/usr/share/nagios/images/unknown.png</file>
+        <file>/usr/share/nagios/images/up.gif</file>
+        <file>/usr/share/nagios/images/warning.png</file>
+        <file>/usr/share/nagios/images/weblogo1.png</file>
+        <file>/usr/share/nagios/images/zoom1.gif</file>
+        <file>/usr/share/nagios/images/zoom2.gif</file>
+        <file>/usr/share/nagios/index.html</file>
+        <file>/usr/share/nagios/main.html</file>
+        <file>/usr/share/nagios/media/critical.wav</file>
+        <file>/usr/share/nagios/media/hostdown.wav</file>
+        <file>/usr/share/nagios/media/warning.wav</file>
+        <file>/usr/share/nagios/robots.txt</file>
+        <file>/usr/share/nagios/side.html</file>
+        <file>/usr/share/nagios/stylesheets/avail.css</file>
+        <file>/usr/share/nagios/stylesheets/checksanity.css</file>
+        <file>/usr/share/nagios/stylesheets/cmd.css</file>
+        <file>/usr/share/nagios/stylesheets/config.css</file>
+        <file>/usr/share/nagios/stylesheets/extinfo.css</file>
+        <file>/usr/share/nagios/stylesheets/histogram.css</file>
+        <file>/usr/share/nagios/stylesheets/history.css</file>
+        <file>/usr/share/nagios/stylesheets/ministatus.css</file>
+        <file>/usr/share/nagios/stylesheets/notifications.css</file>
+        <file>/usr/share/nagios/stylesheets/outages.css</file>
+        <file>/usr/share/nagios/stylesheets/showlog.css</file>
+        <file>/usr/share/nagios/stylesheets/status.css</file>
+        <file>/usr/share/nagios/stylesheets/statusmap.css</file>
+        <file>/usr/share/nagios/stylesheets/summary.css</file>
+        <file>/usr/share/nagios/stylesheets/tac.css</file>
+        <file>/usr/share/nagios/stylesheets/trends.css</file>
+</package>
+
+
+
+
+
+
+<package pkgid="23b988b5b6e50d7cfda50dda22508c5f1dd07307" name="dhcdbd" arch="i586">
+<version epoch="0" ver="1.12" rel="14.2"/>
+        <file type="dir">/usr/share/doc/packages/dhcdbd</file>
+        <file type="dir">/var/lib/named</file>
+        <file>/etc/dbus-1/system.d/dhcdbd.conf</file>
+        <file>/usr/sbin/dhcdbd</file>
+        <file>/usr/share/doc/packages/dhcdbd/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcdbd/README</file>
+        <file>/usr/share/doc/packages/dhcdbd/dbus_service.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcdbd.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcp_options.h</file>
+</package>
+
+
+
+
+
+
+<package pkgid="da37d6c81230024f202fbb92107ab88ade872bd3" name="dhcdbd" arch="ppc">
+<version epoch="0" ver="1.12" rel="14.2"/>
+        <file type="dir">/usr/share/doc/packages/dhcdbd</file>
+        <file type="dir">/var/lib/named</file>
+        <file>/etc/dbus-1/system.d/dhcdbd.conf</file>
+        <file>/usr/sbin/dhcdbd</file>
+        <file>/usr/share/doc/packages/dhcdbd/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcdbd/README</file>
+        <file>/usr/share/doc/packages/dhcdbd/dbus_service.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcdbd.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcp_options.h</file>
+</package>
+
+
+
+
+
+
+<package pkgid="e285ea0354d8c33ec6c631e5ed925142b9e30bf4" name="dhcdbd" arch="src">
+<version epoch="0" ver="1.12" rel="14.2"/>
+        <file>dhcdbd-1.12.tar.gz</file>
+        <file>dhcdbd-1.14-bug-fixes.patch</file>
+        <file>dhcdbd-dbus-reconnect-thoenig-02.patch</file>
+        <file>dhcdbd-lease-dir-rml-1.10-1.patch</file>
+        <file>dhcdbd-less-verbose-logging-rml.patch</file>
+        <file>dhcdbd-no-nr_open-rml.patch</file>
+        <file>dhcdbd-remove-named-user-rml.patch</file>
+        <file>dhcdbd-set-hostname.patch</file>
+        <file>dhcdbd.spec</file>
+</package>
+
+
+
+
+
+
+<package pkgid="bca8cb431261bf677ea69c848c1d54a34b16189a" name="dhcdbd" arch="x86_64">
+<version epoch="0" ver="1.12" rel="14.2"/>
+        <file type="dir">/usr/share/doc/packages/dhcdbd</file>
+        <file type="dir">/var/lib/named</file>
+        <file>/etc/dbus-1/system.d/dhcdbd.conf</file>
+        <file>/usr/sbin/dhcdbd</file>
+        <file>/usr/share/doc/packages/dhcdbd/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcdbd/README</file>
+        <file>/usr/share/doc/packages/dhcdbd/dbus_service.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcdbd.h</file>
+        <file>/usr/share/doc/packages/dhcdbd/dhcp_options.h</file>
+</package>
+
+
+
+
+
+<package pkgid="a6a40ca62165ceddc09181a9c6d11243b544dbc3" name="openldap2" arch="i586">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+        <file type="dir">/etc/openldap</file>
+        <file type="dir">/etc/openldap/schema</file>
+        <file type="dir">/usr/lib/openldap</file>
+        <file type="dir">/usr/lib/openldap/modules</file>
+        <file type="dir">/usr/share/doc/packages/openldap2</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/admin-guide</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/drafts</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/install</file>
+        <file type="dir">/usr/share/openldap</file>
+        <file type="dir">/usr/share/openldap/ucdata</file>
+        <file type="dir">/var/lib/ldap</file>
+        <file type="dir">/var/lib/slurpd</file>
+        <file type="dir">/var/run/slapd</file>
+        <file>/etc/init.d/ldap</file>
+        <file>/etc/init.d/slurpd</file>
+        <file>/etc/openldap/schema/README</file>
+        <file>/etc/openldap/schema/corba.schema</file>
+        <file>/etc/openldap/schema/core.ldif</file>
+        <file>/etc/openldap/schema/core.schema</file>
+        <file>/etc/openldap/schema/cosine.schema</file>
+        <file>/etc/openldap/schema/dyngroup.schema</file>
+        <file>/etc/openldap/schema/inetorgperson.schema</file>
+        <file>/etc/openldap/schema/java.schema</file>
+        <file>/etc/openldap/schema/misc.schema</file>
+        <file>/etc/openldap/schema/nis.schema</file>
+        <file>/etc/openldap/schema/openldap.ldif</file>
+        <file>/etc/openldap/schema/openldap.schema</file>
+        <file>/etc/openldap/schema/ppolicy.schema</file>
+        <file>/etc/openldap/schema/rfc2307bis.schema</file>
+        <file>/etc/openldap/schema/yast.schema</file>
+        <file>/etc/openldap/slapd.conf</file>
+        <file>/etc/openldap/slapd.conf.default</file>
+        <file>/usr/lib/openldap/slapd</file>
+        <file>/usr/lib/openldap/slurpd</file>
+        <file>/usr/lib/sasl2/slapd.conf</file>
+        <file>/usr/sbin/openldap-2.2-slapcat</file>
+        <file>/usr/sbin/rcldap</file>
+        <file>/usr/sbin/rcslurpd</file>
+        <file>/usr/sbin/slapacl</file>
+        <file>/usr/sbin/slapadd</file>
+        <file>/usr/sbin/slapauth</file>
+        <file>/usr/sbin/slapcat</file>
+        <file>/usr/sbin/slapdn</file>
+        <file>/usr/sbin/slapindex</file>
+        <file>/usr/sbin/slappasswd</file>
+        <file>/usr/sbin/slaptest</file>
+        <file>/usr/share/doc/packages/openldap2/ANNOUNCEMENT</file>
+        <file>/usr/share/doc/packages/openldap2/COPYRIGHT</file>
+        <file>/usr/share/doc/packages/openldap2/INSTALL</file>
+        <file>/usr/share/doc/packages/openldap2/LICENSE</file>
+        <file>/usr/share/doc/packages/openldap2/README</file>
+        <file>/usr/share/doc/packages/openldap2/README.update</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/autoconf.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_dit.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_local.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_ref.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_repl.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/copyright.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/dbtools.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/index.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/install.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_dctree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_tree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/license.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/preface.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/proxycache.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/quickstart.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/referrals.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/replication.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/runningslapd.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/sasl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/schema.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/security.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconf2.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconfig.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/syncrepl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/tls.html</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/README</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-behera-ldap-password-policy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-chu-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-authmeth-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-bcp64-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-dn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-filter-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-models-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-protocol-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-roadmap-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-strprep-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-syntaxes-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-url-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-user-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-acl-model-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldap-c-api-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-dupent-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-vlv-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-locate-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-joslin-config-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-lachman-laser-ldap-mail-routing-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-bac-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-binary-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-transfer-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-chaining-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-distproc-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-subordinate-scope-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-weltman-ldapv3-proxy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-adlist-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-assert-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-authzid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-cosine-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-dontusecopy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-incr.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-noop-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-readentry-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-t-f-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-turn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-uuid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-x509-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldup-sync-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/install/configure</file>
+        <file>/usr/share/man/man5/slapd-bdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-hdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldap.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldbm.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldif.5.gz</file>
+        <file>/usr/share/man/man5/slapd-monitor.5.gz</file>
+        <file>/usr/share/man/man5/slapd-relay.5.gz</file>
+        <file>/usr/share/man/man5/slapd.access.5.gz</file>
+        <file>/usr/share/man/man5/slapd.conf.5.gz</file>
+        <file>/usr/share/man/man5/slapd.plugin.5.gz</file>
+        <file>/usr/share/man/man5/slapd.replog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-accesslog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-auditlog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-chain.5.gz</file>
+        <file>/usr/share/man/man5/slapo-dynlist.5.gz</file>
+        <file>/usr/share/man/man5/slapo-lastmod.5.gz</file>
+        <file>/usr/share/man/man5/slapo-pcache.5.gz</file>
+        <file>/usr/share/man/man5/slapo-ppolicy.5.gz</file>
+        <file>/usr/share/man/man5/slapo-refint.5.gz</file>
+        <file>/usr/share/man/man5/slapo-retcode.5.gz</file>
+        <file>/usr/share/man/man5/slapo-rwm.5.gz</file>
+        <file>/usr/share/man/man5/slapo-syncprov.5.gz</file>
+        <file>/usr/share/man/man5/slapo-translucent.5.gz</file>
+        <file>/usr/share/man/man5/slapo-unique.5.gz</file>
+        <file>/usr/share/man/man5/slapo-valsort.5.gz</file>
+        <file>/usr/share/man/man8/slapacl.8.gz</file>
+        <file>/usr/share/man/man8/slapadd.8.gz</file>
+        <file>/usr/share/man/man8/slapauth.8.gz</file>
+        <file>/usr/share/man/man8/slapcat.8.gz</file>
+        <file>/usr/share/man/man8/slapd.8.gz</file>
+        <file>/usr/share/man/man8/slapdn.8.gz</file>
+        <file>/usr/share/man/man8/slapindex.8.gz</file>
+        <file>/usr/share/man/man8/slappasswd.8.gz</file>
+        <file>/usr/share/man/man8/slaptest.8.gz</file>
+        <file>/usr/share/man/man8/slurpd.8.gz</file>
+        <file>/usr/share/openldap/ucdata/case.dat</file>
+        <file>/usr/share/openldap/ucdata/cmbcl.dat</file>
+        <file>/usr/share/openldap/ucdata/comp.dat</file>
+        <file>/usr/share/openldap/ucdata/ctype.dat</file>
+        <file>/usr/share/openldap/ucdata/decomp.dat</file>
+        <file>/usr/share/openldap/ucdata/kdecomp.dat</file>
+        <file>/usr/share/openldap/ucdata/num.dat</file>
+        <file>/usr/share/update-messages/en/openldap2.1</file>
+        <file>/var/adm/fillup-templates/sysconfig.openldap</file>
+        <file>/var/lib/ldap/DB_CONFIG</file>
+        <file>/var/lib/ldap/DB_CONFIG.example</file>
+</package>
+
+
+
+
+
+<package pkgid="2fe6c9ec6a1f7e52edc54154f70421ce9aeb5f96" name="openldap2" arch="ppc">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+        <file type="dir">/etc/openldap</file>
+        <file type="dir">/etc/openldap/schema</file>
+        <file type="dir">/usr/lib/openldap</file>
+        <file type="dir">/usr/lib/openldap/modules</file>
+        <file type="dir">/usr/share/doc/packages/openldap2</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/admin-guide</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/drafts</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/install</file>
+        <file type="dir">/usr/share/openldap</file>
+        <file type="dir">/usr/share/openldap/ucdata</file>
+        <file type="dir">/var/lib/ldap</file>
+        <file type="dir">/var/lib/slurpd</file>
+        <file type="dir">/var/run/slapd</file>
+        <file>/etc/init.d/ldap</file>
+        <file>/etc/init.d/slurpd</file>
+        <file>/etc/openldap/schema/README</file>
+        <file>/etc/openldap/schema/corba.schema</file>
+        <file>/etc/openldap/schema/core.ldif</file>
+        <file>/etc/openldap/schema/core.schema</file>
+        <file>/etc/openldap/schema/cosine.schema</file>
+        <file>/etc/openldap/schema/dyngroup.schema</file>
+        <file>/etc/openldap/schema/inetorgperson.schema</file>
+        <file>/etc/openldap/schema/java.schema</file>
+        <file>/etc/openldap/schema/misc.schema</file>
+        <file>/etc/openldap/schema/nis.schema</file>
+        <file>/etc/openldap/schema/openldap.ldif</file>
+        <file>/etc/openldap/schema/openldap.schema</file>
+        <file>/etc/openldap/schema/ppolicy.schema</file>
+        <file>/etc/openldap/schema/rfc2307bis.schema</file>
+        <file>/etc/openldap/schema/yast.schema</file>
+        <file>/etc/openldap/slapd.conf</file>
+        <file>/etc/openldap/slapd.conf.default</file>
+        <file>/usr/lib/openldap/slapd</file>
+        <file>/usr/lib/openldap/slurpd</file>
+        <file>/usr/lib/sasl2/slapd.conf</file>
+        <file>/usr/sbin/openldap-2.2-slapcat</file>
+        <file>/usr/sbin/rcldap</file>
+        <file>/usr/sbin/rcslurpd</file>
+        <file>/usr/sbin/slapacl</file>
+        <file>/usr/sbin/slapadd</file>
+        <file>/usr/sbin/slapauth</file>
+        <file>/usr/sbin/slapcat</file>
+        <file>/usr/sbin/slapdn</file>
+        <file>/usr/sbin/slapindex</file>
+        <file>/usr/sbin/slappasswd</file>
+        <file>/usr/sbin/slaptest</file>
+        <file>/usr/share/doc/packages/openldap2/ANNOUNCEMENT</file>
+        <file>/usr/share/doc/packages/openldap2/COPYRIGHT</file>
+        <file>/usr/share/doc/packages/openldap2/INSTALL</file>
+        <file>/usr/share/doc/packages/openldap2/LICENSE</file>
+        <file>/usr/share/doc/packages/openldap2/README</file>
+        <file>/usr/share/doc/packages/openldap2/README.update</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/autoconf.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_dit.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_local.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_ref.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_repl.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/copyright.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/dbtools.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/index.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/install.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_dctree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_tree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/license.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/preface.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/proxycache.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/quickstart.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/referrals.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/replication.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/runningslapd.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/sasl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/schema.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/security.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconf2.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconfig.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/syncrepl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/tls.html</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/README</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-behera-ldap-password-policy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-chu-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-authmeth-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-bcp64-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-dn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-filter-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-models-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-protocol-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-roadmap-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-strprep-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-syntaxes-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-url-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-user-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-acl-model-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldap-c-api-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-dupent-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-vlv-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-locate-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-joslin-config-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-lachman-laser-ldap-mail-routing-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-bac-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-binary-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-transfer-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-chaining-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-distproc-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-subordinate-scope-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-weltman-ldapv3-proxy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-adlist-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-assert-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-authzid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-cosine-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-dontusecopy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-incr.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-noop-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-readentry-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-t-f-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-turn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-uuid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-x509-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldup-sync-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/install/configure</file>
+        <file>/usr/share/man/man5/slapd-bdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-hdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldap.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldbm.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldif.5.gz</file>
+        <file>/usr/share/man/man5/slapd-monitor.5.gz</file>
+        <file>/usr/share/man/man5/slapd-relay.5.gz</file>
+        <file>/usr/share/man/man5/slapd.access.5.gz</file>
+        <file>/usr/share/man/man5/slapd.conf.5.gz</file>
+        <file>/usr/share/man/man5/slapd.plugin.5.gz</file>
+        <file>/usr/share/man/man5/slapd.replog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-accesslog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-auditlog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-chain.5.gz</file>
+        <file>/usr/share/man/man5/slapo-dynlist.5.gz</file>
+        <file>/usr/share/man/man5/slapo-lastmod.5.gz</file>
+        <file>/usr/share/man/man5/slapo-pcache.5.gz</file>
+        <file>/usr/share/man/man5/slapo-ppolicy.5.gz</file>
+        <file>/usr/share/man/man5/slapo-refint.5.gz</file>
+        <file>/usr/share/man/man5/slapo-retcode.5.gz</file>
+        <file>/usr/share/man/man5/slapo-rwm.5.gz</file>
+        <file>/usr/share/man/man5/slapo-syncprov.5.gz</file>
+        <file>/usr/share/man/man5/slapo-translucent.5.gz</file>
+        <file>/usr/share/man/man5/slapo-unique.5.gz</file>
+        <file>/usr/share/man/man5/slapo-valsort.5.gz</file>
+        <file>/usr/share/man/man8/slapacl.8.gz</file>
+        <file>/usr/share/man/man8/slapadd.8.gz</file>
+        <file>/usr/share/man/man8/slapauth.8.gz</file>
+        <file>/usr/share/man/man8/slapcat.8.gz</file>
+        <file>/usr/share/man/man8/slapd.8.gz</file>
+        <file>/usr/share/man/man8/slapdn.8.gz</file>
+        <file>/usr/share/man/man8/slapindex.8.gz</file>
+        <file>/usr/share/man/man8/slappasswd.8.gz</file>
+        <file>/usr/share/man/man8/slaptest.8.gz</file>
+        <file>/usr/share/man/man8/slurpd.8.gz</file>
+        <file>/usr/share/openldap/ucdata/case.dat</file>
+        <file>/usr/share/openldap/ucdata/cmbcl.dat</file>
+        <file>/usr/share/openldap/ucdata/comp.dat</file>
+        <file>/usr/share/openldap/ucdata/ctype.dat</file>
+        <file>/usr/share/openldap/ucdata/decomp.dat</file>
+        <file>/usr/share/openldap/ucdata/kdecomp.dat</file>
+        <file>/usr/share/openldap/ucdata/num.dat</file>
+        <file>/usr/share/update-messages/en/openldap2.1</file>
+        <file>/var/adm/fillup-templates/sysconfig.openldap</file>
+        <file>/var/lib/ldap/DB_CONFIG</file>
+        <file>/var/lib/ldap/DB_CONFIG.example</file>
+</package>
+
+
+
+
+
+<package pkgid="2727339181872edbaf13c007c607bc11a14d292c" name="openldap2" arch="src">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+        <file>DB_CONFIG</file>
+        <file>README.update</file>
+        <file>addonschema.tar.gz</file>
+        <file>bconfig-errlog.dif</file>
+        <file>ldap_conf.dif</file>
+        <file>ldapi_url.dif</file>
+        <file>libldap-gethostbyname_r.dif</file>
+        <file>libldap-manpages.dif</file>
+        <file>libldap-referral.dif</file>
+        <file>libldap_ads-sasl-gssapi.dif</file>
+        <file>openldap-2.2.24.dif</file>
+        <file>openldap-2.2.24.tar.bz2</file>
+        <file>openldap-2.3.19.dif</file>
+        <file>openldap-2.3.19.tar.bz2</file>
+        <file>openldap-admin-guide.tar.bz2</file>
+        <file>openldap-ntlm.diff</file>
+        <file>openldap-rc.tgz</file>
+        <file>openldap2.spec</file>
+        <file>pie-compile.dif</file>
+        <file>sasl-slapd.conf</file>
+        <file>secpatch.dif</file>
+        <file>slapd-epollerr.dif</file>
+        <file>slapd-readcontrols.dif</file>
+        <file>slapd_conf.dif</file>
+</package>
+
+
+
+
+
+<package pkgid="3bb100100080c39f059b055ff2f8e96135f5e721" name="openldap2" arch="x86_64">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+        <file type="dir">/etc/openldap</file>
+        <file type="dir">/etc/openldap/schema</file>
+        <file type="dir">/usr/lib/openldap</file>
+        <file type="dir">/usr/lib/openldap/modules</file>
+        <file type="dir">/usr/share/doc/packages/openldap2</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/admin-guide</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/drafts</file>
+        <file type="dir">/usr/share/doc/packages/openldap2/install</file>
+        <file type="dir">/usr/share/openldap</file>
+        <file type="dir">/usr/share/openldap/ucdata</file>
+        <file type="dir">/var/lib/ldap</file>
+        <file type="dir">/var/lib/slurpd</file>
+        <file type="dir">/var/run/slapd</file>
+        <file>/etc/init.d/ldap</file>
+        <file>/etc/init.d/slurpd</file>
+        <file>/etc/openldap/schema/README</file>
+        <file>/etc/openldap/schema/corba.schema</file>
+        <file>/etc/openldap/schema/core.ldif</file>
+        <file>/etc/openldap/schema/core.schema</file>
+        <file>/etc/openldap/schema/cosine.schema</file>
+        <file>/etc/openldap/schema/dyngroup.schema</file>
+        <file>/etc/openldap/schema/inetorgperson.schema</file>
+        <file>/etc/openldap/schema/java.schema</file>
+        <file>/etc/openldap/schema/misc.schema</file>
+        <file>/etc/openldap/schema/nis.schema</file>
+        <file>/etc/openldap/schema/openldap.ldif</file>
+        <file>/etc/openldap/schema/openldap.schema</file>
+        <file>/etc/openldap/schema/ppolicy.schema</file>
+        <file>/etc/openldap/schema/rfc2307bis.schema</file>
+        <file>/etc/openldap/schema/yast.schema</file>
+        <file>/etc/openldap/slapd.conf</file>
+        <file>/etc/openldap/slapd.conf.default</file>
+        <file>/usr/lib/openldap/slapd</file>
+        <file>/usr/lib/openldap/slurpd</file>
+        <file>/usr/lib64/sasl2/slapd.conf</file>
+        <file>/usr/sbin/openldap-2.2-slapcat</file>
+        <file>/usr/sbin/rcldap</file>
+        <file>/usr/sbin/rcslurpd</file>
+        <file>/usr/sbin/slapacl</file>
+        <file>/usr/sbin/slapadd</file>
+        <file>/usr/sbin/slapauth</file>
+        <file>/usr/sbin/slapcat</file>
+        <file>/usr/sbin/slapdn</file>
+        <file>/usr/sbin/slapindex</file>
+        <file>/usr/sbin/slappasswd</file>
+        <file>/usr/sbin/slaptest</file>
+        <file>/usr/share/doc/packages/openldap2/ANNOUNCEMENT</file>
+        <file>/usr/share/doc/packages/openldap2/COPYRIGHT</file>
+        <file>/usr/share/doc/packages/openldap2/INSTALL</file>
+        <file>/usr/share/doc/packages/openldap2/LICENSE</file>
+        <file>/usr/share/doc/packages/openldap2/README</file>
+        <file>/usr/share/doc/packages/openldap2/README.update</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/autoconf.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_dit.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_local.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_ref.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/config_repl.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/copyright.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/dbtools.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/index.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/install.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_dctree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/intro_tree.gif</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/license.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/preface.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/proxycache.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/quickstart.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/referrals.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/replication.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/runningslapd.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/sasl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/schema.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/security.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconf2.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/slapdconfig.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/syncrepl.html</file>
+        <file>/usr/share/doc/packages/openldap2/admin-guide/tls.html</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/README</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-behera-ldap-password-policy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-chu-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-authmeth-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-bcp64-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-dn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-filter-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-models-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-protocol-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-roadmap-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-strprep-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-syntaxes-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-url-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapbis-user-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-acl-model-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldap-c-api-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-dupent-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-ldapv3-vlv-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-ietf-ldapext-locate-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-joslin-config-schema-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-lachman-laser-ldap-mail-routing-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-acm-bac-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-admin-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-binary-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-legg-ldap-transfer-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-chaining-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-csn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-distproc-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-sermersheim-ldap-subordinate-scope-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-weltman-ldapv3-proxy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-adlist-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-assert-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-authzid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-cosine-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-dontusecopy-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-incr.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-noop-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-readentry-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-t-f-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-turn-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-uuid-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldap-x509-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/drafts/draft-zeilenga-ldup-sync-xx.txt</file>
+        <file>/usr/share/doc/packages/openldap2/install/configure</file>
+        <file>/usr/share/man/man5/slapd-bdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-hdb.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldap.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldbm.5.gz</file>
+        <file>/usr/share/man/man5/slapd-ldif.5.gz</file>
+        <file>/usr/share/man/man5/slapd-monitor.5.gz</file>
+        <file>/usr/share/man/man5/slapd-relay.5.gz</file>
+        <file>/usr/share/man/man5/slapd.access.5.gz</file>
+        <file>/usr/share/man/man5/slapd.conf.5.gz</file>
+        <file>/usr/share/man/man5/slapd.plugin.5.gz</file>
+        <file>/usr/share/man/man5/slapd.replog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-accesslog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-auditlog.5.gz</file>
+        <file>/usr/share/man/man5/slapo-chain.5.gz</file>
+        <file>/usr/share/man/man5/slapo-dynlist.5.gz</file>
+        <file>/usr/share/man/man5/slapo-lastmod.5.gz</file>
+        <file>/usr/share/man/man5/slapo-pcache.5.gz</file>
+        <file>/usr/share/man/man5/slapo-ppolicy.5.gz</file>
+        <file>/usr/share/man/man5/slapo-refint.5.gz</file>
+        <file>/usr/share/man/man5/slapo-retcode.5.gz</file>
+        <file>/usr/share/man/man5/slapo-rwm.5.gz</file>
+        <file>/usr/share/man/man5/slapo-syncprov.5.gz</file>
+        <file>/usr/share/man/man5/slapo-translucent.5.gz</file>
+        <file>/usr/share/man/man5/slapo-unique.5.gz</file>
+        <file>/usr/share/man/man5/slapo-valsort.5.gz</file>
+        <file>/usr/share/man/man8/slapacl.8.gz</file>
+        <file>/usr/share/man/man8/slapadd.8.gz</file>
+        <file>/usr/share/man/man8/slapauth.8.gz</file>
+        <file>/usr/share/man/man8/slapcat.8.gz</file>
+        <file>/usr/share/man/man8/slapd.8.gz</file>
+        <file>/usr/share/man/man8/slapdn.8.gz</file>
+        <file>/usr/share/man/man8/slapindex.8.gz</file>
+        <file>/usr/share/man/man8/slappasswd.8.gz</file>
+        <file>/usr/share/man/man8/slaptest.8.gz</file>
+        <file>/usr/share/man/man8/slurpd.8.gz</file>
+        <file>/usr/share/openldap/ucdata/case.dat</file>
+        <file>/usr/share/openldap/ucdata/cmbcl.dat</file>
+        <file>/usr/share/openldap/ucdata/comp.dat</file>
+        <file>/usr/share/openldap/ucdata/ctype.dat</file>
+        <file>/usr/share/openldap/ucdata/decomp.dat</file>
+        <file>/usr/share/openldap/ucdata/kdecomp.dat</file>
+        <file>/usr/share/openldap/ucdata/num.dat</file>
+        <file>/usr/share/update-messages/en/openldap2.1</file>
+        <file>/var/adm/fillup-templates/sysconfig.openldap</file>
+        <file>/var/lib/ldap/DB_CONFIG</file>
+        <file>/var/lib/ldap/DB_CONFIG.example</file>
+</package>
+
+
+
+
+<package pkgid="7ba58f2b9498981c5f20d25f9675a6592317b694" name="dhcp" arch="i586">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+
+<package pkgid="739feea694870b250262a846af418e4c3d887ecd" name="dhcp" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+
+<package pkgid="7042e04a0b649bcc0a2100ddde62e8fb1ce82927" name="dhcp" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+<package pkgid="22a69d9e4b792e588b4542659fa4ac329fd9e5a8" name="dhcp" arch="i586">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+<package pkgid="f9e69cf37731c8b7323c34d46d20b444d551c765" name="dhcp-client" arch="i586">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file>/etc/dhclient.conf</file>
+        <file>/sbin/dhclient</file>
+        <file>/sbin/dhclient-script</file>
+        <file>/usr/share/man/man5/dhclient.conf.5.gz</file>
+        <file>/usr/share/man/man5/dhclient.leases.5.gz</file>
+        <file>/usr/share/man/man8/dhclient-script.8.gz</file>
+        <file>/usr/share/man/man8/dhclient.8.gz</file>
+        <file>/var/lib/dhcp/dhclient.leases</file>
+</package>
+
+
+
+<package pkgid="f6ba046b24618a07a8b0cc1477e039a8150c0ab1" name="dhcp" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+<package pkgid="729e9b2704cab26322040c44f0315280c9db0eab" name="dhcp-client" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file>/etc/dhclient.conf</file>
+        <file>/sbin/dhclient</file>
+        <file>/sbin/dhclient-script</file>
+        <file>/usr/share/man/man5/dhclient.conf.5.gz</file>
+        <file>/usr/share/man/man5/dhclient.leases.5.gz</file>
+        <file>/usr/share/man/man8/dhclient-script.8.gz</file>
+        <file>/usr/share/man/man8/dhclient.8.gz</file>
+        <file>/var/lib/dhcp/dhclient.leases</file>
+</package>
+
+
+
+<package pkgid="b9a718ca537188add526485aceba4592a2b84ca7" name="dhcp" arch="src">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file>DDNS-howto.txt</file>
+        <file>clearip</file>
+        <file>dhclient.c.dif</file>
+        <file>dhcp-3.0.1rc14-tmpfile.dif</file>
+        <file>dhcp-3.0.2-extended_option_environment.patch</file>
+        <file>dhcp-3.0.3-dhclient-nis-01-thoenig.patch</file>
+        <file>dhcp-3.0.3-dhclient-script-dhcdbd.patch</file>
+        <file>dhcp-3.0.3-ldap-patch.gz</file>
+        <file>dhcp-3.0.3-man.dif</file>
+        <file>dhcp-3.0.3.tar.bz2</file>
+        <file>dhcp-3.0.3b1-pie.dif</file>
+        <file>dhcp-3.0.3b1.dif</file>
+        <file>dhcp-3.0b2pl18.paranoia.dif</file>
+        <file>dhcp-3.0b2pl24.resolv.conf.dif</file>
+        <file>dhcp-3.0rc10.dif</file>
+        <file>dhcp-3.0rc10.filedes.dif</file>
+        <file>dhcp-send-hostname-rml.patch</file>
+        <file>dhcp.LIESMICH</file>
+        <file>dhcp.README</file>
+        <file>dhcp.README.upgrade</file>
+        <file>dhcp.spec</file>
+        <file>dhcpsync</file>
+        <file>dhcpsync.8</file>
+        <file>dnscompr.py</file>
+        <file>examples.tar.gz</file>
+        <file>leases.awk</file>
+        <file>leasestate</file>
+        <file>listlease</file>
+        <file>makefile.diff</file>
+        <file>rc.dhcpd</file>
+        <file>rc.dhcrelay</file>
+        <file>reset-ip</file>
+        <file>sysconfig.dhcpd</file>
+        <file>sysconfig.dhcrelay</file>
+        <file>sysconfig.syslog-dhcpd</file>
+        <file>test.c.dif</file>
+</package>
+
+
+
+<package pkgid="5b8842037e72ca22fa32df2516962822e4c6a313" name="dhcp" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file type="dir">/usr/share/doc/packages/dhcp</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc</file>
+        <file type="dir">/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP</file>
+        <file type="dir">/var/lib/dhcp</file>
+        <file>/usr/bin/omshell</file>
+        <file>/usr/share/doc/packages/dhcp/LICENSE</file>
+        <file>/usr/share/doc/packages/dhcp/README</file>
+        <file>/usr/share/doc/packages/dhcp/RELNOTES</file>
+        <file>/usr/share/doc/packages/dhcp/doc/IANA-arp-parameters</file>
+        <file>/usr/share/doc/packages/dhcp/doc/api+protocol</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-authentication-14.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-dhcp-dns-12.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-failover-07.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/draft-ietf-dhc-ldap-schema-01.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient-script.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.8</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.conf.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhclient.leases.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-eval.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/ja_JP.eucJP/dhcp-options.5</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc1542.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2131.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2132.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2485.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc2489.txt</file>
+        <file>/usr/share/doc/packages/dhcp/doc/rfc951.txt</file>
+        <file>/usr/share/man/man1/omshell.1.gz</file>
+        <file>/usr/share/man/man5/dhcp-eval.5.gz</file>
+</package>
+
+
+
+<package pkgid="a45eeef08edd16af1b70ca359d1032cf01e28de3" name="dhcp-client" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+        <file>/etc/dhclient.conf</file>
+        <file>/sbin/dhclient</file>
+        <file>/sbin/dhclient-script</file>
+        <file>/usr/share/man/man5/dhclient.conf.5.gz</file>
+        <file>/usr/share/man/man5/dhclient.leases.5.gz</file>
+        <file>/usr/share/man/man8/dhclient-script.8.gz</file>
+        <file>/usr/share/man/man8/dhclient.8.gz</file>
+        <file>/var/lib/dhcp/dhclient.leases</file>
+</package>
+
+
+<package pkgid="1cd7ea460f5e4210df54699831f528287bb918f8" name="libextractor" arch="i586">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+        <file type="dir">/usr/share/doc/packages/libextractor</file>
+        <file>/usr/bin/extract</file>
+        <file>/usr/lib/libextractor.so.1</file>
+        <file>/usr/lib/libextractor.so.1.1.0</file>
+        <file>/usr/share/doc/packages/libextractor/AUTHORS</file>
+        <file>/usr/share/doc/packages/libextractor/COPYING</file>
+        <file>/usr/share/doc/packages/libextractor/NEWS</file>
+        <file>/usr/share/doc/packages/libextractor/README</file>
+        <file>/usr/share/doc/packages/libextractor/TODO</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ga/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ro/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/rw/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/man/man1/extract.1.gz</file>
+</package>
+
+
+<package pkgid="06a9e560a069498df7bbb3a8978b4a6dc9e03164" name="libextractor" arch="ppc">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+        <file type="dir">/usr/share/doc/packages/libextractor</file>
+        <file>/usr/bin/extract</file>
+        <file>/usr/lib/libextractor.so.1</file>
+        <file>/usr/lib/libextractor.so.1.1.0</file>
+        <file>/usr/share/doc/packages/libextractor/AUTHORS</file>
+        <file>/usr/share/doc/packages/libextractor/COPYING</file>
+        <file>/usr/share/doc/packages/libextractor/NEWS</file>
+        <file>/usr/share/doc/packages/libextractor/README</file>
+        <file>/usr/share/doc/packages/libextractor/TODO</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ga/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ro/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/rw/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/man/man1/extract.1.gz</file>
+</package>
+
+
+<package pkgid="e42db911c0eee82d350b06c1fd2de7951a700a28" name="libextractor" arch="src">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+        <file>heap-overflow-asfextractor.patch</file>
+        <file>heap-overflow-qtextractor.patch</file>
+        <file>libextractor-0.5.10.tar.gz</file>
+        <file>libextractor.spec</file>
+</package>
+
+
+<package pkgid="39753714ea4afc56bb957742bc74e31126beb474" name="libextractor" arch="x86_64">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+        <file type="dir">/usr/share/doc/packages/libextractor</file>
+        <file>/usr/bin/extract</file>
+        <file>/usr/lib64/libextractor.so.1</file>
+        <file>/usr/lib64/libextractor.so.1.1.0</file>
+        <file>/usr/share/doc/packages/libextractor/AUTHORS</file>
+        <file>/usr/share/doc/packages/libextractor/COPYING</file>
+        <file>/usr/share/doc/packages/libextractor/NEWS</file>
+        <file>/usr/share/doc/packages/libextractor/README</file>
+        <file>/usr/share/doc/packages/libextractor/TODO</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ga/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/ro/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/locale/rw/LC_MESSAGES/libextractor.mo</file>
+        <file>/usr/share/man/man1/extract.1.gz</file>
+</package>
+
+<package pkgid="a144f4f4e4dd6a949f7b0ca7c79c3bb8a2e56851" name="ivman" arch="i586">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+        <file type="dir">/etc/ivman</file>
+        <file type="dir">/usr/share/doc/packages/ivman</file>
+        <file>/etc/ivman/IvmConfigActions.xml</file>
+        <file>/etc/ivman/IvmConfigBase.xml</file>
+        <file>/etc/ivman/IvmConfigConditions.xml</file>
+        <file>/etc/ivman/IvmConfigProperties.xml</file>
+        <file>/usr/bin/halmount</file>
+        <file>/usr/bin/halmount.py</file>
+        <file>/usr/bin/ivman</file>
+        <file>/usr/bin/ivman-launch</file>
+        <file>/usr/share/doc/packages/ivman/AUTHORS</file>
+        <file>/usr/share/doc/packages/ivman/ChangeLog</file>
+        <file>/usr/share/doc/packages/ivman/README</file>
+        <file>/usr/share/doc/packages/ivman/TODO</file>
+        <file>/usr/share/locale/fr/LC_MESSAGES/ivman.mo</file>
+        <file>/usr/share/man/man5/IvmConfigActions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigBase.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigConditions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigProperties.xml.5.gz</file>
+        <file>/usr/share/man/man8/ivman-launch.8.gz</file>
+        <file>/usr/share/man/man8/ivman.8.gz</file>
+</package>
+
+<package pkgid="140b8a8de66f5f1ba8d8160f2ee2054e6fd7e138" name="ivman" arch="ppc">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+        <file type="dir">/etc/ivman</file>
+        <file type="dir">/usr/share/doc/packages/ivman</file>
+        <file>/etc/ivman/IvmConfigActions.xml</file>
+        <file>/etc/ivman/IvmConfigBase.xml</file>
+        <file>/etc/ivman/IvmConfigConditions.xml</file>
+        <file>/etc/ivman/IvmConfigProperties.xml</file>
+        <file>/usr/bin/halmount</file>
+        <file>/usr/bin/halmount.py</file>
+        <file>/usr/bin/ivman</file>
+        <file>/usr/bin/ivman-launch</file>
+        <file>/usr/share/doc/packages/ivman/AUTHORS</file>
+        <file>/usr/share/doc/packages/ivman/ChangeLog</file>
+        <file>/usr/share/doc/packages/ivman/README</file>
+        <file>/usr/share/doc/packages/ivman/TODO</file>
+        <file>/usr/share/locale/fr/LC_MESSAGES/ivman.mo</file>
+        <file>/usr/share/man/man5/IvmConfigActions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigBase.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigConditions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigProperties.xml.5.gz</file>
+        <file>/usr/share/man/man8/ivman-launch.8.gz</file>
+        <file>/usr/share/man/man8/ivman.8.gz</file>
+</package>
+
+<package pkgid="8210ad822c977350b7682b02e42934f2dee22ab5" name="ivman" arch="src">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+        <file>halmount.py</file>
+        <file>ivman-0.6.9-config.patch</file>
+        <file>ivman-0.6.9-makeuserconfigs.patch</file>
+        <file>ivman-0.6.9.tar.bz2</file>
+        <file>ivman.spec</file>
+</package>
+
+<package pkgid="4430628c022d87831285e2c8b42c26638b182987" name="ivman" arch="x86_64">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+        <file type="dir">/etc/ivman</file>
+        <file type="dir">/usr/share/doc/packages/ivman</file>
+        <file>/etc/ivman/IvmConfigActions.xml</file>
+        <file>/etc/ivman/IvmConfigBase.xml</file>
+        <file>/etc/ivman/IvmConfigConditions.xml</file>
+        <file>/etc/ivman/IvmConfigProperties.xml</file>
+        <file>/usr/bin/halmount</file>
+        <file>/usr/bin/halmount.py</file>
+        <file>/usr/bin/ivman</file>
+        <file>/usr/bin/ivman-launch</file>
+        <file>/usr/share/doc/packages/ivman/AUTHORS</file>
+        <file>/usr/share/doc/packages/ivman/ChangeLog</file>
+        <file>/usr/share/doc/packages/ivman/README</file>
+        <file>/usr/share/doc/packages/ivman/TODO</file>
+        <file>/usr/share/locale/fr/LC_MESSAGES/ivman.mo</file>
+        <file>/usr/share/man/man5/IvmConfigActions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigBase.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigConditions.xml.5.gz</file>
+        <file>/usr/share/man/man5/IvmConfigProperties.xml.5.gz</file>
+        <file>/usr/share/man/man8/ivman-launch.8.gz</file>
+        <file>/usr/share/man/man8/ivman.8.gz</file>
+</package>
+<package pkgid="5f64cb2a850f614871e7ce39e5927f2f16b138c7" name="util-linux-crypto" arch="i586">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+        <file>/sbin/cryptsetup</file>
+        <file>/sbin/cryptsetup.sh</file>
+        <file>/sbin/hashalot</file>
+        <file>/usr/sbin/dmconvert</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/cryptsetup.mo</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/dmconvert.mo</file>
+        <file>/usr/share/man/man1/hashalot.1.gz</file>
+        <file>/usr/share/man/man8/cryptsetup.8.gz</file>
+</package>
+<package pkgid="e63397586ea3e175876cc4dd476e847eea0e0f2e" name="util-linux-crypto" arch="ppc">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+        <file>/sbin/cryptsetup</file>
+        <file>/sbin/cryptsetup.sh</file>
+        <file>/sbin/hashalot</file>
+        <file>/usr/sbin/dmconvert</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/cryptsetup.mo</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/dmconvert.mo</file>
+        <file>/usr/share/man/man1/hashalot.1.gz</file>
+        <file>/usr/share/man/man8/cryptsetup.8.gz</file>
+</package>
+<package pkgid="316f825d4fd1220fda20df3e4018da6e4ebbc076" name="util-linux-crypto" arch="src">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+        <file>cryptsetup-0.1-dmi.exists.patch</file>
+        <file>cryptsetup-0.1-retval.patch</file>
+        <file>cryptsetup-0.1-static.patch</file>
+        <file>cryptsetup-0.1-timeout.patch</file>
+        <file>cryptsetup-0.1.tar.bz2</file>
+        <file>cryptsetup.8</file>
+        <file>cryptsetup.sh</file>
+        <file>dmconvert-0.2-uninitialized.patch</file>
+        <file>dmconvert-0.2.tar.bz2</file>
+        <file>hashalot-0.3.tar.bz2</file>
+        <file>util-linux-crypto.spec</file>
+</package>
+<package pkgid="8b428d265f0998310d65412f56babd53d4bced53" name="util-linux-crypto" arch="x86_64">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+        <file>/sbin/cryptsetup</file>
+        <file>/sbin/cryptsetup.sh</file>
+        <file>/sbin/hashalot</file>
+        <file>/usr/sbin/dmconvert</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/cryptsetup.mo</file>
+        <file>/usr/share/locale/de/LC_MESSAGES/dmconvert.mo</file>
+        <file>/usr/share/man/man1/hashalot.1.gz</file>
+        <file>/usr/share/man/man8/cryptsetup.8.gz</file>
+</package>
+</filelists>
diff --git a/devel/devel.dmacvicar/repodata/other.xml b/devel/devel.dmacvicar/repodata/other.xml
new file mode 100644 (file)
index 0000000..d696a20
--- /dev/null
@@ -0,0 +1,10481 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<otherdata xmlns="http://linux.duke.edu/metadata/other" packages="47">
+<package pkgid="d401b0253012e812a395ddb043f5b0ab02eff59e" name="opera" arch="i586">
+<version epoch="0" ver="8.54" rel="0.1"/>
+<changelog author="- ltinkl@suse.cz" date="1146744000">- fix #168288 - VUL-0: opera code exec via stylesheet (update to 8.54)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1140436800">- security update to 8.52 (#151788)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ro@suse.de" date="1134561600">- really use buildroot</changelog>
+<changelog author="- ltinkl@suse.cz" date="1134475200">- add buildroot + norootforbuild</changelog>
+<changelog author="- ltinkl@suse.cz" date="1132747200">- VUL-0: opera command line url shell command injection (#134905)
+- update to 8.51</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127563200">- fix version number, really 8.50 :)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127304000">- update to 8.05, fixing security issue #117980</changelog>
+<changelog author="- ltinkl@suse.cz" date="1125576000">- go back to the stable version 8.02, register the version
+  using the key kindly provided by Opera</changelog>
+<changelog author="- ltinkl@suse.cz" date="1122984000">- update to 8.10 Preview</changelog>
+<changelog author="- ltinkl@suse.cz" date="1119268800">- update to 8.01, fixing several security issues (#91179 and others)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1116849600">- major update to 8.0, all platforms</changelog>
+<changelog author="- ltinkl@suse.cz" date="1108036800">- don't allow tricking the user into running arbitrary commands
+  (#49218)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1096977600">- fix Netscape plugins (#46010)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1095249600">- x86 and ppc versions switched to dynamically linked against Qt (#43526)
+- fix local file detection vulnerability (#43923)
+- fix address bar spoofing vulnerability (#42279)</changelog>
+<changelog author="- kukuk@suse.de" date="1095163200">- Remove OpenMotif1 and OpenMotif2 plugins [Bug #43311]</changelog>
+<changelog author="- ltinkl@suse.cz" date="1092830400">- updated to 7.54</changelog>
+<changelog author="- ltinkl@suse.cz" date="1090584000">- update to 7.53 (#42279)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1089374400">- update to 7.52</changelog>
+<changelog author="- ltinkl@suse.cz" date="1086696000">- security bugfix update (#41628)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1084449600">- updated to 7.50 (all archs)
+- threw away the now unused patch</changelog>
+<changelog author="- ltinkl@suse.cz" date="1077537600">- updated to 7.23 for all archs
+- updated the language files
+- got rid of the motifwrapper hack</changelog>
+<changelog author="- aj@suse.de" date="1063713600">- Remove broken requires.</changelog>
+<changelog author="- pthomas@suse.de" date="1062072000">- Use the operamotifwrapper plugin from opera 7.20b as this
+  is linked against the current libXm.so.3.</changelog>
+<changelog author="- adrian@suse.de" date="1061035200">- install desktop file from kappfinder</changelog>
+<changelog author="- pthomas@suse.de" date="1060948800">- Update ix86 and ppc to 7.11.
+- Update sparc to 6.12 beta.
+- Add most of the available language files.</changelog>
+<changelog author="- ro@suse.de" date="1037188800">- comment out kde2 stuff</changelog>
+<changelog author="- uli@suse.de" date="1030536000">- update -&gt; 6.03 for i386 &amp; compatibles</changelog>
+<changelog author="- ro@suse.de" date="1029326400">- removed gnome1 support</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- Use %ix86 macro</changelog>
+<changelog author="- sndirsch@suse.de" date="1025784000">- added german help module (Bug #16341)</changelog>
+<changelog author="- stepan@suse.de" date="1023278400">- patch install script to recognize hammer architecture.</changelog>
+<changelog author="- uli@suse.de" date="1022673600">- update -&gt; 6.01 (secfix)</changelog>
+<changelog author="- uli@suse.de" date="1022500800">- added German localization from http://www.blauwal.at/opera
+  (bug #16341)</changelog>
+<changelog author="- uli@suse.de" date="1021464000">- update -&gt; 6.0 final (x86 only)</changelog>
+<changelog author="- uli@suse.de" date="1014379200">- update -&gt; 6.0 (x86 only)</changelog>
+<changelog author="- ro@suse.de" date="1011787200">- build using kde2-compat
+- will need port to kde3 (icon pathes only)</changelog>
+<changelog author="- uli@suse.de" date="1000728000">- initial package, x86/sparc/ppc only</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+
+
+<package pkgid="2b8a73c02d8d6b57096be23eed26d5733d67ae80" name="opera" arch="src">
+<version epoch="0" ver="8.54" rel="0.1"/>
+<changelog author="- ltinkl@suse.cz" date="1146744000">- fix #168288 - VUL-0: opera code exec via stylesheet (update to 8.54)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1140436800">- security update to 8.52 (#151788)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ro@suse.de" date="1134561600">- really use buildroot</changelog>
+<changelog author="- ltinkl@suse.cz" date="1134475200">- add buildroot + norootforbuild</changelog>
+<changelog author="- ltinkl@suse.cz" date="1132747200">- VUL-0: opera command line url shell command injection (#134905)
+- update to 8.51</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127563200">- fix version number, really 8.50 :)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127304000">- update to 8.05, fixing security issue #117980</changelog>
+<changelog author="- ltinkl@suse.cz" date="1125576000">- go back to the stable version 8.02, register the version
+  using the key kindly provided by Opera</changelog>
+<changelog author="- ltinkl@suse.cz" date="1122984000">- update to 8.10 Preview</changelog>
+<changelog author="- ltinkl@suse.cz" date="1119268800">- update to 8.01, fixing several security issues (#91179 and others)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1116849600">- major update to 8.0, all platforms</changelog>
+<changelog author="- ltinkl@suse.cz" date="1108036800">- don't allow tricking the user into running arbitrary commands
+  (#49218)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1096977600">- fix Netscape plugins (#46010)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1095249600">- x86 and ppc versions switched to dynamically linked against Qt (#43526)
+- fix local file detection vulnerability (#43923)
+- fix address bar spoofing vulnerability (#42279)</changelog>
+<changelog author="- kukuk@suse.de" date="1095163200">- Remove OpenMotif1 and OpenMotif2 plugins [Bug #43311]</changelog>
+<changelog author="- ltinkl@suse.cz" date="1092830400">- updated to 7.54</changelog>
+<changelog author="- ltinkl@suse.cz" date="1090584000">- update to 7.53 (#42279)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1089374400">- update to 7.52</changelog>
+<changelog author="- ltinkl@suse.cz" date="1086696000">- security bugfix update (#41628)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1084449600">- updated to 7.50 (all archs)
+- threw away the now unused patch</changelog>
+<changelog author="- ltinkl@suse.cz" date="1077537600">- updated to 7.23 for all archs
+- updated the language files
+- got rid of the motifwrapper hack</changelog>
+<changelog author="- aj@suse.de" date="1063713600">- Remove broken requires.</changelog>
+<changelog author="- pthomas@suse.de" date="1062072000">- Use the operamotifwrapper plugin from opera 7.20b as this
+  is linked against the current libXm.so.3.</changelog>
+<changelog author="- adrian@suse.de" date="1061035200">- install desktop file from kappfinder</changelog>
+<changelog author="- pthomas@suse.de" date="1060948800">- Update ix86 and ppc to 7.11.
+- Update sparc to 6.12 beta.
+- Add most of the available language files.</changelog>
+<changelog author="- ro@suse.de" date="1037188800">- comment out kde2 stuff</changelog>
+<changelog author="- uli@suse.de" date="1030536000">- update -&gt; 6.03 for i386 &amp; compatibles</changelog>
+<changelog author="- ro@suse.de" date="1029326400">- removed gnome1 support</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- Use %ix86 macro</changelog>
+<changelog author="- sndirsch@suse.de" date="1025784000">- added german help module (Bug #16341)</changelog>
+<changelog author="- stepan@suse.de" date="1023278400">- patch install script to recognize hammer architecture.</changelog>
+<changelog author="- uli@suse.de" date="1022673600">- update -&gt; 6.01 (secfix)</changelog>
+<changelog author="- uli@suse.de" date="1022500800">- added German localization from http://www.blauwal.at/opera
+  (bug #16341)</changelog>
+<changelog author="- uli@suse.de" date="1021464000">- update -&gt; 6.0 final (x86 only)</changelog>
+<changelog author="- uli@suse.de" date="1014379200">- update -&gt; 6.0 (x86 only)</changelog>
+<changelog author="- ro@suse.de" date="1011787200">- build using kde2-compat
+- will need port to kde3 (icon pathes only)</changelog>
+<changelog author="- uli@suse.de" date="1000728000">- initial package, x86/sparc/ppc only</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+
+
+<package pkgid="c67d4bea93ba82482aaff57cae548fb0da1e8f9d" name="opera" arch="x86_64">
+<version epoch="0" ver="8.54" rel="0.1"/>
+<changelog author="- ltinkl@suse.cz" date="1146744000">- fix #168288 - VUL-0: opera code exec via stylesheet (update to 8.54)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1140436800">- security update to 8.52 (#151788)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ro@suse.de" date="1134561600">- really use buildroot</changelog>
+<changelog author="- ltinkl@suse.cz" date="1134475200">- add buildroot + norootforbuild</changelog>
+<changelog author="- ltinkl@suse.cz" date="1132747200">- VUL-0: opera command line url shell command injection (#134905)
+- update to 8.51</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127563200">- fix version number, really 8.50 :)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1127304000">- update to 8.05, fixing security issue #117980</changelog>
+<changelog author="- ltinkl@suse.cz" date="1125576000">- go back to the stable version 8.02, register the version
+  using the key kindly provided by Opera</changelog>
+<changelog author="- ltinkl@suse.cz" date="1122984000">- update to 8.10 Preview</changelog>
+<changelog author="- ltinkl@suse.cz" date="1119268800">- update to 8.01, fixing several security issues (#91179 and others)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1116849600">- major update to 8.0, all platforms</changelog>
+<changelog author="- ltinkl@suse.cz" date="1108036800">- don't allow tricking the user into running arbitrary commands
+  (#49218)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1096977600">- fix Netscape plugins (#46010)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1095249600">- x86 and ppc versions switched to dynamically linked against Qt (#43526)
+- fix local file detection vulnerability (#43923)
+- fix address bar spoofing vulnerability (#42279)</changelog>
+<changelog author="- kukuk@suse.de" date="1095163200">- Remove OpenMotif1 and OpenMotif2 plugins [Bug #43311]</changelog>
+<changelog author="- ltinkl@suse.cz" date="1092830400">- updated to 7.54</changelog>
+<changelog author="- ltinkl@suse.cz" date="1090584000">- update to 7.53 (#42279)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1089374400">- update to 7.52</changelog>
+<changelog author="- ltinkl@suse.cz" date="1086696000">- security bugfix update (#41628)</changelog>
+<changelog author="- ltinkl@suse.cz" date="1084449600">- updated to 7.50 (all archs)
+- threw away the now unused patch</changelog>
+<changelog author="- ltinkl@suse.cz" date="1077537600">- updated to 7.23 for all archs
+- updated the language files
+- got rid of the motifwrapper hack</changelog>
+<changelog author="- aj@suse.de" date="1063713600">- Remove broken requires.</changelog>
+<changelog author="- pthomas@suse.de" date="1062072000">- Use the operamotifwrapper plugin from opera 7.20b as this
+  is linked against the current libXm.so.3.</changelog>
+<changelog author="- adrian@suse.de" date="1061035200">- install desktop file from kappfinder</changelog>
+<changelog author="- pthomas@suse.de" date="1060948800">- Update ix86 and ppc to 7.11.
+- Update sparc to 6.12 beta.
+- Add most of the available language files.</changelog>
+<changelog author="- ro@suse.de" date="1037188800">- comment out kde2 stuff</changelog>
+<changelog author="- uli@suse.de" date="1030536000">- update -&gt; 6.03 for i386 &amp; compatibles</changelog>
+<changelog author="- ro@suse.de" date="1029326400">- removed gnome1 support</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- Use %ix86 macro</changelog>
+<changelog author="- sndirsch@suse.de" date="1025784000">- added german help module (Bug #16341)</changelog>
+<changelog author="- stepan@suse.de" date="1023278400">- patch install script to recognize hammer architecture.</changelog>
+<changelog author="- uli@suse.de" date="1022673600">- update -&gt; 6.01 (secfix)</changelog>
+<changelog author="- uli@suse.de" date="1022500800">- added German localization from http://www.blauwal.at/opera
+  (bug #16341)</changelog>
+<changelog author="- uli@suse.de" date="1021464000">- update -&gt; 6.0 final (x86 only)</changelog>
+<changelog author="- uli@suse.de" date="1014379200">- update -&gt; 6.0 (x86 only)</changelog>
+<changelog author="- ro@suse.de" date="1011787200">- build using kde2-compat
+- will need port to kde3 (icon pathes only)</changelog>
+<changelog author="- uli@suse.de" date="1000728000">- initial package, x86/sparc/ppc only</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="47aa34762bfe1b236dc8f97ddbc6c71e1ae1184b" name="pdns" arch="i586">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+<changelog author="- nadvornik@suse.cz" date="1146744000">- fixed crash on malformed packets CVE-2006-2069 [#170542]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- nadvornik@suse.cz" date="1130846400">- updated to 2.9.19</changelog>
+<changelog author="- nadvornik@suse.cz" date="1118145600">- fixed init scripts
+- used patches from http://www.linuxnetworks.de/pdnsldap/index.html</changelog>
+<changelog author="- nadvornik@suse.cz" date="1113998400">- fixed to compile on x86_64 with gcc4</changelog>
+<changelog author="- nadvornik@suse.cz" date="1110542400">- installed html documentation [#71738]</changelog>
+<changelog author="- nadvornik@suse.cz" date="1108555200">- new package</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="57c94864f94bbed8681d3498958189a426de15a9" name="pdns" arch="src">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+<changelog author="- nadvornik@suse.cz" date="1146744000">- fixed crash on malformed packets CVE-2006-2069 [#170542]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- nadvornik@suse.cz" date="1130846400">- updated to 2.9.19</changelog>
+<changelog author="- nadvornik@suse.cz" date="1118145600">- fixed init scripts
+- used patches from http://www.linuxnetworks.de/pdnsldap/index.html</changelog>
+<changelog author="- nadvornik@suse.cz" date="1113998400">- fixed to compile on x86_64 with gcc4</changelog>
+<changelog author="- nadvornik@suse.cz" date="1110542400">- installed html documentation [#71738]</changelog>
+<changelog author="- nadvornik@suse.cz" date="1108555200">- new package</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+
+<package pkgid="cc316a1be4793728b58aa0d4f8d74cc334bf1cd9" name="pdns" arch="x86_64">
+<version epoch="0" ver="2.9.19" rel="13.2"/>
+<changelog author="- nadvornik@suse.cz" date="1146744000">- fixed crash on malformed packets CVE-2006-2069 [#170542]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- nadvornik@suse.cz" date="1130846400">- updated to 2.9.19</changelog>
+<changelog author="- nadvornik@suse.cz" date="1118145600">- fixed init scripts
+- used patches from http://www.linuxnetworks.de/pdnsldap/index.html</changelog>
+<changelog author="- nadvornik@suse.cz" date="1113998400">- fixed to compile on x86_64 with gcc4</changelog>
+<changelog author="- nadvornik@suse.cz" date="1110542400">- installed html documentation [#71738]</changelog>
+<changelog author="- nadvornik@suse.cz" date="1108555200">- new package</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="4d7988178018b1d5af490ac899d1cdfd2419ff52" name="dovecot" arch="i586">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+<changelog author="- mrueckert@suse.de" date="1147780800">- added dovecot-1.0.beta7_directory-traversal.patch:
+  Fix Mailbox names list disclosure with mboxes
+  (#175188, CVE-2006-2414)</changelog>
+<changelog author="- mrueckert@suse.de" date="1139832000">- added dovecot-1.0.beta3_indexfixes.patch
+  * added index sync fixes
+  * added fix for ldap urls
+  * let dovecot not send header for mails we didnt announce with
+  EXISTS
+  * allow empty protocol line so you can use dovecot for pipe only
+- removed dovecot-1.0.beta2-sqlite_signedness.patch
+  applied upstream
+- replaced all occurences of /usr/libexec with %{_libdir}</changelog>
+<changelog author="- mrueckert@suse.de" date="1139400000">- update to version 1.0.beta3
+  * Dotlock code changed to timeout faster in some situations when
+  the lock file is old.
+  + Added support for loading SQL drivers dynamically (see INSTALL file
+  for how to build them)
+  + Keywords are stored to dboxes, and other dbox improvements.
+  + dict-sql could actually work now, making quota-in-sql-database
+  possibly working now (not fully tested)
+  + Added mail storage conversion plugin to convert automatically from
+  one mailbox format to another while user logs in. Doesn't preserve
+  UIDVALIDITY/UIDs though.
+  + Added plugin { .. } section to dovecot.conf for passing parameters
+  to plugins (see dovecot-example.conf).
+  + Added ssl-build-param binary which is used to generate
+  ssl-parameters.dat. Main dovecot binary doesn't anymore link to
+  SSL libraries, and this also makes the process title be clearer
+  about why the process is eating all the CPU.
+  o Fix building without OpenSSL
+  o Fixed memory leak in MySQL driver
+  o Fixes to checkpassword
+  o Broken Content-Length header could have broken mbox opening
+  o Fixed potential hangs after APPEND command
+  o Fixed potential crashes in dovecot-auth and imap/pop3-login
+  o zlib plugin now links with -lz so it could actually work
+  o kqueue fixes by Vaclav Haisman
+- update dovecot-lda with latest fixes to compile against b3</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mrueckert@suse.de" date="1138017600">- update to 1.0.beta2. this is a bugfix release.
+  + Added SQLite support. Patch by Jakob Hirsch.
+  + Added auth_debug_passwords setting. If it's not enabled,
+  hide all password strings from logs.
+  + Added mail_cache_min_mail_count and mbox_min_index_size
+  settings which can be used to make Dovecot do less disk writes
+  in small mailboxes where they don't benefit that much.
+  + Added --build-ssl-parameters parameter to dovecot binary
+- SSL parameters were being regenerated every 10 minutes,
+  although not with all systems.
+- Fixed dovecot-auth crashing at startup. Happened only with some
+  specific compilers.
+- base_dir was supposed to be set world-readable,
+  not world-writable
+- disabled epoll for now.
+  Jakob Hirsch is working on a patch for this.
+- sqlite3 support enabled
+  + dovecot-1.0.beta2-sqlite_signedness.patch
+  fix a signedness warning
+- apply fix for dovecot-lda that allows using default_mail_env
+- includes dovecot-lda changelog</changelog>
+<changelog author="- mrueckert@suse.de" date="1137758400">- added dovecot-1.0.beta1_sslparam-regeneration.patch
+  fix timer for regeneration of ssl params</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- added dovecot-1.0beta1_pie.patch
+- compile with -fstack-protector</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- Update to version package for 1.0.beta1
+  this is a complete rewrite see
+  /usr/share/doc/packages/dovecot/ChangeLog.gz for all changes
+- added dovecot-cvs_inotify.patch
+  fixes a small include for inotify support</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- added LDAP_DEPRECATED to CFLAGS</changelog>
+<changelog author="- mmj@suse.de" date="1117800000">- Compile with -fpie/-pie</changelog>
+<changelog author="- mmj@suse.de" date="1108382400">- Update to 0.99.14 including:
+  o Message address fields are now parsed differently, fixing some
+  issues with spaces. Affects only clients which use FETCH ENVELOPE
+  command.
+  o Message MIME parser was somewhat broken with missing MIME boundaries
+  o mbox: Don't allow X-UID headers in mails to override the UIDs we
+  would otherwise set. Too large values can break some clients and
+  cause other trouble.
+  o passwd-file userdb wasn't working
+  o PAM crashed with 64bit systems
+  o non-SSL inetd startup wasn't working
+  o If UID FETCH notices and skips an expunged message, don't return
+  a NO reply. It's not needed and only makes clients give error
+  messages.</changelog>
+<changelog author="- mmj@suse.de" date="1106481600">- Update to 0.99.13 including:
+  o GNUTLS support hasn't been working for a while, so it's not even
+  tried to be used anymore unless explicitly wanted.
+  o Added CRAM-MD5 authentication mechanism.
+  o Added SMD5 and LDAP-MD5 password schemes and changed MD5
+  scheme to use LDAP-MD5 if the password isn't in MD5crypt format.
+  o Workaround for some POP3 client bugs: if message doesn't
+  contain the &quot;end of headers&quot; empty line, add it automatically.
+  o vpopmail supports now all password schemes, most importantly
+  MD5crypt works now without support from libc's crypt()
+  o SQL and LDAP authentication was broken
+  o SEARCH UNKEYWORD wasn't working</changelog>
+<changelog author="- mmj@suse.de" date="1102420800">- Update to 0.99.12.1 including:
+  o Fix memory leaks in LDAP, MySQL and PGSQL userdb/passdb
+  o Fix hanging when parsing mails that have over 4096 bytes in one
+  line (SMTP servers normally don't allow over 1000 bytes so it
+  shouldn't be much of a problem)
+  o FETCH BODYSTRUCTURE sometimes gave a wrong reply
+  (eg. with FETCH (BODYSTRUCTURE RFC822.SIZE) if it wasn't cached)
+  o Never return more than one INBOX in LIST even if there are such
+  files. They don't work anyway and it just confuses clients.
+  o mbox: Don't allow creating INBOX directory by creating/renaming
+  mailboxes under it. They just wouldn't work.
+  o POP3: Don't return PLAIN in SASL list. We don't support initial SASL
+  responses, so it only breaks with most clients that try to use it.
+  o IMAP and POP3 login processes may have sent each line in two IP
+  packets, one with the data and another with CR+LF. Some clients
+  didn't work because of this.</changelog>
+<changelog author="- kukuk@suse.de" date="1100520000">- Use common-* PAM config files</changelog>
+<changelog author="- mmj@suse.de" date="1098532800">- Switch heimdal-* to kerberos-devel-packages in #nfb as pr. requst
+  of Mr. Carsten Höger</changelog>
+<changelog author="- mmj@suse.de" date="1094299200">- Update to dovecot-0.99.11 which mainly is a bugfix release with:
+  o 127.* and ::1 IP addresses are treated as secured with
+  disable_plaintext_auth = yes
+  o auth_debug setting for extra authentication debugging
+  o Some documentation and error message updates
+  o Create PID file in /var/run/dovecot/master.pid
+  o home setting is now optional in static userdb
+  o Added mail setting to static userdb
+  o After APPENDing to selected mailbox Dovecot didn't always notice the
+  new mail immediately which broke some clients
+  o THREAD and SORT commands crashed with some mails
+  o If APPENDed mail ended with CR character, Dovecot aborted the saving
+  o Output streams sometimes sent data duplicated and lost part of it.
+  This could have caused various strange problems, but looks like in
+  practise it rarely caused real problems.</changelog>
+<changelog author="- mmj@suse.de" date="1093953600">- Don't create unused directories [#44362]</changelog>
+<changelog author="- mmj@suse.de" date="1092052800">- Update to dovecot-0.99.10.9 with the following item since .6:
+  o MySQL compiling got broken in last release
+  o More PostgreSQL reconnection fixing
+  o LDAP support compiles now with Solaris LDAP library
+  o IMAP BODY and BODYSTRUCTURE replies were wrong for MIME parts which
+  didn't contain Content-Type header.
+  o MySQL and PostgreSQL auth didn't reconnect if connection was lost
+  to SQL server
+  o Linking fixes for dovecot-auth with some systems
+  o Last fix for disconnecting client when downloading mail longer than
+  30 seconds actually made it never disconnect client. Now it works
+  properly: disconnect when client hasn't read _any_ data for 30
+  seconds.
+  o Added outlook-pop3-no-nuls workaround to fix Outlook hang in
+  mails with NULs.
+  o Config file lines can now contain quoted strings (&quot;value &quot;)
+  o If client didn't finish downloading a single mail in 30 seconds,
+  Dovecot closed the connection. This was supposed to work so that
+  if client hasn't read data at all in 30 seconds, it's disconnected.
+  o Maildir: LIST now doesn't skip symlinks</changelog>
+<changelog author="- mmj@suse.de" date="1087819200">- Update to dovecot-0.99.10.6 which is a bugfix release</changelog>
+<changelog author="- mmj@suse.de" date="1085832000">- Update to dovecot-0.99.10.5 which main feature is mysql support</changelog>
+<changelog author="- mmj@suse.de" date="1085572800">- Adjust the modules path to a more suitable place, and work the
+  configuration a bit.</changelog>
+<changelog author="- mmj@suse.de" date="1084881600">- Initial package of dovecot-0.99.10.4. Thanks to darix for hints.</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="1c550fddb834bce56275ba603276b27296e03cf7" name="dovecot" arch="ppc">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+<changelog author="- mrueckert@suse.de" date="1147780800">- added dovecot-1.0.beta7_directory-traversal.patch:
+  Fix Mailbox names list disclosure with mboxes
+  (#175188, CVE-2006-2414)</changelog>
+<changelog author="- mrueckert@suse.de" date="1139832000">- added dovecot-1.0.beta3_indexfixes.patch
+  * added index sync fixes
+  * added fix for ldap urls
+  * let dovecot not send header for mails we didnt announce with
+  EXISTS
+  * allow empty protocol line so you can use dovecot for pipe only
+- removed dovecot-1.0.beta2-sqlite_signedness.patch
+  applied upstream
+- replaced all occurences of /usr/libexec with %{_libdir}</changelog>
+<changelog author="- mrueckert@suse.de" date="1139400000">- update to version 1.0.beta3
+  * Dotlock code changed to timeout faster in some situations when
+  the lock file is old.
+  + Added support for loading SQL drivers dynamically (see INSTALL file
+  for how to build them)
+  + Keywords are stored to dboxes, and other dbox improvements.
+  + dict-sql could actually work now, making quota-in-sql-database
+  possibly working now (not fully tested)
+  + Added mail storage conversion plugin to convert automatically from
+  one mailbox format to another while user logs in. Doesn't preserve
+  UIDVALIDITY/UIDs though.
+  + Added plugin { .. } section to dovecot.conf for passing parameters
+  to plugins (see dovecot-example.conf).
+  + Added ssl-build-param binary which is used to generate
+  ssl-parameters.dat. Main dovecot binary doesn't anymore link to
+  SSL libraries, and this also makes the process title be clearer
+  about why the process is eating all the CPU.
+  o Fix building without OpenSSL
+  o Fixed memory leak in MySQL driver
+  o Fixes to checkpassword
+  o Broken Content-Length header could have broken mbox opening
+  o Fixed potential hangs after APPEND command
+  o Fixed potential crashes in dovecot-auth and imap/pop3-login
+  o zlib plugin now links with -lz so it could actually work
+  o kqueue fixes by Vaclav Haisman
+- update dovecot-lda with latest fixes to compile against b3</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mrueckert@suse.de" date="1138017600">- update to 1.0.beta2. this is a bugfix release.
+  + Added SQLite support. Patch by Jakob Hirsch.
+  + Added auth_debug_passwords setting. If it's not enabled,
+  hide all password strings from logs.
+  + Added mail_cache_min_mail_count and mbox_min_index_size
+  settings which can be used to make Dovecot do less disk writes
+  in small mailboxes where they don't benefit that much.
+  + Added --build-ssl-parameters parameter to dovecot binary
+- SSL parameters were being regenerated every 10 minutes,
+  although not with all systems.
+- Fixed dovecot-auth crashing at startup. Happened only with some
+  specific compilers.
+- base_dir was supposed to be set world-readable,
+  not world-writable
+- disabled epoll for now.
+  Jakob Hirsch is working on a patch for this.
+- sqlite3 support enabled
+  + dovecot-1.0.beta2-sqlite_signedness.patch
+  fix a signedness warning
+- apply fix for dovecot-lda that allows using default_mail_env
+- includes dovecot-lda changelog</changelog>
+<changelog author="- mrueckert@suse.de" date="1137758400">- added dovecot-1.0.beta1_sslparam-regeneration.patch
+  fix timer for regeneration of ssl params</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- added dovecot-1.0beta1_pie.patch
+- compile with -fstack-protector</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- Update to version package for 1.0.beta1
+  this is a complete rewrite see
+  /usr/share/doc/packages/dovecot/ChangeLog.gz for all changes
+- added dovecot-cvs_inotify.patch
+  fixes a small include for inotify support</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- added LDAP_DEPRECATED to CFLAGS</changelog>
+<changelog author="- mmj@suse.de" date="1117800000">- Compile with -fpie/-pie</changelog>
+<changelog author="- mmj@suse.de" date="1108382400">- Update to 0.99.14 including:
+  o Message address fields are now parsed differently, fixing some
+  issues with spaces. Affects only clients which use FETCH ENVELOPE
+  command.
+  o Message MIME parser was somewhat broken with missing MIME boundaries
+  o mbox: Don't allow X-UID headers in mails to override the UIDs we
+  would otherwise set. Too large values can break some clients and
+  cause other trouble.
+  o passwd-file userdb wasn't working
+  o PAM crashed with 64bit systems
+  o non-SSL inetd startup wasn't working
+  o If UID FETCH notices and skips an expunged message, don't return
+  a NO reply. It's not needed and only makes clients give error
+  messages.</changelog>
+<changelog author="- mmj@suse.de" date="1106481600">- Update to 0.99.13 including:
+  o GNUTLS support hasn't been working for a while, so it's not even
+  tried to be used anymore unless explicitly wanted.
+  o Added CRAM-MD5 authentication mechanism.
+  o Added SMD5 and LDAP-MD5 password schemes and changed MD5
+  scheme to use LDAP-MD5 if the password isn't in MD5crypt format.
+  o Workaround for some POP3 client bugs: if message doesn't
+  contain the &quot;end of headers&quot; empty line, add it automatically.
+  o vpopmail supports now all password schemes, most importantly
+  MD5crypt works now without support from libc's crypt()
+  o SQL and LDAP authentication was broken
+  o SEARCH UNKEYWORD wasn't working</changelog>
+<changelog author="- mmj@suse.de" date="1102420800">- Update to 0.99.12.1 including:
+  o Fix memory leaks in LDAP, MySQL and PGSQL userdb/passdb
+  o Fix hanging when parsing mails that have over 4096 bytes in one
+  line (SMTP servers normally don't allow over 1000 bytes so it
+  shouldn't be much of a problem)
+  o FETCH BODYSTRUCTURE sometimes gave a wrong reply
+  (eg. with FETCH (BODYSTRUCTURE RFC822.SIZE) if it wasn't cached)
+  o Never return more than one INBOX in LIST even if there are such
+  files. They don't work anyway and it just confuses clients.
+  o mbox: Don't allow creating INBOX directory by creating/renaming
+  mailboxes under it. They just wouldn't work.
+  o POP3: Don't return PLAIN in SASL list. We don't support initial SASL
+  responses, so it only breaks with most clients that try to use it.
+  o IMAP and POP3 login processes may have sent each line in two IP
+  packets, one with the data and another with CR+LF. Some clients
+  didn't work because of this.</changelog>
+<changelog author="- kukuk@suse.de" date="1100520000">- Use common-* PAM config files</changelog>
+<changelog author="- mmj@suse.de" date="1098532800">- Switch heimdal-* to kerberos-devel-packages in #nfb as pr. requst
+  of Mr. Carsten Höger</changelog>
+<changelog author="- mmj@suse.de" date="1094299200">- Update to dovecot-0.99.11 which mainly is a bugfix release with:
+  o 127.* and ::1 IP addresses are treated as secured with
+  disable_plaintext_auth = yes
+  o auth_debug setting for extra authentication debugging
+  o Some documentation and error message updates
+  o Create PID file in /var/run/dovecot/master.pid
+  o home setting is now optional in static userdb
+  o Added mail setting to static userdb
+  o After APPENDing to selected mailbox Dovecot didn't always notice the
+  new mail immediately which broke some clients
+  o THREAD and SORT commands crashed with some mails
+  o If APPENDed mail ended with CR character, Dovecot aborted the saving
+  o Output streams sometimes sent data duplicated and lost part of it.
+  This could have caused various strange problems, but looks like in
+  practise it rarely caused real problems.</changelog>
+<changelog author="- mmj@suse.de" date="1093953600">- Don't create unused directories [#44362]</changelog>
+<changelog author="- mmj@suse.de" date="1092052800">- Update to dovecot-0.99.10.9 with the following item since .6:
+  o MySQL compiling got broken in last release
+  o More PostgreSQL reconnection fixing
+  o LDAP support compiles now with Solaris LDAP library
+  o IMAP BODY and BODYSTRUCTURE replies were wrong for MIME parts which
+  didn't contain Content-Type header.
+  o MySQL and PostgreSQL auth didn't reconnect if connection was lost
+  to SQL server
+  o Linking fixes for dovecot-auth with some systems
+  o Last fix for disconnecting client when downloading mail longer than
+  30 seconds actually made it never disconnect client. Now it works
+  properly: disconnect when client hasn't read _any_ data for 30
+  seconds.
+  o Added outlook-pop3-no-nuls workaround to fix Outlook hang in
+  mails with NULs.
+  o Config file lines can now contain quoted strings (&quot;value &quot;)
+  o If client didn't finish downloading a single mail in 30 seconds,
+  Dovecot closed the connection. This was supposed to work so that
+  if client hasn't read data at all in 30 seconds, it's disconnected.
+  o Maildir: LIST now doesn't skip symlinks</changelog>
+<changelog author="- mmj@suse.de" date="1087819200">- Update to dovecot-0.99.10.6 which is a bugfix release</changelog>
+<changelog author="- mmj@suse.de" date="1085832000">- Update to dovecot-0.99.10.5 which main feature is mysql support</changelog>
+<changelog author="- mmj@suse.de" date="1085572800">- Adjust the modules path to a more suitable place, and work the
+  configuration a bit.</changelog>
+<changelog author="- mmj@suse.de" date="1084881600">- Initial package of dovecot-0.99.10.4. Thanks to darix for hints.</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="a4998429399faa5919990fdbc786a114d7a79d40" name="dovecot" arch="src">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+<changelog author="- mrueckert@suse.de" date="1147780800">- added dovecot-1.0.beta7_directory-traversal.patch:
+  Fix Mailbox names list disclosure with mboxes
+  (#175188, CVE-2006-2414)</changelog>
+<changelog author="- mrueckert@suse.de" date="1139832000">- added dovecot-1.0.beta3_indexfixes.patch
+  * added index sync fixes
+  * added fix for ldap urls
+  * let dovecot not send header for mails we didnt announce with
+  EXISTS
+  * allow empty protocol line so you can use dovecot for pipe only
+- removed dovecot-1.0.beta2-sqlite_signedness.patch
+  applied upstream
+- replaced all occurences of /usr/libexec with %{_libdir}</changelog>
+<changelog author="- mrueckert@suse.de" date="1139400000">- update to version 1.0.beta3
+  * Dotlock code changed to timeout faster in some situations when
+  the lock file is old.
+  + Added support for loading SQL drivers dynamically (see INSTALL file
+  for how to build them)
+  + Keywords are stored to dboxes, and other dbox improvements.
+  + dict-sql could actually work now, making quota-in-sql-database
+  possibly working now (not fully tested)
+  + Added mail storage conversion plugin to convert automatically from
+  one mailbox format to another while user logs in. Doesn't preserve
+  UIDVALIDITY/UIDs though.
+  + Added plugin { .. } section to dovecot.conf for passing parameters
+  to plugins (see dovecot-example.conf).
+  + Added ssl-build-param binary which is used to generate
+  ssl-parameters.dat. Main dovecot binary doesn't anymore link to
+  SSL libraries, and this also makes the process title be clearer
+  about why the process is eating all the CPU.
+  o Fix building without OpenSSL
+  o Fixed memory leak in MySQL driver
+  o Fixes to checkpassword
+  o Broken Content-Length header could have broken mbox opening
+  o Fixed potential hangs after APPEND command
+  o Fixed potential crashes in dovecot-auth and imap/pop3-login
+  o zlib plugin now links with -lz so it could actually work
+  o kqueue fixes by Vaclav Haisman
+- update dovecot-lda with latest fixes to compile against b3</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mrueckert@suse.de" date="1138017600">- update to 1.0.beta2. this is a bugfix release.
+  + Added SQLite support. Patch by Jakob Hirsch.
+  + Added auth_debug_passwords setting. If it's not enabled,
+  hide all password strings from logs.
+  + Added mail_cache_min_mail_count and mbox_min_index_size
+  settings which can be used to make Dovecot do less disk writes
+  in small mailboxes where they don't benefit that much.
+  + Added --build-ssl-parameters parameter to dovecot binary
+- SSL parameters were being regenerated every 10 minutes,
+  although not with all systems.
+- Fixed dovecot-auth crashing at startup. Happened only with some
+  specific compilers.
+- base_dir was supposed to be set world-readable,
+  not world-writable
+- disabled epoll for now.
+  Jakob Hirsch is working on a patch for this.
+- sqlite3 support enabled
+  + dovecot-1.0.beta2-sqlite_signedness.patch
+  fix a signedness warning
+- apply fix for dovecot-lda that allows using default_mail_env
+- includes dovecot-lda changelog</changelog>
+<changelog author="- mrueckert@suse.de" date="1137758400">- added dovecot-1.0.beta1_sslparam-regeneration.patch
+  fix timer for regeneration of ssl params</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- added dovecot-1.0beta1_pie.patch
+- compile with -fstack-protector</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- Update to version package for 1.0.beta1
+  this is a complete rewrite see
+  /usr/share/doc/packages/dovecot/ChangeLog.gz for all changes
+- added dovecot-cvs_inotify.patch
+  fixes a small include for inotify support</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- added LDAP_DEPRECATED to CFLAGS</changelog>
+<changelog author="- mmj@suse.de" date="1117800000">- Compile with -fpie/-pie</changelog>
+<changelog author="- mmj@suse.de" date="1108382400">- Update to 0.99.14 including:
+  o Message address fields are now parsed differently, fixing some
+  issues with spaces. Affects only clients which use FETCH ENVELOPE
+  command.
+  o Message MIME parser was somewhat broken with missing MIME boundaries
+  o mbox: Don't allow X-UID headers in mails to override the UIDs we
+  would otherwise set. Too large values can break some clients and
+  cause other trouble.
+  o passwd-file userdb wasn't working
+  o PAM crashed with 64bit systems
+  o non-SSL inetd startup wasn't working
+  o If UID FETCH notices and skips an expunged message, don't return
+  a NO reply. It's not needed and only makes clients give error
+  messages.</changelog>
+<changelog author="- mmj@suse.de" date="1106481600">- Update to 0.99.13 including:
+  o GNUTLS support hasn't been working for a while, so it's not even
+  tried to be used anymore unless explicitly wanted.
+  o Added CRAM-MD5 authentication mechanism.
+  o Added SMD5 and LDAP-MD5 password schemes and changed MD5
+  scheme to use LDAP-MD5 if the password isn't in MD5crypt format.
+  o Workaround for some POP3 client bugs: if message doesn't
+  contain the &quot;end of headers&quot; empty line, add it automatically.
+  o vpopmail supports now all password schemes, most importantly
+  MD5crypt works now without support from libc's crypt()
+  o SQL and LDAP authentication was broken
+  o SEARCH UNKEYWORD wasn't working</changelog>
+<changelog author="- mmj@suse.de" date="1102420800">- Update to 0.99.12.1 including:
+  o Fix memory leaks in LDAP, MySQL and PGSQL userdb/passdb
+  o Fix hanging when parsing mails that have over 4096 bytes in one
+  line (SMTP servers normally don't allow over 1000 bytes so it
+  shouldn't be much of a problem)
+  o FETCH BODYSTRUCTURE sometimes gave a wrong reply
+  (eg. with FETCH (BODYSTRUCTURE RFC822.SIZE) if it wasn't cached)
+  o Never return more than one INBOX in LIST even if there are such
+  files. They don't work anyway and it just confuses clients.
+  o mbox: Don't allow creating INBOX directory by creating/renaming
+  mailboxes under it. They just wouldn't work.
+  o POP3: Don't return PLAIN in SASL list. We don't support initial SASL
+  responses, so it only breaks with most clients that try to use it.
+  o IMAP and POP3 login processes may have sent each line in two IP
+  packets, one with the data and another with CR+LF. Some clients
+  didn't work because of this.</changelog>
+<changelog author="- kukuk@suse.de" date="1100520000">- Use common-* PAM config files</changelog>
+<changelog author="- mmj@suse.de" date="1098532800">- Switch heimdal-* to kerberos-devel-packages in #nfb as pr. requst
+  of Mr. Carsten Höger</changelog>
+<changelog author="- mmj@suse.de" date="1094299200">- Update to dovecot-0.99.11 which mainly is a bugfix release with:
+  o 127.* and ::1 IP addresses are treated as secured with
+  disable_plaintext_auth = yes
+  o auth_debug setting for extra authentication debugging
+  o Some documentation and error message updates
+  o Create PID file in /var/run/dovecot/master.pid
+  o home setting is now optional in static userdb
+  o Added mail setting to static userdb
+  o After APPENDing to selected mailbox Dovecot didn't always notice the
+  new mail immediately which broke some clients
+  o THREAD and SORT commands crashed with some mails
+  o If APPENDed mail ended with CR character, Dovecot aborted the saving
+  o Output streams sometimes sent data duplicated and lost part of it.
+  This could have caused various strange problems, but looks like in
+  practise it rarely caused real problems.</changelog>
+<changelog author="- mmj@suse.de" date="1093953600">- Don't create unused directories [#44362]</changelog>
+<changelog author="- mmj@suse.de" date="1092052800">- Update to dovecot-0.99.10.9 with the following item since .6:
+  o MySQL compiling got broken in last release
+  o More PostgreSQL reconnection fixing
+  o LDAP support compiles now with Solaris LDAP library
+  o IMAP BODY and BODYSTRUCTURE replies were wrong for MIME parts which
+  didn't contain Content-Type header.
+  o MySQL and PostgreSQL auth didn't reconnect if connection was lost
+  to SQL server
+  o Linking fixes for dovecot-auth with some systems
+  o Last fix for disconnecting client when downloading mail longer than
+  30 seconds actually made it never disconnect client. Now it works
+  properly: disconnect when client hasn't read _any_ data for 30
+  seconds.
+  o Added outlook-pop3-no-nuls workaround to fix Outlook hang in
+  mails with NULs.
+  o Config file lines can now contain quoted strings (&quot;value &quot;)
+  o If client didn't finish downloading a single mail in 30 seconds,
+  Dovecot closed the connection. This was supposed to work so that
+  if client hasn't read data at all in 30 seconds, it's disconnected.
+  o Maildir: LIST now doesn't skip symlinks</changelog>
+<changelog author="- mmj@suse.de" date="1087819200">- Update to dovecot-0.99.10.6 which is a bugfix release</changelog>
+<changelog author="- mmj@suse.de" date="1085832000">- Update to dovecot-0.99.10.5 which main feature is mysql support</changelog>
+<changelog author="- mmj@suse.de" date="1085572800">- Adjust the modules path to a more suitable place, and work the
+  configuration a bit.</changelog>
+<changelog author="- mmj@suse.de" date="1084881600">- Initial package of dovecot-0.99.10.4. Thanks to darix for hints.</changelog>
+</package>
+
+
+
+
+
+
+
+
+
+<package pkgid="57cd740de9f629fae4c0f6b665634ccfd40845fc" name="dovecot" arch="x86_64">
+<version epoch="0" ver="1.0.beta3" rel="13.2"/>
+<changelog author="- mrueckert@suse.de" date="1147780800">- added dovecot-1.0.beta7_directory-traversal.patch:
+  Fix Mailbox names list disclosure with mboxes
+  (#175188, CVE-2006-2414)</changelog>
+<changelog author="- mrueckert@suse.de" date="1139832000">- added dovecot-1.0.beta3_indexfixes.patch
+  * added index sync fixes
+  * added fix for ldap urls
+  * let dovecot not send header for mails we didnt announce with
+  EXISTS
+  * allow empty protocol line so you can use dovecot for pipe only
+- removed dovecot-1.0.beta2-sqlite_signedness.patch
+  applied upstream
+- replaced all occurences of /usr/libexec with %{_libdir}</changelog>
+<changelog author="- mrueckert@suse.de" date="1139400000">- update to version 1.0.beta3
+  * Dotlock code changed to timeout faster in some situations when
+  the lock file is old.
+  + Added support for loading SQL drivers dynamically (see INSTALL file
+  for how to build them)
+  + Keywords are stored to dboxes, and other dbox improvements.
+  + dict-sql could actually work now, making quota-in-sql-database
+  possibly working now (not fully tested)
+  + Added mail storage conversion plugin to convert automatically from
+  one mailbox format to another while user logs in. Doesn't preserve
+  UIDVALIDITY/UIDs though.
+  + Added plugin { .. } section to dovecot.conf for passing parameters
+  to plugins (see dovecot-example.conf).
+  + Added ssl-build-param binary which is used to generate
+  ssl-parameters.dat. Main dovecot binary doesn't anymore link to
+  SSL libraries, and this also makes the process title be clearer
+  about why the process is eating all the CPU.
+  o Fix building without OpenSSL
+  o Fixed memory leak in MySQL driver
+  o Fixes to checkpassword
+  o Broken Content-Length header could have broken mbox opening
+  o Fixed potential hangs after APPEND command
+  o Fixed potential crashes in dovecot-auth and imap/pop3-login
+  o zlib plugin now links with -lz so it could actually work
+  o kqueue fixes by Vaclav Haisman
+- update dovecot-lda with latest fixes to compile against b3</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mrueckert@suse.de" date="1138017600">- update to 1.0.beta2. this is a bugfix release.
+  + Added SQLite support. Patch by Jakob Hirsch.
+  + Added auth_debug_passwords setting. If it's not enabled,
+  hide all password strings from logs.
+  + Added mail_cache_min_mail_count and mbox_min_index_size
+  settings which can be used to make Dovecot do less disk writes
+  in small mailboxes where they don't benefit that much.
+  + Added --build-ssl-parameters parameter to dovecot binary
+- SSL parameters were being regenerated every 10 minutes,
+  although not with all systems.
+- Fixed dovecot-auth crashing at startup. Happened only with some
+  specific compilers.
+- base_dir was supposed to be set world-readable,
+  not world-writable
+- disabled epoll for now.
+  Jakob Hirsch is working on a patch for this.
+- sqlite3 support enabled
+  + dovecot-1.0.beta2-sqlite_signedness.patch
+  fix a signedness warning
+- apply fix for dovecot-lda that allows using default_mail_env
+- includes dovecot-lda changelog</changelog>
+<changelog author="- mrueckert@suse.de" date="1137758400">- added dovecot-1.0.beta1_sslparam-regeneration.patch
+  fix timer for regeneration of ssl params</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- added dovecot-1.0beta1_pie.patch
+- compile with -fstack-protector</changelog>
+<changelog author="- mrueckert@suse.de" date="1137499200">- Update to version package for 1.0.beta1
+  this is a complete rewrite see
+  /usr/share/doc/packages/dovecot/ChangeLog.gz for all changes
+- added dovecot-cvs_inotify.patch
+  fixes a small include for inotify support</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- added LDAP_DEPRECATED to CFLAGS</changelog>
+<changelog author="- mmj@suse.de" date="1117800000">- Compile with -fpie/-pie</changelog>
+<changelog author="- mmj@suse.de" date="1108382400">- Update to 0.99.14 including:
+  o Message address fields are now parsed differently, fixing some
+  issues with spaces. Affects only clients which use FETCH ENVELOPE
+  command.
+  o Message MIME parser was somewhat broken with missing MIME boundaries
+  o mbox: Don't allow X-UID headers in mails to override the UIDs we
+  would otherwise set. Too large values can break some clients and
+  cause other trouble.
+  o passwd-file userdb wasn't working
+  o PAM crashed with 64bit systems
+  o non-SSL inetd startup wasn't working
+  o If UID FETCH notices and skips an expunged message, don't return
+  a NO reply. It's not needed and only makes clients give error
+  messages.</changelog>
+<changelog author="- mmj@suse.de" date="1106481600">- Update to 0.99.13 including:
+  o GNUTLS support hasn't been working for a while, so it's not even
+  tried to be used anymore unless explicitly wanted.
+  o Added CRAM-MD5 authentication mechanism.
+  o Added SMD5 and LDAP-MD5 password schemes and changed MD5
+  scheme to use LDAP-MD5 if the password isn't in MD5crypt format.
+  o Workaround for some POP3 client bugs: if message doesn't
+  contain the &quot;end of headers&quot; empty line, add it automatically.
+  o vpopmail supports now all password schemes, most importantly
+  MD5crypt works now without support from libc's crypt()
+  o SQL and LDAP authentication was broken
+  o SEARCH UNKEYWORD wasn't working</changelog>
+<changelog author="- mmj@suse.de" date="1102420800">- Update to 0.99.12.1 including:
+  o Fix memory leaks in LDAP, MySQL and PGSQL userdb/passdb
+  o Fix hanging when parsing mails that have over 4096 bytes in one
+  line (SMTP servers normally don't allow over 1000 bytes so it
+  shouldn't be much of a problem)
+  o FETCH BODYSTRUCTURE sometimes gave a wrong reply
+  (eg. with FETCH (BODYSTRUCTURE RFC822.SIZE) if it wasn't cached)
+  o Never return more than one INBOX in LIST even if there are such
+  files. They don't work anyway and it just confuses clients.
+  o mbox: Don't allow creating INBOX directory by creating/renaming
+  mailboxes under it. They just wouldn't work.
+  o POP3: Don't return PLAIN in SASL list. We don't support initial SASL
+  responses, so it only breaks with most clients that try to use it.
+  o IMAP and POP3 login processes may have sent each line in two IP
+  packets, one with the data and another with CR+LF. Some clients
+  didn't work because of this.</changelog>
+<changelog author="- kukuk@suse.de" date="1100520000">- Use common-* PAM config files</changelog>
+<changelog author="- mmj@suse.de" date="1098532800">- Switch heimdal-* to kerberos-devel-packages in #nfb as pr. requst
+  of Mr. Carsten Höger</changelog>
+<changelog author="- mmj@suse.de" date="1094299200">- Update to dovecot-0.99.11 which mainly is a bugfix release with:
+  o 127.* and ::1 IP addresses are treated as secured with
+  disable_plaintext_auth = yes
+  o auth_debug setting for extra authentication debugging
+  o Some documentation and error message updates
+  o Create PID file in /var/run/dovecot/master.pid
+  o home setting is now optional in static userdb
+  o Added mail setting to static userdb
+  o After APPENDing to selected mailbox Dovecot didn't always notice the
+  new mail immediately which broke some clients
+  o THREAD and SORT commands crashed with some mails
+  o If APPENDed mail ended with CR character, Dovecot aborted the saving
+  o Output streams sometimes sent data duplicated and lost part of it.
+  This could have caused various strange problems, but looks like in
+  practise it rarely caused real problems.</changelog>
+<changelog author="- mmj@suse.de" date="1093953600">- Don't create unused directories [#44362]</changelog>
+<changelog author="- mmj@suse.de" date="1092052800">- Update to dovecot-0.99.10.9 with the following item since .6:
+  o MySQL compiling got broken in last release
+  o More PostgreSQL reconnection fixing
+  o LDAP support compiles now with Solaris LDAP library
+  o IMAP BODY and BODYSTRUCTURE replies were wrong for MIME parts which
+  didn't contain Content-Type header.
+  o MySQL and PostgreSQL auth didn't reconnect if connection was lost
+  to SQL server
+  o Linking fixes for dovecot-auth with some systems
+  o Last fix for disconnecting client when downloading mail longer than
+  30 seconds actually made it never disconnect client. Now it works
+  properly: disconnect when client hasn't read _any_ data for 30
+  seconds.
+  o Added outlook-pop3-no-nuls workaround to fix Outlook hang in
+  mails with NULs.
+  o Config file lines can now contain quoted strings (&quot;value &quot;)
+  o If client didn't finish downloading a single mail in 30 seconds,
+  Dovecot closed the connection. This was supposed to work so that
+  if client hasn't read data at all in 30 seconds, it's disconnected.
+  o Maildir: LIST now doesn't skip symlinks</changelog>
+<changelog author="- mmj@suse.de" date="1087819200">- Update to dovecot-0.99.10.6 which is a bugfix release</changelog>
+<changelog author="- mmj@suse.de" date="1085832000">- Update to dovecot-0.99.10.5 which main feature is mysql support</changelog>
+<changelog author="- mmj@suse.de" date="1085572800">- Adjust the modules path to a more suitable place, and work the
+  configuration a bit.</changelog>
+<changelog author="- mmj@suse.de" date="1084881600">- Initial package of dovecot-0.99.10.4. Thanks to darix for hints.</changelog>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="0591487b293027292fb55d3fd3402e5dd2cb4184" name="avahi" arch="i586">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+<changelog author="- sbrabec@suse.cz" date="1147780800">- Fixed invalid UTF-8 hostname DoS (#137781).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1147176000">- Fixed buffer overflow in avahi_record_to_string() (#137781#c7).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1145880000">- Replaced Obsoletes by Conflicts for mDNSResponder* (#149676).</changelog>
+<changelog author="- jpr@suse.de" date="1145448000">- Fix compat layer ABI inconsistencies with mDNSResponder (part of
+  [#149676])</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144238400">- Wait for daemon initialization in the init script (#150902).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144152000">- Use Provides and Obsoletes instead of Conflicts to allow seamless
+  replacement.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1142856000">- Call %insserv_cleanup in %postun.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Provide libdns_sd.so RPM symbol, as mDNSResponder does.
+- Reduced BuildRequires.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Fixed circular dependency between avahi and avahi-glib
+  (avahi-discover moved to avahi-glib).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- sbrabec@suse.cz" date="1138017600">- Updated to version 0.6.5.
+- Enable howl compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1137412800">- Updated to version 0.6.4.</changelog>
+<changelog author="- ro@suse.de" date="1135080000">- fix build with dbus-1 0.60
+- added libxml2-python to nfb</changelog>
+<changelog author="- rml@suse.de" date="1134734400">- Move libdns_sd.so to avahi-compat-mDNSResponder from
+  avahi-compat-mDNSResponder-devel (fix up requires/provides)</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Provide dns_sd.h compatibility symlink.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Enable mDNSResponder compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Call aclocal to compile in PLUS.
+- Enabled qt4 bindings.
+- Fixed file ownership.
+- Disabled parallel build - mono fails often.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133438400">- Bi-arch fix.
+- Build as user.
+- Enabled parallel build.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133352000">- Updated to version 0.6.1.
+- Moved all devel files and Requires to devel subpackage.</changelog>
+<changelog author="- ro@suse.de" date="1129118400">- use gnome2-devel-packages in nfb and cleanup nfb
+- fix build of sharp part on x86_64</changelog>
+<changelog author="- ro@suse.de" date="1128945600">- removed restart_on_update in postinstall
+  (only needed in postuninstall)</changelog>
+<changelog author="- jpr@suse.de" date="1128600000">- Initial check in</changelog>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="4af6068aa9c160924eba7014a5885c2767b51fb0" name="avahi" arch="ppc">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+<changelog author="- sbrabec@suse.cz" date="1147780800">- Fixed invalid UTF-8 hostname DoS (#137781).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1147176000">- Fixed buffer overflow in avahi_record_to_string() (#137781#c7).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1145880000">- Replaced Obsoletes by Conflicts for mDNSResponder* (#149676).</changelog>
+<changelog author="- jpr@suse.de" date="1145448000">- Fix compat layer ABI inconsistencies with mDNSResponder (part of
+  [#149676])</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144238400">- Wait for daemon initialization in the init script (#150902).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144152000">- Use Provides and Obsoletes instead of Conflicts to allow seamless
+  replacement.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1142856000">- Call %insserv_cleanup in %postun.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Provide libdns_sd.so RPM symbol, as mDNSResponder does.
+- Reduced BuildRequires.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Fixed circular dependency between avahi and avahi-glib
+  (avahi-discover moved to avahi-glib).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- sbrabec@suse.cz" date="1138017600">- Updated to version 0.6.5.
+- Enable howl compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1137412800">- Updated to version 0.6.4.</changelog>
+<changelog author="- ro@suse.de" date="1135080000">- fix build with dbus-1 0.60
+- added libxml2-python to nfb</changelog>
+<changelog author="- rml@suse.de" date="1134734400">- Move libdns_sd.so to avahi-compat-mDNSResponder from
+  avahi-compat-mDNSResponder-devel (fix up requires/provides)</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Provide dns_sd.h compatibility symlink.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Enable mDNSResponder compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Call aclocal to compile in PLUS.
+- Enabled qt4 bindings.
+- Fixed file ownership.
+- Disabled parallel build - mono fails often.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133438400">- Bi-arch fix.
+- Build as user.
+- Enabled parallel build.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133352000">- Updated to version 0.6.1.
+- Moved all devel files and Requires to devel subpackage.</changelog>
+<changelog author="- ro@suse.de" date="1129118400">- use gnome2-devel-packages in nfb and cleanup nfb
+- fix build of sharp part on x86_64</changelog>
+<changelog author="- ro@suse.de" date="1128945600">- removed restart_on_update in postinstall
+  (only needed in postuninstall)</changelog>
+<changelog author="- jpr@suse.de" date="1128600000">- Initial check in</changelog>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="404c792eaaef7fec29a175f2d06fd73de7b727fa" name="avahi" arch="src">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+<changelog author="- sbrabec@suse.cz" date="1147780800">- Fixed invalid UTF-8 hostname DoS (#137781).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1147176000">- Fixed buffer overflow in avahi_record_to_string() (#137781#c7).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1145880000">- Replaced Obsoletes by Conflicts for mDNSResponder* (#149676).</changelog>
+<changelog author="- jpr@suse.de" date="1145448000">- Fix compat layer ABI inconsistencies with mDNSResponder (part of
+  [#149676])</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144238400">- Wait for daemon initialization in the init script (#150902).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144152000">- Use Provides and Obsoletes instead of Conflicts to allow seamless
+  replacement.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1142856000">- Call %insserv_cleanup in %postun.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Provide libdns_sd.so RPM symbol, as mDNSResponder does.
+- Reduced BuildRequires.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Fixed circular dependency between avahi and avahi-glib
+  (avahi-discover moved to avahi-glib).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- sbrabec@suse.cz" date="1138017600">- Updated to version 0.6.5.
+- Enable howl compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1137412800">- Updated to version 0.6.4.</changelog>
+<changelog author="- ro@suse.de" date="1135080000">- fix build with dbus-1 0.60
+- added libxml2-python to nfb</changelog>
+<changelog author="- rml@suse.de" date="1134734400">- Move libdns_sd.so to avahi-compat-mDNSResponder from
+  avahi-compat-mDNSResponder-devel (fix up requires/provides)</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Provide dns_sd.h compatibility symlink.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Enable mDNSResponder compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Call aclocal to compile in PLUS.
+- Enabled qt4 bindings.
+- Fixed file ownership.
+- Disabled parallel build - mono fails often.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133438400">- Bi-arch fix.
+- Build as user.
+- Enabled parallel build.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133352000">- Updated to version 0.6.1.
+- Moved all devel files and Requires to devel subpackage.</changelog>
+<changelog author="- ro@suse.de" date="1129118400">- use gnome2-devel-packages in nfb and cleanup nfb
+- fix build of sharp part on x86_64</changelog>
+<changelog author="- ro@suse.de" date="1128945600">- removed restart_on_update in postinstall
+  (only needed in postuninstall)</changelog>
+<changelog author="- jpr@suse.de" date="1128600000">- Initial check in</changelog>
+</package>
+
+
+
+
+
+
+
+
+<package pkgid="a46d02c6fcf43387c9424c35933f9c0476d35a4b" name="avahi" arch="x86_64">
+<version epoch="0" ver="0.6.5" rel="29.3"/>
+<changelog author="- sbrabec@suse.cz" date="1147780800">- Fixed invalid UTF-8 hostname DoS (#137781).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1147176000">- Fixed buffer overflow in avahi_record_to_string() (#137781#c7).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1145880000">- Replaced Obsoletes by Conflicts for mDNSResponder* (#149676).</changelog>
+<changelog author="- jpr@suse.de" date="1145448000">- Fix compat layer ABI inconsistencies with mDNSResponder (part of
+  [#149676])</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144238400">- Wait for daemon initialization in the init script (#150902).</changelog>
+<changelog author="- sbrabec@suse.cz" date="1144152000">- Use Provides and Obsoletes instead of Conflicts to allow seamless
+  replacement.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1142856000">- Call %insserv_cleanup in %postun.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Provide libdns_sd.so RPM symbol, as mDNSResponder does.
+- Reduced BuildRequires.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1139486400">- Fixed circular dependency between avahi and avahi-glib
+  (avahi-discover moved to avahi-glib).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- sbrabec@suse.cz" date="1138017600">- Updated to version 0.6.5.
+- Enable howl compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1137412800">- Updated to version 0.6.4.</changelog>
+<changelog author="- ro@suse.de" date="1135080000">- fix build with dbus-1 0.60
+- added libxml2-python to nfb</changelog>
+<changelog author="- rml@suse.de" date="1134734400">- Move libdns_sd.so to avahi-compat-mDNSResponder from
+  avahi-compat-mDNSResponder-devel (fix up requires/provides)</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Provide dns_sd.h compatibility symlink.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Enable mDNSResponder compatibility layer as separate packages.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133524800">- Call aclocal to compile in PLUS.
+- Enabled qt4 bindings.
+- Fixed file ownership.
+- Disabled parallel build - mono fails often.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133438400">- Bi-arch fix.
+- Build as user.
+- Enabled parallel build.</changelog>
+<changelog author="- sbrabec@suse.cz" date="1133352000">- Updated to version 0.6.1.
+- Moved all devel files and Requires to devel subpackage.</changelog>
+<changelog author="- ro@suse.de" date="1129118400">- use gnome2-devel-packages in nfb and cleanup nfb
+- fix build of sharp part on x86_64</changelog>
+<changelog author="- ro@suse.de" date="1128945600">- removed restart_on_update in postinstall
+  (only needed in postuninstall)</changelog>
+<changelog author="- jpr@suse.de" date="1128600000">- Initial check in</changelog>
+</package>
+
+
+
+
+
+
+
+<package pkgid="52f27233cfb8fc172c9660f1c6b5dadebdede30e" name="nagios-www" arch="i586">
+<version epoch="0" ver="1.3" rel="14.1"/>
+<changelog author="- stark@suse.de" date="1146657600">- fixed possible buffer overflow in CGI scripts (#140494)
+  (CVE-2006-2162)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- stark@suse.de" date="1136808000">- fixed off-by-one overflow (#141124)</changelog>
+<changelog author="- stark@suse.de" date="1132315200">- update to stable version 1.3</changelog>
+<changelog author="- stark@suse.de" date="1128600000">- fixed autobuild warnings about printf format</changelog>
+<changelog author="- stark@suse.de" date="1116072000">- fixed compiler errors about usage of uninitialized vars</changelog>
+<changelog author="- aj@suse.de" date="1114344000">- Compile with GCC4.</changelog>
+<changelog author="- ro@suse.de" date="1093867200">- removed apache1 traces</changelog>
+<changelog author="- stark@suse.de" date="1083585600">- fix compiler-warnings</changelog>
+<changelog author="- stark@suse.de" date="1075809600">- update to nagios 1.2</changelog>
+<changelog author="- stark@suse.de" date="1063886400">- /usr/lib/nagios and /usr/lib/nagios/plugins are owned
+  by root (#31100)</changelog>
+<changelog author="- stark@suse.de" date="1063022400">- add apache2 config-files, require http_daemon
+  and need apache2-devel-packages for build (#30157)</changelog>
+<changelog author="- ro@suse.de" date="1061985600">- remove traceroute-lbl from neededforbuild
+  traceroute from net-tools is used anyway</changelog>
+<changelog author="- stark@suse.de" date="1061208000">- added activation meta-data to sysconfig (#28830)</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- change cmdgrp from nogroup to www (the group apache now runs
+  under) [#21782]
+- implement try-restart correctly</changelog>
+<changelog author="- stark@suse.de" date="1059652800">- use stop/restart macros</changelog>
+<changelog author="- aj@suse.de" date="1059480000">- Fix chown invocation.</changelog>
+<changelog author="- stark@suse.de" date="1059134400">- added bugfixes</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Make sure plugin directory exist</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Add nagios libdir to filelist</changelog>
+<changelog author="- stark@suse.de" date="1054728000">- update to bugfix release 1.1
+  * File descriptor leak in file-based performance routines
+  * Timestamp fix for PostgreSQL 7.3
+  * Minor bug fixes to cmd and history CGIs
+  * Minor documentation fixes</changelog>
+<changelog author="- stark@suse.de" date="1042459200">- added sysconfig-metadata (#22607)</changelog>
+<changelog author="- stark@suse.de" date="1038830400">- adopted sapmoni.dif for gcc 3.3</changelog>
+<changelog author="- stark@suse.de" date="1038225600">- update to 1.0 final
+  * Minor bug fixes in CGIs
+  * Minor doc updates
+  * Bug fix for flex host downtime
+  * Embedded perl bug fixe for arguments with spaces
+  * Host escalation logic fix</changelog>
+<changelog author="- stark@suse.de" date="1034683200">- running pre-flight configuration check always when
+  starting nagios (#20916)
+- added logrotate definitions for logfiles
+- fixed embedded perl (#20916) (thanks to mls)</changelog>
+<changelog author="- stark@suse.de" date="1032782400">- update to 1.0b6
+  * Added wildcards and multiple hosts/hostgroups to host dependency
+  and service dependency definitions
+  * Added wildcard support for members directive in hostgroup
+  definitions
+  * Minor bug fixes and cleanups in CGIs
+  * Minor cleanups in the core
+  * New version of snprintf() included that supports %f
+  * Notification interval for escalations now defaults to
+  non-escalated value</changelog>
+<changelog author="- stark@suse.de" date="1030104000">- update to 1.0b5
+  * Fixed memory leak related to processing of passive checks
+  * Memory leak fixes in the CGIs
+  * Minor interface changes to the status and TAC CGIs
+  * Bug fix in the notification CGI
+  * Added minimalistic sample object config file (minimal.cfg)</changelog>
+<changelog author="- stark@suse.de" date="1029499200">- added PreReq (Bug #17938 #17939)</changelog>
+<changelog author="- stark@suse.de" date="1029153600">- updated sapmoni patch to provide documentation</changelog>
+<changelog author="- mls@suse.de" date="1029153600">- made it work with threaded perl-5.8</changelog>
+<changelog author="- stark@suse.de" date="1028808000">- added patch to be able to use nagios-plugins-sap</changelog>
+<changelog author="- adrian@suse.de" date="1027771200">- fix neededforbuild</changelog>
+<changelog author="- stark@suse.de" date="1025870400">- update to 1.0b4
+  * Address field in host definitions is now optional in
+  template-based config format. Defaults to host name if not
+  specified.
+  * Fixed memory leak when processing passive checks
+  * Added illegal_macro_output_chars variable to main config file
+  to allow for stripping dangerous shell characters from plugin
+  output</changelog>
+<changelog author="- stark@suse.de" date="1023883200">- removed -devel subpackage and moved the file to main-package</changelog>
+<changelog author="- stark@suse.de" date="1023710400">- update to 1.0b3
+  * Bug fixes in template-based object code
+  * Bug fix in tac CGI for counting host and service states
+  properly
+  * Default is now to check for external commands as often as
+  possible
+  * Ping syntax for WAP interface moved to CGI config file
+  * Command file user/group defaults to Nagios user/group unless
+  overridden with configure script options
+  * Sample template-based object config file fixes</changelog>
+<changelog author="- stark@suse.de" date="1022587200">- update to 1.0b2
+  * Fixed bug with escaping strings for DB support
+  (MySQL &amp; PostgreSQL)
+  * Several bug fixes in template-based object code
+  * Multiple hostgroups supported in host and service escalations
+  * Wildcard support in service, service escalation, and host
+  escalation definitions</changelog>
+<changelog author="- stark@suse.de" date="1022155200">- install config-file templates</changelog>
+<changelog author="- stark@suse.de" date="1022068800">- initial version of Nagios (the successor of NetSaint)</changelog>
+</package>
+
+
+
+
+
+
+
+<package pkgid="3dc9c17696ba449e5f7252881bb11f956f6b893a" name="nagios" arch="src">
+<version epoch="0" ver="1.3" rel="14.1"/>
+<changelog author="- stark@suse.de" date="1146657600">- fixed possible buffer overflow in CGI scripts (#140494)
+  (CVE-2006-2162)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- stark@suse.de" date="1136808000">- fixed off-by-one overflow (#141124)</changelog>
+<changelog author="- stark@suse.de" date="1132315200">- update to stable version 1.3</changelog>
+<changelog author="- stark@suse.de" date="1128600000">- fixed autobuild warnings about printf format</changelog>
+<changelog author="- stark@suse.de" date="1116072000">- fixed compiler errors about usage of uninitialized vars</changelog>
+<changelog author="- aj@suse.de" date="1114344000">- Compile with GCC4.</changelog>
+<changelog author="- ro@suse.de" date="1093867200">- removed apache1 traces</changelog>
+<changelog author="- stark@suse.de" date="1083585600">- fix compiler-warnings</changelog>
+<changelog author="- stark@suse.de" date="1075809600">- update to nagios 1.2</changelog>
+<changelog author="- stark@suse.de" date="1063886400">- /usr/lib/nagios and /usr/lib/nagios/plugins are owned
+  by root (#31100)</changelog>
+<changelog author="- stark@suse.de" date="1063022400">- add apache2 config-files, require http_daemon
+  and need apache2-devel-packages for build (#30157)</changelog>
+<changelog author="- ro@suse.de" date="1061985600">- remove traceroute-lbl from neededforbuild
+  traceroute from net-tools is used anyway</changelog>
+<changelog author="- stark@suse.de" date="1061208000">- added activation meta-data to sysconfig (#28830)</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- change cmdgrp from nogroup to www (the group apache now runs
+  under) [#21782]
+- implement try-restart correctly</changelog>
+<changelog author="- stark@suse.de" date="1059652800">- use stop/restart macros</changelog>
+<changelog author="- aj@suse.de" date="1059480000">- Fix chown invocation.</changelog>
+<changelog author="- stark@suse.de" date="1059134400">- added bugfixes</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Make sure plugin directory exist</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Add nagios libdir to filelist</changelog>
+<changelog author="- stark@suse.de" date="1054728000">- update to bugfix release 1.1
+  * File descriptor leak in file-based performance routines
+  * Timestamp fix for PostgreSQL 7.3
+  * Minor bug fixes to cmd and history CGIs
+  * Minor documentation fixes</changelog>
+<changelog author="- stark@suse.de" date="1042459200">- added sysconfig-metadata (#22607)</changelog>
+<changelog author="- stark@suse.de" date="1038830400">- adopted sapmoni.dif for gcc 3.3</changelog>
+<changelog author="- stark@suse.de" date="1038225600">- update to 1.0 final
+  * Minor bug fixes in CGIs
+  * Minor doc updates
+  * Bug fix for flex host downtime
+  * Embedded perl bug fixe for arguments with spaces
+  * Host escalation logic fix</changelog>
+<changelog author="- stark@suse.de" date="1034683200">- running pre-flight configuration check always when
+  starting nagios (#20916)
+- added logrotate definitions for logfiles
+- fixed embedded perl (#20916) (thanks to mls)</changelog>
+<changelog author="- stark@suse.de" date="1032782400">- update to 1.0b6
+  * Added wildcards and multiple hosts/hostgroups to host dependency
+  and service dependency definitions
+  * Added wildcard support for members directive in hostgroup
+  definitions
+  * Minor bug fixes and cleanups in CGIs
+  * Minor cleanups in the core
+  * New version of snprintf() included that supports %f
+  * Notification interval for escalations now defaults to
+  non-escalated value</changelog>
+<changelog author="- stark@suse.de" date="1030104000">- update to 1.0b5
+  * Fixed memory leak related to processing of passive checks
+  * Memory leak fixes in the CGIs
+  * Minor interface changes to the status and TAC CGIs
+  * Bug fix in the notification CGI
+  * Added minimalistic sample object config file (minimal.cfg)</changelog>
+<changelog author="- stark@suse.de" date="1029499200">- added PreReq (Bug #17938 #17939)</changelog>
+<changelog author="- stark@suse.de" date="1029153600">- updated sapmoni patch to provide documentation</changelog>
+<changelog author="- mls@suse.de" date="1029153600">- made it work with threaded perl-5.8</changelog>
+<changelog author="- stark@suse.de" date="1028808000">- added patch to be able to use nagios-plugins-sap</changelog>
+<changelog author="- adrian@suse.de" date="1027771200">- fix neededforbuild</changelog>
+<changelog author="- stark@suse.de" date="1025870400">- update to 1.0b4
+  * Address field in host definitions is now optional in
+  template-based config format. Defaults to host name if not
+  specified.
+  * Fixed memory leak when processing passive checks
+  * Added illegal_macro_output_chars variable to main config file
+  to allow for stripping dangerous shell characters from plugin
+  output</changelog>
+<changelog author="- stark@suse.de" date="1023883200">- removed -devel subpackage and moved the file to main-package</changelog>
+<changelog author="- stark@suse.de" date="1023710400">- update to 1.0b3
+  * Bug fixes in template-based object code
+  * Bug fix in tac CGI for counting host and service states
+  properly
+  * Default is now to check for external commands as often as
+  possible
+  * Ping syntax for WAP interface moved to CGI config file
+  * Command file user/group defaults to Nagios user/group unless
+  overridden with configure script options
+  * Sample template-based object config file fixes</changelog>
+<changelog author="- stark@suse.de" date="1022587200">- update to 1.0b2
+  * Fixed bug with escaping strings for DB support
+  (MySQL &amp; PostgreSQL)
+  * Several bug fixes in template-based object code
+  * Multiple hostgroups supported in host and service escalations
+  * Wildcard support in service, service escalation, and host
+  escalation definitions</changelog>
+<changelog author="- stark@suse.de" date="1022155200">- install config-file templates</changelog>
+<changelog author="- stark@suse.de" date="1022068800">- initial version of Nagios (the successor of NetSaint)</changelog>
+</package>
+
+
+
+
+
+
+
+<package pkgid="3bfcce7e5a0d0daf60bc1a89eb2d8e0a4efe6b8a" name="nagios-www" arch="x86_64">
+<version epoch="0" ver="1.3" rel="14.1"/>
+<changelog author="- stark@suse.de" date="1146657600">- fixed possible buffer overflow in CGI scripts (#140494)
+  (CVE-2006-2162)</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- stark@suse.de" date="1136808000">- fixed off-by-one overflow (#141124)</changelog>
+<changelog author="- stark@suse.de" date="1132315200">- update to stable version 1.3</changelog>
+<changelog author="- stark@suse.de" date="1128600000">- fixed autobuild warnings about printf format</changelog>
+<changelog author="- stark@suse.de" date="1116072000">- fixed compiler errors about usage of uninitialized vars</changelog>
+<changelog author="- aj@suse.de" date="1114344000">- Compile with GCC4.</changelog>
+<changelog author="- ro@suse.de" date="1093867200">- removed apache1 traces</changelog>
+<changelog author="- stark@suse.de" date="1083585600">- fix compiler-warnings</changelog>
+<changelog author="- stark@suse.de" date="1075809600">- update to nagios 1.2</changelog>
+<changelog author="- stark@suse.de" date="1063886400">- /usr/lib/nagios and /usr/lib/nagios/plugins are owned
+  by root (#31100)</changelog>
+<changelog author="- stark@suse.de" date="1063022400">- add apache2 config-files, require http_daemon
+  and need apache2-devel-packages for build (#30157)</changelog>
+<changelog author="- ro@suse.de" date="1061985600">- remove traceroute-lbl from neededforbuild
+  traceroute from net-tools is used anyway</changelog>
+<changelog author="- stark@suse.de" date="1061208000">- added activation meta-data to sysconfig (#28830)</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- change cmdgrp from nogroup to www (the group apache now runs
+  under) [#21782]
+- implement try-restart correctly</changelog>
+<changelog author="- stark@suse.de" date="1059652800">- use stop/restart macros</changelog>
+<changelog author="- aj@suse.de" date="1059480000">- Fix chown invocation.</changelog>
+<changelog author="- stark@suse.de" date="1059134400">- added bugfixes</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Make sure plugin directory exist</changelog>
+<changelog author="- kukuk@suse.de" date="1055419200">- Add nagios libdir to filelist</changelog>
+<changelog author="- stark@suse.de" date="1054728000">- update to bugfix release 1.1
+  * File descriptor leak in file-based performance routines
+  * Timestamp fix for PostgreSQL 7.3
+  * Minor bug fixes to cmd and history CGIs
+  * Minor documentation fixes</changelog>
+<changelog author="- stark@suse.de" date="1042459200">- added sysconfig-metadata (#22607)</changelog>
+<changelog author="- stark@suse.de" date="1038830400">- adopted sapmoni.dif for gcc 3.3</changelog>
+<changelog author="- stark@suse.de" date="1038225600">- update to 1.0 final
+  * Minor bug fixes in CGIs
+  * Minor doc updates
+  * Bug fix for flex host downtime
+  * Embedded perl bug fixe for arguments with spaces
+  * Host escalation logic fix</changelog>
+<changelog author="- stark@suse.de" date="1034683200">- running pre-flight configuration check always when
+  starting nagios (#20916)
+- added logrotate definitions for logfiles
+- fixed embedded perl (#20916) (thanks to mls)</changelog>
+<changelog author="- stark@suse.de" date="1032782400">- update to 1.0b6
+  * Added wildcards and multiple hosts/hostgroups to host dependency
+  and service dependency definitions
+  * Added wildcard support for members directive in hostgroup
+  definitions
+  * Minor bug fixes and cleanups in CGIs
+  * Minor cleanups in the core
+  * New version of snprintf() included that supports %f
+  * Notification interval for escalations now defaults to
+  non-escalated value</changelog>
+<changelog author="- stark@suse.de" date="1030104000">- update to 1.0b5
+  * Fixed memory leak related to processing of passive checks
+  * Memory leak fixes in the CGIs
+  * Minor interface changes to the status and TAC CGIs
+  * Bug fix in the notification CGI
+  * Added minimalistic sample object config file (minimal.cfg)</changelog>
+<changelog author="- stark@suse.de" date="1029499200">- added PreReq (Bug #17938 #17939)</changelog>
+<changelog author="- stark@suse.de" date="1029153600">- updated sapmoni patch to provide documentation</changelog>
+<changelog author="- mls@suse.de" date="1029153600">- made it work with threaded perl-5.8</changelog>
+<changelog author="- stark@suse.de" date="1028808000">- added patch to be able to use nagios-plugins-sap</changelog>
+<changelog author="- adrian@suse.de" date="1027771200">- fix neededforbuild</changelog>
+<changelog author="- stark@suse.de" date="1025870400">- update to 1.0b4
+  * Address field in host definitions is now optional in
+  template-based config format. Defaults to host name if not
+  specified.
+  * Fixed memory leak when processing passive checks
+  * Added illegal_macro_output_chars variable to main config file
+  to allow for stripping dangerous shell characters from plugin
+  output</changelog>
+<changelog author="- stark@suse.de" date="1023883200">- removed -devel subpackage and moved the file to main-package</changelog>
+<changelog author="- stark@suse.de" date="1023710400">- update to 1.0b3
+  * Bug fixes in template-based object code
+  * Bug fix in tac CGI for counting host and service states
+  properly
+  * Default is now to check for external commands as often as
+  possible
+  * Ping syntax for WAP interface moved to CGI config file
+  * Command file user/group defaults to Nagios user/group unless
+  overridden with configure script options
+  * Sample template-based object config file fixes</changelog>
+<changelog author="- stark@suse.de" date="1022587200">- update to 1.0b2
+  * Fixed bug with escaping strings for DB support
+  (MySQL &amp; PostgreSQL)
+  * Several bug fixes in template-based object code
+  * Multiple hostgroups supported in host and service escalations
+  * Wildcard support in service, service escalation, and host
+  escalation definitions</changelog>
+<changelog author="- stark@suse.de" date="1022155200">- install config-file templates</changelog>
+<changelog author="- stark@suse.de" date="1022068800">- initial version of Nagios (the successor of NetSaint)</changelog>
+</package>
+
+
+
+
+
+
+<package pkgid="23b988b5b6e50d7cfda50dda22508c5f1dd07307" name="dhcdbd" arch="i586">
+<version epoch="0" ver="1.12" rel="14.2"/>
+<changelog author="- rml@suse.de" date="1147176000">- Fix 64-bit build (use pkg-config like we should)</changelog>
+<changelog author="- rml@suse.de" date="1146744000">- Ask dhclient to set the hostname as required by the sysconfig
+  option DHCLIENT_HOSTNAME_OPTION (Novell major bug #139532)
+- Fix possible memory corruption</changelog>
+<changelog author="- rml@suse.de" date="1143547200">- Log debugging spew less readily (Novell bug #161138)</changelog>
+<changelog author="- rml@suse.de" date="1142942400">- Survive DBUS restarts (Novell bug #150042)</changelog>
+<changelog author="- rml@suse.de" date="1140782400">- Add dhcdbd-no-nr_open-rml.patch to replace NR_OPEN with a call
+  to getdtablesize() to fix build on beta and remove use of
+  NR_OPEN.</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rml@suse.de" date="1137672000">- 1.12 (bug fixes only)</changelog>
+<changelog author="- rml@suse.de" date="1136980800">- Remove named user from dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1136548800">- Use correct lease directory (fix bug #139606)</changelog>
+<changelog author="- rml@suse.de" date="1136376000">- proper permissions on /etc/dbus-1/system.d/dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1134993600">- Update to 1.11 (fixes build against DBUS 0.60)</changelog>
+<changelog author="- rml@suse.de" date="1134388800">- Remove dhcdbd service file and do not create named user or
+  group (#136872)</changelog>
+<changelog author="- ro@suse.de" date="1134129600">- create named user and group (since referenced in config)
+  fixing (#136872)</changelog>
+<changelog author="- rml@suse.de" date="1132228800">- Upgrade to 1.10 (includes our two patches).</changelog>
+<changelog author="- rml@suse.de" date="1130760000">- Add patch dhcdbd-quiet-dhclient.patch to pass &quot;-q&quot; option to
+  dhclient in order to minimize noise.
+- Add patch dhcdbd-use-daemon.patch to use daemon(3) instead of
+  hand-coded daemonizing code.  Also ensures that stdout and stderr
+  are redirected to /dev/null, not /dev/console.</changelog>
+<changelog author="- rml@suse.de" date="1129809600">- Update to dhcdbd 1.9</changelog>
+<changelog author="- ro@suse.de" date="1123848000">- changed requires (#104373)</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Remove init script for rml, no longer needed</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Change requires for rml</changelog>
+<changelog author="- ro@suse.de" date="1119614400">- created package taken from FC
+- first steps to adapt init script</changelog>
+</package>
+
+
+
+
+
+
+<package pkgid="da37d6c81230024f202fbb92107ab88ade872bd3" name="dhcdbd" arch="ppc">
+<version epoch="0" ver="1.12" rel="14.2"/>
+<changelog author="- rml@suse.de" date="1147176000">- Fix 64-bit build (use pkg-config like we should)</changelog>
+<changelog author="- rml@suse.de" date="1146744000">- Ask dhclient to set the hostname as required by the sysconfig
+  option DHCLIENT_HOSTNAME_OPTION (Novell major bug #139532)
+- Fix possible memory corruption</changelog>
+<changelog author="- rml@suse.de" date="1143547200">- Log debugging spew less readily (Novell bug #161138)</changelog>
+<changelog author="- rml@suse.de" date="1142942400">- Survive DBUS restarts (Novell bug #150042)</changelog>
+<changelog author="- rml@suse.de" date="1140782400">- Add dhcdbd-no-nr_open-rml.patch to replace NR_OPEN with a call
+  to getdtablesize() to fix build on beta and remove use of
+  NR_OPEN.</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rml@suse.de" date="1137672000">- 1.12 (bug fixes only)</changelog>
+<changelog author="- rml@suse.de" date="1136980800">- Remove named user from dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1136548800">- Use correct lease directory (fix bug #139606)</changelog>
+<changelog author="- rml@suse.de" date="1136376000">- proper permissions on /etc/dbus-1/system.d/dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1134993600">- Update to 1.11 (fixes build against DBUS 0.60)</changelog>
+<changelog author="- rml@suse.de" date="1134388800">- Remove dhcdbd service file and do not create named user or
+  group (#136872)</changelog>
+<changelog author="- ro@suse.de" date="1134129600">- create named user and group (since referenced in config)
+  fixing (#136872)</changelog>
+<changelog author="- rml@suse.de" date="1132228800">- Upgrade to 1.10 (includes our two patches).</changelog>
+<changelog author="- rml@suse.de" date="1130760000">- Add patch dhcdbd-quiet-dhclient.patch to pass &quot;-q&quot; option to
+  dhclient in order to minimize noise.
+- Add patch dhcdbd-use-daemon.patch to use daemon(3) instead of
+  hand-coded daemonizing code.  Also ensures that stdout and stderr
+  are redirected to /dev/null, not /dev/console.</changelog>
+<changelog author="- rml@suse.de" date="1129809600">- Update to dhcdbd 1.9</changelog>
+<changelog author="- ro@suse.de" date="1123848000">- changed requires (#104373)</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Remove init script for rml, no longer needed</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Change requires for rml</changelog>
+<changelog author="- ro@suse.de" date="1119614400">- created package taken from FC
+- first steps to adapt init script</changelog>
+</package>
+
+
+
+
+
+
+<package pkgid="e285ea0354d8c33ec6c631e5ed925142b9e30bf4" name="dhcdbd" arch="src">
+<version epoch="0" ver="1.12" rel="14.2"/>
+<changelog author="- rml@suse.de" date="1147176000">- Fix 64-bit build (use pkg-config like we should)</changelog>
+<changelog author="- rml@suse.de" date="1146744000">- Ask dhclient to set the hostname as required by the sysconfig
+  option DHCLIENT_HOSTNAME_OPTION (Novell major bug #139532)
+- Fix possible memory corruption</changelog>
+<changelog author="- rml@suse.de" date="1143547200">- Log debugging spew less readily (Novell bug #161138)</changelog>
+<changelog author="- rml@suse.de" date="1142942400">- Survive DBUS restarts (Novell bug #150042)</changelog>
+<changelog author="- rml@suse.de" date="1140782400">- Add dhcdbd-no-nr_open-rml.patch to replace NR_OPEN with a call
+  to getdtablesize() to fix build on beta and remove use of
+  NR_OPEN.</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rml@suse.de" date="1137672000">- 1.12 (bug fixes only)</changelog>
+<changelog author="- rml@suse.de" date="1136980800">- Remove named user from dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1136548800">- Use correct lease directory (fix bug #139606)</changelog>
+<changelog author="- rml@suse.de" date="1136376000">- proper permissions on /etc/dbus-1/system.d/dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1134993600">- Update to 1.11 (fixes build against DBUS 0.60)</changelog>
+<changelog author="- rml@suse.de" date="1134388800">- Remove dhcdbd service file and do not create named user or
+  group (#136872)</changelog>
+<changelog author="- ro@suse.de" date="1134129600">- create named user and group (since referenced in config)
+  fixing (#136872)</changelog>
+<changelog author="- rml@suse.de" date="1132228800">- Upgrade to 1.10 (includes our two patches).</changelog>
+<changelog author="- rml@suse.de" date="1130760000">- Add patch dhcdbd-quiet-dhclient.patch to pass &quot;-q&quot; option to
+  dhclient in order to minimize noise.
+- Add patch dhcdbd-use-daemon.patch to use daemon(3) instead of
+  hand-coded daemonizing code.  Also ensures that stdout and stderr
+  are redirected to /dev/null, not /dev/console.</changelog>
+<changelog author="- rml@suse.de" date="1129809600">- Update to dhcdbd 1.9</changelog>
+<changelog author="- ro@suse.de" date="1123848000">- changed requires (#104373)</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Remove init script for rml, no longer needed</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Change requires for rml</changelog>
+<changelog author="- ro@suse.de" date="1119614400">- created package taken from FC
+- first steps to adapt init script</changelog>
+</package>
+
+
+
+
+
+
+<package pkgid="bca8cb431261bf677ea69c848c1d54a34b16189a" name="dhcdbd" arch="x86_64">
+<version epoch="0" ver="1.12" rel="14.2"/>
+<changelog author="- rml@suse.de" date="1147176000">- Fix 64-bit build (use pkg-config like we should)</changelog>
+<changelog author="- rml@suse.de" date="1146744000">- Ask dhclient to set the hostname as required by the sysconfig
+  option DHCLIENT_HOSTNAME_OPTION (Novell major bug #139532)
+- Fix possible memory corruption</changelog>
+<changelog author="- rml@suse.de" date="1143547200">- Log debugging spew less readily (Novell bug #161138)</changelog>
+<changelog author="- rml@suse.de" date="1142942400">- Survive DBUS restarts (Novell bug #150042)</changelog>
+<changelog author="- rml@suse.de" date="1140782400">- Add dhcdbd-no-nr_open-rml.patch to replace NR_OPEN with a call
+  to getdtablesize() to fix build on beta and remove use of
+  NR_OPEN.</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rml@suse.de" date="1137672000">- 1.12 (bug fixes only)</changelog>
+<changelog author="- rml@suse.de" date="1136980800">- Remove named user from dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1136548800">- Use correct lease directory (fix bug #139606)</changelog>
+<changelog author="- rml@suse.de" date="1136376000">- proper permissions on /etc/dbus-1/system.d/dhcdbd.conf</changelog>
+<changelog author="- rml@suse.de" date="1134993600">- Update to 1.11 (fixes build against DBUS 0.60)</changelog>
+<changelog author="- rml@suse.de" date="1134388800">- Remove dhcdbd service file and do not create named user or
+  group (#136872)</changelog>
+<changelog author="- ro@suse.de" date="1134129600">- create named user and group (since referenced in config)
+  fixing (#136872)</changelog>
+<changelog author="- rml@suse.de" date="1132228800">- Upgrade to 1.10 (includes our two patches).</changelog>
+<changelog author="- rml@suse.de" date="1130760000">- Add patch dhcdbd-quiet-dhclient.patch to pass &quot;-q&quot; option to
+  dhclient in order to minimize noise.
+- Add patch dhcdbd-use-daemon.patch to use daemon(3) instead of
+  hand-coded daemonizing code.  Also ensures that stdout and stderr
+  are redirected to /dev/null, not /dev/console.</changelog>
+<changelog author="- rml@suse.de" date="1129809600">- Update to dhcdbd 1.9</changelog>
+<changelog author="- ro@suse.de" date="1123848000">- changed requires (#104373)</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Remove init script for rml, no longer needed</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Change requires for rml</changelog>
+<changelog author="- ro@suse.de" date="1119614400">- created package taken from FC
+- first steps to adapt init script</changelog>
+</package>
+
+
+
+
+
+<package pkgid="a6a40ca62165ceddc09181a9c6d11243b544dbc3" name="openldap2" arch="i586">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+<changelog author="- rhafer@suse.de" date="1147262400">- Really apply the patch for Bug#160566
+- slapd could crash while processing queries with pre-/postread
+  controls (Bug#173877, ITS#4532)</changelog>
+<changelog author="- rhafer@suse.de" date="1143201600">- Backported fix from CVS for occasional crashes in referral
+  chasing code (as used in e.g. back-meta/back-ldap).
+  (Bug: #160566, ITS: #4448)</changelog>
+<changelog author="- rhafer@suse.de" date="1142251200">- openldap2 must obsolete -back-monitor and -back-ldap to have them
+  removed during update (Bug: #157576)</changelog>
+<changelog author="- rhafer@suse.de" date="1140177600">- Add &quot;external&quot; to the list of supported SASL mechanisms
+  (Bug: #151771)</changelog>
+<changelog author="- rhafer@suse.de" date="1140091200">- Error out when conversion from old configfile to config database
+  fails (Bug: #135484,#135490 ITS: #4407)</changelog>
+<changelog author="- rhafer@suse.de" date="1139832000">- Don't ignore non-read/write epoll events (Bug: #149993,
+  ITS: #4395)
+- Added update message to /usr/share/update-messages/en/ and enable
+  it, when update did not succeed.</changelog>
+<changelog author="- rhafer@suse.de" date="1139486400">- OPENLDAP_CHOWN_DIRS honors databases defined in include files
+  (Bug: #135473)
+- Fixed version numbers in README.update
+- Fixed GSSAPI binds against Active Directory (Bug: #149390)</changelog>
+<changelog author="- rhafer@suse.de" date="1138968000">- Cleaned up update procedure
+- man-pages updates and fixes (Fate: #6365)</changelog>
+<changelog author="- rhafer@suse.de" date="1138363200">- Updated to 2.3.19 (Bug #144371)</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rhafer@suse.de" date="1138190400">- Updated Admin Guide to latest version
+- build slapcat from openldap-2.2.24 and install it to
+  /usr/sbin/openldap-2.2-slapcat to be able to migrate from
+  OpenLDAP 2.2.
+- removed slapd-backbdb-dbupgrade which is no longer needed
+- attempt to dump/reload bdb databases in %{post}
+- Update notes in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1137153600">- New sysconfig variable OPENLDAP_KRB5_KEYTAB
+- Cleanup in default configuration and init scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1136980800">- Updated to 2.3.17
+- Remove OPENLDAP_RUN_DB_RECOVER from sysconfig file in %post
+  slapd does now automatically recover the database if needed
+- Removed unneeded README.SuSE
+- Small adjustments to the default DB_CONFIG file</changelog>
+<changelog author="- rhafer@suse.de" date="1136808000">- Updated to 2.3.16</changelog>
+<changelog author="- rhafer@suse.de" date="1134993600">- Fixed filelist (slapd-hdb man-page was missing)</changelog>
+<changelog author="- rhafer@suse.de" date="1134129600">- Fixed build on x86_64</changelog>
+<changelog author="- rhafer@suse.de" date="1133956800">- Merged -back-ldap and -back-monitor subpackages into the main
+  package and don't build them as dynamic modules anymore.
+- updated to OpenLDAP 2.3.13</changelog>
+<changelog author="- rhafer@suse.de" date="1133179200">- updated to OpenLDAP 2.3.12</changelog>
+<changelog author="- rhafer@suse.de" date="1130328000">- updated to OpenLDAP 2.3.11
+- removed the &quot;LDAP_DEPRECATED&quot; workaround</changelog>
+<changelog author="- rhafer@suse.de" date="1127736000">- Add &quot;LDAP_DEPRECATED&quot; to ldap.h for now</changelog>
+<changelog author="- rhafer@suse.de" date="1127476800">- updated to OpenLDAP 2.3.7</changelog>
+<changelog author="- rhafer@suse.de" date="1124193600">- allow start_tls while chasing referrals (Bug #94355, ITS #3791)</changelog>
+<changelog author="- rhafer@suse.de" date="1120478400">- devel-subpackage requires openldap2-client of the same version
+  (Bugzilla: #93579)</changelog>
+<changelog author="- uli@suse.de" date="1120132800">- build with -fPIE (not -fpie) to avoid GOT overflow on s390*</changelog>
+<changelog author="- rhafer@suse.de" date="1119441600">- build the server packages with -fpie/-pie</changelog>
+<changelog author="- rhafer@suse.de" date="1118836800">- updated to 2.2.27</changelog>
+<changelog author="- rhafer@suse.de" date="1117022400">- libldap-gethostbyname_r.dif: Use gethostbyname_r instead of
+  gethostbyname in libldap. Should fix host lookups through
+  nss_ldap (Bugzilla: #76173)</changelog>
+<changelog author="- rhafer@suse.de" date="1115985600">- Updated to 2.2.26
+- made /%{_libdir}]/sasl2/slapd.conf %config(noreplace)</changelog>
+<changelog author="- rhafer@suse.de" date="1114689600">- Added /%{_libdir}]/sasl2/slapd.conf to avoid warnings about
+  unconfigured OTP mechanism (Bugzilla: #80588)</changelog>
+<changelog author="- rhafer@suse.de" date="1113307200">- added minimal timeout to startproc in init-script to let it
+  report the &quot;failed&quot; status correctly in case of misconfiguration
+  (Bugzilla: #76393)</changelog>
+<changelog author="- rhafer@suse.de" date="1112616000">- crl-check.dif: Implements CRL checking on client and server side
+- use different base ports for differnt values of BUILD_INCARNATION
+  (/.buildenv) to allow parallel runs of the test-suite on a single
+  machine</changelog>
+<changelog author="- uli@suse.de" date="1112616000">- force yielding-select test to yes (test occasionally hangs QEMU)</changelog>
+<changelog author="- uli@suse.de" date="1112356800">- disable test suite on ARM (hangs QEMU)</changelog>
+<changelog author="- rhafer@suse.de" date="1112097600">- updated to 2.2.24
+- enabled back-hdb</changelog>
+<changelog author="- rhafer@suse.de" date="1109764800">- syncrepl.dif: merged latest syncrepl fixes (Bugzilla: #65928)
+- libldap-reinit-fdset.dif: Re-init fd_sets when select is
+  interupted (Bugzilla #50076, ITS: #3524)</changelog>
+<changelog author="- rhafer@suse.de" date="1108641600">- checkproc_before_recover.dif: Check if slapd is stopped before
+  running db_recover from the init script. (Bugzilla: #50962)</changelog>
+<changelog author="- rhafer@suse.de" date="1107259200">- Cleanup back-bdb databases in %post, db-4.3 changed the
+  transaction log format again.
+- cosmetic fixes in init script</changelog>
+<changelog author="- rhafer@suse.de" date="1106654400">- updated to 2.2.23
+- cleaned up #neededforbuild
+- package should also build on older SuSE Linux releases now
+- increased killproc timeout in init-script (Bugzilla: #47227)</changelog>
+<changelog author="- rhafer@suse.de" date="1105617600">- updated to 2.2.20
+- Removed unneeded dependencies</changelog>
+<changelog author="- kukuk@suse.de" date="1102680000">- don't install *.la files</changelog>
+<changelog author="- rhafer@suse.de" date="1100088000">- updated to 2.2.18
+- use kerberos-devel-packages in neededforbuild</changelog>
+<changelog author="- ro@suse.de" date="1096027200">- re-arranged specfile to sequence (header (package/descr)* rest)
+  so the checking parser is not confused ...</changelog>
+<changelog author="- rhafer@suse.de" date="1096027200">- Added pre_checkin.sh to generate a separate openldap2-client
+  spec-file from which the openldap2-client and openldap2-devel
+  subpackages are built. Should reduce build time for libldap as
+  the test-suite is only executed in openldap2.spec.</changelog>
+<changelog author="- rhafer@suse.de" date="1094817600">- libldap-result.dif: ldapsearch was hanging in select() when
+  retrieving results from eDirectory through a StartTLS protected
+  connection (Bugzilla #44942)</changelog>
+<changelog author="- dobey@suse.de" date="1092052800">- added ntlm support</changelog>
+<changelog author="- rhafer@suse.de" date="1091534400">- updated to 2.2.16
+- Updated ACLs in slapd_conf.dif to disable default read access
+  to the &quot;userPKCS12&quot; Attribute
+- rc-check-conn.diff: When starting slapd wait until is accepts
+  connections, or 10 seconds at maximum (Bugzilla #41354)
+- Backported -o slp={on|off} feature from OpenLDAP Head and added
+  new sysconfig variable (OPENLDAP_REGISTER_SLP) to be able
+  to switch SLP registration on and off. (Bugzilla #39865)
+- removed unneeded README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1083326400">- updated to 2.2.11
+- remove SLES8 update specific stuff
+- Bugzilla #39652: Updated slapd_conf.dif to contain basic access
+  control
+- Bugzilla #39468: Added missing items to yast.schema
+- fixed strict-aliasing compiler warnings (strict-aliasing.dif)</changelog>
+<changelog author="- coolo@suse.de" date="1083240000">- build with several jobs if available</changelog>
+<changelog author="- rhafer@suse.de" date="1082376000">- ldapi_url.dif: Fixed paths for LDAPI-socket, pid-file and
+  args-file (Bugzilla #38790)
+- ldbm_modrdn.dif: Fixed back-ldbm modrdn indexing bug (ITS #3059,
+  Bugzilla #38915)
+- modify_check_duplicates.dif: check for duplicate attribute
+  values in modify requests (ITS #3066/#3097, Bugzilla #38607)
+- updated and renamed yast2userconfig.schema to yast.schema as it
+  contains more that only user configuration now
+- syncrepl.dif: addtional fixes for syncrepl (ITS #3055, #3056)
+- test_syncrepl_timeout: increased sleep timeout in syncrepl
+  testsuite</changelog>
+<changelog author="- rhafer@suse.de" date="1080820800">- added &quot;TLS_REQCERT allow&quot; to /etc/openldap/ldap.conf, to make
+  START_TLS work without access to the CA Certificate.
+  (Bugzilla: #37393)</changelog>
+<changelog author="- rhafer@suse.de" date="1080302400">- fixed filelist
+- check-build.sh (build on kernel &gt;= 2.6.4 hosts only)
+- yast2user.schema / slapd.conf fixed (#37076)
+- don't check for TLS-options is init-script anymore (#33560)
+- fixed various typos in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1079524800">- fixed build of openldap-2.1-slapcat (using correct db41 include
+  files, build backends as on sles8)
+- attempt to update bdb database and reindex ldbm database in %{post}
+- Update notes in README.update
+- better default configuration (including default DB_CONFIG file)
+- misc updates for the YaST schema
+- fixed crasher in syncrepl-code (syncrepl.dif)</changelog>
+<changelog author="- schwab@suse.de" date="1079438400">- Fix type mismatch.</changelog>
+<changelog author="- rhafer@suse.de" date="1078228800">- updated to 2.2.6
+- build a openldap-2.1-slapcat from 2.1.25 sources  to be able to
+  migrate from SLES8 and SL 9.0</changelog>
+<changelog author="- ro@suse.de" date="1077192000">- added check-build.sh (build on 2.6 hosts only)</changelog>
+<changelog author="- rhafer@suse.de" date="1075982400">- updated to 2.2.5
+- adjusted rfc2307bis.schema to support UTF-8 values in most
+  attributes
+- enabled proxycache-overlay (wiht fix to work with back-ldbm)</changelog>
+<changelog author="- rhafer@suse.de" date="1073995200">- updated to 2.2.4
+- updated Admin Guide to most recent version</changelog>
+<changelog author="- adrian@suse.de" date="1073736000">- add %defattr
+- fix build as user</changelog>
+<changelog author="- rhafer@suse.de" date="1070884800">- updated to 2.1.25
+- small fixes for the YaST user schema</changelog>
+<changelog author="- rhafer@suse.de" date="1068552000">- enabled SLP-support</changelog>
+<changelog author="- kukuk@suse.de" date="1066392000">- Remove unused des from neededforbuild</changelog>
+<changelog author="- mt@suse.de" date="1062504000">- Bugzilla #29859: fixed typo in sysconfig metadata,
+  usage of OPENLDAP_LDAPS_INTERFACES in init script
+- added /usr/lib/sasl2/slapd.conf permissions handling
+- added sysconfig variable OPENLDAP_SLAPD_PARAMS=&quot;&quot;
+  to support additional slapd start parameters
+- added sysconfig variable OPENLDAP_START_LDAPI=NO/yes
+  for ldapi:/// (LDAP over IPC) URLs</changelog>
+<changelog author="- rhafer@suse.de" date="1060862400">- added activation metadata to sysconfig template (Bugzilla #28911)
+- removed lint from specfile</changelog>
+<changelog author="- rhafer@suse.de" date="1060257600">- added %stop_on_removal and %restart_on_update calls
+- bdb_addcnt.dif fixes a possible endless loop in id2entry()
+- addonschema.tar.gz: some extra Schema files (YaST, RFC2307bis)</changelog>
+<changelog author="- rhafer@suse.de" date="1058356800">- removed fillup_only and call fillup_and_insserv correctly
+- new Options in sysconfig.openldap: OPENLDAP_LDAP_INTERFACES,
+  OPENLDAP_LDAPS_INTERFACES and OPENLDAP_RUN_DB_RECOVER</changelog>
+<changelog author="- rhafer@suse.de" date="1057060800">- updated to 2.1.22
+- updated Admin Guide to most recent version
+- build librewrite with -fPIC</changelog>
+<changelog author="- rhafer@suse.de" date="1055764800">- updated to 2.1.21</changelog>
+<changelog author="- ro@suse.de" date="1055332800">- fixed requires lines</changelog>
+<changelog author="- rhafer@suse.de" date="1053950400">- don't link back-ldap against librewrite.a, it's already linked
+  into slapd (package should build on non-i386 Archs again)</changelog>
+<changelog author="- rhafer@suse.de" date="1053691200">- fixed dynamic build of back-ldap
+- new subpackage back-ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1053432000">- updated to version 2.1.20
+- enabled dynamic backend modules
+- new subpackages back-perl, back-meta and back-monitor
+- remove unpacked files from BuildRoot</changelog>
+<changelog author="- rhafer@suse.de" date="1052481600">- updated to version 2.1.19</changelog>
+<changelog author="- ro@suse.de" date="1050494400">- fixed requires for devel-package ...</changelog>
+<changelog author="- ro@suse.de" date="1050408000">- fixed neededforbuild</changelog>
+<changelog author="- kukuk@suse.de" date="1045137600">- Enable IPv6 again</changelog>
+<changelog author="- rhafer@suse.de" date="1044964800">- added /etc/openldap to filelist</changelog>
+<changelog author="- rhafer@suse.de" date="1044273600">- switch default backend to ldbm</changelog>
+<changelog author="- ro@suse.de" date="1044187200">- fixed requires for devel package (cyrus-sasl2-devel)</changelog>
+<changelog author="- rhafer@suse.de" date="1044014400">- liblber.dif: Fixes two bugs in liblber by which remote attackers
+  could crash the LDAP server (Bugzilla #22469, OpenLDAP ITS #2275
+  and #2280)</changelog>
+<changelog author="- choeger@suse.de" date="1042545600">- build using sasl2</changelog>
+<changelog author="- rhafer@suse.de" date="1042459200">- updated to version 2.1.12
+- added metadata to sysconfig template (Bug: #22666)</changelog>
+<changelog author="- rhafer@suse.de" date="1038484800">- updated to version 2.1.8
+- added additional fix of 64bit archs
+- added secpatch.dif to fix setuid issues in libldap</changelog>
+<changelog author="- rhafer@suse.de" date="1031313600">- fix for Bugzilla ID #18981, chown to OPENLDAP_USER didn't work
+  with multiple database backend directories</changelog>
+<changelog author="- rhafer@suse.de" date="1030968000">- removed damoenstart_ipv6.diff and disabled IPv6 support due to
+  massive problems with nss_ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1030363200">- ldap_user.dif: slapd is now run a the user/group ldap (Bugzilla
+  ID#17697)</changelog>
+<changelog author="- rhafer@suse.de" date="1030104000">- updated to version 2.1.4, which fixes tons of bugs
+- added damoenstart_ipv6.diff (slapd was not starting when
+  configured to listen on IPv4 and IPv6 interfaces, as done by the
+  start script)
+- added README.SuSE with some hints about the bdb-backend
+- updated filelist to include only the man pages of the backends,
+  that were built</changelog>
+<changelog author="- rhafer@suse.de" date="1029412800">- removed termcap and readline from neededforbuild</changelog>
+<changelog author="- rhafer@suse.de" date="1028808000">- enabled {CRYPT} passwords
+- update filelist (added new manpages)</changelog>
+<changelog author="- rhafer@suse.de" date="1027598400">- patches for 64 bit architectures</changelog>
+<changelog author="- rhafer@suse.de" date="1027080000">- update to 2.1.3</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- fix openldap2-devel requires</changelog>
+<changelog author="- rhafer@suse.de" date="1025784000">- switched back from cyrus-sasl2 to cyrus-sasl</changelog>
+<changelog author="- rhafer@suse.de" date="1025697600">- updated to OpenLDAP 2.1.2
+- added the OpenLDAP Administration Guide
+- enabled additional backends (ldap, meta, monitor)</changelog>
+<changelog author="- olh@suse.de" date="1023710400">- hack build/ltconfig to build shared libs on ppc64</changelog>
+<changelog author="- rhafer@suse.de" date="1023278400">- created /etc/sysconfig/openldap and OPENLDAP_START_LDAPS variable
+  to enable ldap over ssl support</changelog>
+<changelog author="- rhafer@suse.de" date="1015502400">- Fix for Bugzilla ID#14569 (added cyrus-sasl-devel openssl-devel
+  to the &quot;Requires&quot; Section of the -devel subpackage)</changelog>
+<changelog author="- rhafer@suse.de" date="1014033600">- updated to the latest STABLE release (2.0.23) which fixes some
+  nasty bugs see ITS #1562,#1582,#1577,#1578</changelog>
+<changelog author="- rhafer@suse.de" date="1013083200">- updated to the latest release (which fixes a index corruption
+  bug)
+- cleanup in neededforbuild
+- small fixes for the init-scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1011268800">- updated to the latest stable release (2.0.21)</changelog>
+<changelog author="- egmont@suselinux.hu" date="1011182400">- removed periods and colons from startup/shutdown messages</changelog>
+<changelog author="- rhafer@suse.de" date="1011096000">- updated to v2.0.20 (which fixes a security hole in ACL
+  processing)</changelog>
+<changelog author="- rhafer@suse.de" date="1010750400">- converted archive to bzip2
+- makes use of %{_libdir} now
+- set CFLAGS to -O0 for archs ia64, s390(x) and alpha otherwise
+  the test suite fails on these archs
+- changed slapd.conf to store the database under /var/lib/ldap
+  (this patch was missing in the last versions by accident)</changelog>
+<changelog author="- rhafer@suse.de" date="1010404800">- update to v2.0.19</changelog>
+<changelog author="- rhafer@suse.de" date="1007640000">- eliminated START_LDAP, START_SLURPD variables in rc.config
+- created separate init script for slurpd
+- moved init scripts from dif to separate source tgz</changelog>
+<changelog author="- choeger@suse.de" date="1004097600">- update to v2.0.18</changelog>
+<changelog author="- choeger@suse.de" date="1003147200">- update to v2.0.17
+  added a sleep to the restart section
+  moved some manpages to the client package</changelog>
+<changelog author="- choeger@suse.de" date="1001937600">- update to v2.0.15</changelog>
+<changelog author="- choeger@suse.de" date="1000296000">- backported the full bugfix from openldap-2.0.14</changelog>
+<changelog author="- choeger@suse.de" date="1000209600">- Bugfix for slurpd millionth second bug (ITS#1323)</changelog>
+<changelog author="- choeger@suse.de" date="1000123200">- moved ldapfilter.conf ldaptemplates.conf ldapsearchprefs.conf
+  to openldap2-client package</changelog>
+<changelog author="- choeger@suse.de" date="999518400">- update to version 2.0.12</changelog>
+<changelog author="- choeger@suse.de" date="994075200">- bugfix: init script was not LSB compliant, Bugzilla ID#9072</changelog>
+<changelog author="- ro@suse.de" date="992952000">- fixed for autoconf again</changelog>
+<changelog author="- choeger@suse.de" date="992606400">- update to 2.0.11
+- removed autoconf in specfile, because it doesn't work</changelog>
+<changelog author="- choeger@suse.de" date="990619200">- update to version 2.0.10 (minor fixes)</changelog>
+<changelog author="- choeger@suse.de" date="990532800">- update to version 2.0.9</changelog>
+<changelog author="- choeger@suse.de" date="988027200">- removed kerberos support
+- added aci support</changelog>
+<changelog author="- choeger@suse.de" date="987768000">- added kerberos support</changelog>
+<changelog author="- choeger@suse.de" date="986472000">- moved section 5 and 8 manpages to the server part of package</changelog>
+<changelog author="- kukuk@suse.de" date="984571200">- Move *.so links into -devel package
+- -devel requires -client</changelog>
+<changelog author="- choeger@suse.de" date="984052800">- split up into openldap2-client and -devel</changelog>
+<changelog author="- ro@suse.de" date="983275200">- changed neededforbuild &lt;cyrus-sasl&gt; to &lt;cyrus-sasl cyrus-sasl-devel&gt;</changelog>
+<changelog author="- ro@suse.de" date="982929600">- added readline/readline-devel to neededforbuild (split from bash)</changelog>
+<changelog author="- choeger@suse.de" date="978609600">- bugfix: slapd.conf rename /var/lib/openldap-ldbm to
+  /var/lib/ldap
+  init script: use $remote_fs</changelog>
+<changelog author="- olh@suse.de" date="978436800">- use script name in %post</changelog>
+<changelog author="- choeger@suse.de" date="976190400">- bugfix from Andreas Jaeger:
+  workaround for glibc2.2, detach</changelog>
+<changelog author="- ro@suse.de" date="975672000">- hacked configure for apparently broken pthread</changelog>
+<changelog author="- ro@suse.de" date="975672000">- fixed spec</changelog>
+<changelog author="- choeger@suse.de" date="974980800">- made configs %config(noreplace) (Bug 4112)
+- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="974894400">- adopted new init scheme</changelog>
+<changelog author="- choeger@suse.de" date="974289600">- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="973857600">- added buildroot</changelog>
+<changelog author="- choeger@suse.de" date="973598400">- long package name
+- new version, 2.0.7</changelog>
+<changelog author="- choeger@suse.de" date="970833600">- first package of openldap2 (v2.0.6)</changelog>
+</package>
+
+
+
+
+
+<package pkgid="2fe6c9ec6a1f7e52edc54154f70421ce9aeb5f96" name="openldap2" arch="ppc">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+<changelog author="- rhafer@suse.de" date="1147262400">- Really apply the patch for Bug#160566
+- slapd could crash while processing queries with pre-/postread
+  controls (Bug#173877, ITS#4532)</changelog>
+<changelog author="- rhafer@suse.de" date="1143201600">- Backported fix from CVS for occasional crashes in referral
+  chasing code (as used in e.g. back-meta/back-ldap).
+  (Bug: #160566, ITS: #4448)</changelog>
+<changelog author="- rhafer@suse.de" date="1142251200">- openldap2 must obsolete -back-monitor and -back-ldap to have them
+  removed during update (Bug: #157576)</changelog>
+<changelog author="- rhafer@suse.de" date="1140177600">- Add &quot;external&quot; to the list of supported SASL mechanisms
+  (Bug: #151771)</changelog>
+<changelog author="- rhafer@suse.de" date="1140091200">- Error out when conversion from old configfile to config database
+  fails (Bug: #135484,#135490 ITS: #4407)</changelog>
+<changelog author="- rhafer@suse.de" date="1139832000">- Don't ignore non-read/write epoll events (Bug: #149993,
+  ITS: #4395)
+- Added update message to /usr/share/update-messages/en/ and enable
+  it, when update did not succeed.</changelog>
+<changelog author="- rhafer@suse.de" date="1139486400">- OPENLDAP_CHOWN_DIRS honors databases defined in include files
+  (Bug: #135473)
+- Fixed version numbers in README.update
+- Fixed GSSAPI binds against Active Directory (Bug: #149390)</changelog>
+<changelog author="- rhafer@suse.de" date="1138968000">- Cleaned up update procedure
+- man-pages updates and fixes (Fate: #6365)</changelog>
+<changelog author="- rhafer@suse.de" date="1138363200">- Updated to 2.3.19 (Bug #144371)</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rhafer@suse.de" date="1138190400">- Updated Admin Guide to latest version
+- build slapcat from openldap-2.2.24 and install it to
+  /usr/sbin/openldap-2.2-slapcat to be able to migrate from
+  OpenLDAP 2.2.
+- removed slapd-backbdb-dbupgrade which is no longer needed
+- attempt to dump/reload bdb databases in %{post}
+- Update notes in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1137153600">- New sysconfig variable OPENLDAP_KRB5_KEYTAB
+- Cleanup in default configuration and init scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1136980800">- Updated to 2.3.17
+- Remove OPENLDAP_RUN_DB_RECOVER from sysconfig file in %post
+  slapd does now automatically recover the database if needed
+- Removed unneeded README.SuSE
+- Small adjustments to the default DB_CONFIG file</changelog>
+<changelog author="- rhafer@suse.de" date="1136808000">- Updated to 2.3.16</changelog>
+<changelog author="- rhafer@suse.de" date="1134993600">- Fixed filelist (slapd-hdb man-page was missing)</changelog>
+<changelog author="- rhafer@suse.de" date="1134129600">- Fixed build on x86_64</changelog>
+<changelog author="- rhafer@suse.de" date="1133956800">- Merged -back-ldap and -back-monitor subpackages into the main
+  package and don't build them as dynamic modules anymore.
+- updated to OpenLDAP 2.3.13</changelog>
+<changelog author="- rhafer@suse.de" date="1133179200">- updated to OpenLDAP 2.3.12</changelog>
+<changelog author="- rhafer@suse.de" date="1130328000">- updated to OpenLDAP 2.3.11
+- removed the &quot;LDAP_DEPRECATED&quot; workaround</changelog>
+<changelog author="- rhafer@suse.de" date="1127736000">- Add &quot;LDAP_DEPRECATED&quot; to ldap.h for now</changelog>
+<changelog author="- rhafer@suse.de" date="1127476800">- updated to OpenLDAP 2.3.7</changelog>
+<changelog author="- rhafer@suse.de" date="1124193600">- allow start_tls while chasing referrals (Bug #94355, ITS #3791)</changelog>
+<changelog author="- rhafer@suse.de" date="1120478400">- devel-subpackage requires openldap2-client of the same version
+  (Bugzilla: #93579)</changelog>
+<changelog author="- uli@suse.de" date="1120132800">- build with -fPIE (not -fpie) to avoid GOT overflow on s390*</changelog>
+<changelog author="- rhafer@suse.de" date="1119441600">- build the server packages with -fpie/-pie</changelog>
+<changelog author="- rhafer@suse.de" date="1118836800">- updated to 2.2.27</changelog>
+<changelog author="- rhafer@suse.de" date="1117022400">- libldap-gethostbyname_r.dif: Use gethostbyname_r instead of
+  gethostbyname in libldap. Should fix host lookups through
+  nss_ldap (Bugzilla: #76173)</changelog>
+<changelog author="- rhafer@suse.de" date="1115985600">- Updated to 2.2.26
+- made /%{_libdir}]/sasl2/slapd.conf %config(noreplace)</changelog>
+<changelog author="- rhafer@suse.de" date="1114689600">- Added /%{_libdir}]/sasl2/slapd.conf to avoid warnings about
+  unconfigured OTP mechanism (Bugzilla: #80588)</changelog>
+<changelog author="- rhafer@suse.de" date="1113307200">- added minimal timeout to startproc in init-script to let it
+  report the &quot;failed&quot; status correctly in case of misconfiguration
+  (Bugzilla: #76393)</changelog>
+<changelog author="- rhafer@suse.de" date="1112616000">- crl-check.dif: Implements CRL checking on client and server side
+- use different base ports for differnt values of BUILD_INCARNATION
+  (/.buildenv) to allow parallel runs of the test-suite on a single
+  machine</changelog>
+<changelog author="- uli@suse.de" date="1112616000">- force yielding-select test to yes (test occasionally hangs QEMU)</changelog>
+<changelog author="- uli@suse.de" date="1112356800">- disable test suite on ARM (hangs QEMU)</changelog>
+<changelog author="- rhafer@suse.de" date="1112097600">- updated to 2.2.24
+- enabled back-hdb</changelog>
+<changelog author="- rhafer@suse.de" date="1109764800">- syncrepl.dif: merged latest syncrepl fixes (Bugzilla: #65928)
+- libldap-reinit-fdset.dif: Re-init fd_sets when select is
+  interupted (Bugzilla #50076, ITS: #3524)</changelog>
+<changelog author="- rhafer@suse.de" date="1108641600">- checkproc_before_recover.dif: Check if slapd is stopped before
+  running db_recover from the init script. (Bugzilla: #50962)</changelog>
+<changelog author="- rhafer@suse.de" date="1107259200">- Cleanup back-bdb databases in %post, db-4.3 changed the
+  transaction log format again.
+- cosmetic fixes in init script</changelog>
+<changelog author="- rhafer@suse.de" date="1106654400">- updated to 2.2.23
+- cleaned up #neededforbuild
+- package should also build on older SuSE Linux releases now
+- increased killproc timeout in init-script (Bugzilla: #47227)</changelog>
+<changelog author="- rhafer@suse.de" date="1105617600">- updated to 2.2.20
+- Removed unneeded dependencies</changelog>
+<changelog author="- kukuk@suse.de" date="1102680000">- don't install *.la files</changelog>
+<changelog author="- rhafer@suse.de" date="1100088000">- updated to 2.2.18
+- use kerberos-devel-packages in neededforbuild</changelog>
+<changelog author="- ro@suse.de" date="1096027200">- re-arranged specfile to sequence (header (package/descr)* rest)
+  so the checking parser is not confused ...</changelog>
+<changelog author="- rhafer@suse.de" date="1096027200">- Added pre_checkin.sh to generate a separate openldap2-client
+  spec-file from which the openldap2-client and openldap2-devel
+  subpackages are built. Should reduce build time for libldap as
+  the test-suite is only executed in openldap2.spec.</changelog>
+<changelog author="- rhafer@suse.de" date="1094817600">- libldap-result.dif: ldapsearch was hanging in select() when
+  retrieving results from eDirectory through a StartTLS protected
+  connection (Bugzilla #44942)</changelog>
+<changelog author="- dobey@suse.de" date="1092052800">- added ntlm support</changelog>
+<changelog author="- rhafer@suse.de" date="1091534400">- updated to 2.2.16
+- Updated ACLs in slapd_conf.dif to disable default read access
+  to the &quot;userPKCS12&quot; Attribute
+- rc-check-conn.diff: When starting slapd wait until is accepts
+  connections, or 10 seconds at maximum (Bugzilla #41354)
+- Backported -o slp={on|off} feature from OpenLDAP Head and added
+  new sysconfig variable (OPENLDAP_REGISTER_SLP) to be able
+  to switch SLP registration on and off. (Bugzilla #39865)
+- removed unneeded README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1083326400">- updated to 2.2.11
+- remove SLES8 update specific stuff
+- Bugzilla #39652: Updated slapd_conf.dif to contain basic access
+  control
+- Bugzilla #39468: Added missing items to yast.schema
+- fixed strict-aliasing compiler warnings (strict-aliasing.dif)</changelog>
+<changelog author="- coolo@suse.de" date="1083240000">- build with several jobs if available</changelog>
+<changelog author="- rhafer@suse.de" date="1082376000">- ldapi_url.dif: Fixed paths for LDAPI-socket, pid-file and
+  args-file (Bugzilla #38790)
+- ldbm_modrdn.dif: Fixed back-ldbm modrdn indexing bug (ITS #3059,
+  Bugzilla #38915)
+- modify_check_duplicates.dif: check for duplicate attribute
+  values in modify requests (ITS #3066/#3097, Bugzilla #38607)
+- updated and renamed yast2userconfig.schema to yast.schema as it
+  contains more that only user configuration now
+- syncrepl.dif: addtional fixes for syncrepl (ITS #3055, #3056)
+- test_syncrepl_timeout: increased sleep timeout in syncrepl
+  testsuite</changelog>
+<changelog author="- rhafer@suse.de" date="1080820800">- added &quot;TLS_REQCERT allow&quot; to /etc/openldap/ldap.conf, to make
+  START_TLS work without access to the CA Certificate.
+  (Bugzilla: #37393)</changelog>
+<changelog author="- rhafer@suse.de" date="1080302400">- fixed filelist
+- check-build.sh (build on kernel &gt;= 2.6.4 hosts only)
+- yast2user.schema / slapd.conf fixed (#37076)
+- don't check for TLS-options is init-script anymore (#33560)
+- fixed various typos in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1079524800">- fixed build of openldap-2.1-slapcat (using correct db41 include
+  files, build backends as on sles8)
+- attempt to update bdb database and reindex ldbm database in %{post}
+- Update notes in README.update
+- better default configuration (including default DB_CONFIG file)
+- misc updates for the YaST schema
+- fixed crasher in syncrepl-code (syncrepl.dif)</changelog>
+<changelog author="- schwab@suse.de" date="1079438400">- Fix type mismatch.</changelog>
+<changelog author="- rhafer@suse.de" date="1078228800">- updated to 2.2.6
+- build a openldap-2.1-slapcat from 2.1.25 sources  to be able to
+  migrate from SLES8 and SL 9.0</changelog>
+<changelog author="- ro@suse.de" date="1077192000">- added check-build.sh (build on 2.6 hosts only)</changelog>
+<changelog author="- rhafer@suse.de" date="1075982400">- updated to 2.2.5
+- adjusted rfc2307bis.schema to support UTF-8 values in most
+  attributes
+- enabled proxycache-overlay (wiht fix to work with back-ldbm)</changelog>
+<changelog author="- rhafer@suse.de" date="1073995200">- updated to 2.2.4
+- updated Admin Guide to most recent version</changelog>
+<changelog author="- adrian@suse.de" date="1073736000">- add %defattr
+- fix build as user</changelog>
+<changelog author="- rhafer@suse.de" date="1070884800">- updated to 2.1.25
+- small fixes for the YaST user schema</changelog>
+<changelog author="- rhafer@suse.de" date="1068552000">- enabled SLP-support</changelog>
+<changelog author="- kukuk@suse.de" date="1066392000">- Remove unused des from neededforbuild</changelog>
+<changelog author="- mt@suse.de" date="1062504000">- Bugzilla #29859: fixed typo in sysconfig metadata,
+  usage of OPENLDAP_LDAPS_INTERFACES in init script
+- added /usr/lib/sasl2/slapd.conf permissions handling
+- added sysconfig variable OPENLDAP_SLAPD_PARAMS=&quot;&quot;
+  to support additional slapd start parameters
+- added sysconfig variable OPENLDAP_START_LDAPI=NO/yes
+  for ldapi:/// (LDAP over IPC) URLs</changelog>
+<changelog author="- rhafer@suse.de" date="1060862400">- added activation metadata to sysconfig template (Bugzilla #28911)
+- removed lint from specfile</changelog>
+<changelog author="- rhafer@suse.de" date="1060257600">- added %stop_on_removal and %restart_on_update calls
+- bdb_addcnt.dif fixes a possible endless loop in id2entry()
+- addonschema.tar.gz: some extra Schema files (YaST, RFC2307bis)</changelog>
+<changelog author="- rhafer@suse.de" date="1058356800">- removed fillup_only and call fillup_and_insserv correctly
+- new Options in sysconfig.openldap: OPENLDAP_LDAP_INTERFACES,
+  OPENLDAP_LDAPS_INTERFACES and OPENLDAP_RUN_DB_RECOVER</changelog>
+<changelog author="- rhafer@suse.de" date="1057060800">- updated to 2.1.22
+- updated Admin Guide to most recent version
+- build librewrite with -fPIC</changelog>
+<changelog author="- rhafer@suse.de" date="1055764800">- updated to 2.1.21</changelog>
+<changelog author="- ro@suse.de" date="1055332800">- fixed requires lines</changelog>
+<changelog author="- rhafer@suse.de" date="1053950400">- don't link back-ldap against librewrite.a, it's already linked
+  into slapd (package should build on non-i386 Archs again)</changelog>
+<changelog author="- rhafer@suse.de" date="1053691200">- fixed dynamic build of back-ldap
+- new subpackage back-ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1053432000">- updated to version 2.1.20
+- enabled dynamic backend modules
+- new subpackages back-perl, back-meta and back-monitor
+- remove unpacked files from BuildRoot</changelog>
+<changelog author="- rhafer@suse.de" date="1052481600">- updated to version 2.1.19</changelog>
+<changelog author="- ro@suse.de" date="1050494400">- fixed requires for devel-package ...</changelog>
+<changelog author="- ro@suse.de" date="1050408000">- fixed neededforbuild</changelog>
+<changelog author="- kukuk@suse.de" date="1045137600">- Enable IPv6 again</changelog>
+<changelog author="- rhafer@suse.de" date="1044964800">- added /etc/openldap to filelist</changelog>
+<changelog author="- rhafer@suse.de" date="1044273600">- switch default backend to ldbm</changelog>
+<changelog author="- ro@suse.de" date="1044187200">- fixed requires for devel package (cyrus-sasl2-devel)</changelog>
+<changelog author="- rhafer@suse.de" date="1044014400">- liblber.dif: Fixes two bugs in liblber by which remote attackers
+  could crash the LDAP server (Bugzilla #22469, OpenLDAP ITS #2275
+  and #2280)</changelog>
+<changelog author="- choeger@suse.de" date="1042545600">- build using sasl2</changelog>
+<changelog author="- rhafer@suse.de" date="1042459200">- updated to version 2.1.12
+- added metadata to sysconfig template (Bug: #22666)</changelog>
+<changelog author="- rhafer@suse.de" date="1038484800">- updated to version 2.1.8
+- added additional fix of 64bit archs
+- added secpatch.dif to fix setuid issues in libldap</changelog>
+<changelog author="- rhafer@suse.de" date="1031313600">- fix for Bugzilla ID #18981, chown to OPENLDAP_USER didn't work
+  with multiple database backend directories</changelog>
+<changelog author="- rhafer@suse.de" date="1030968000">- removed damoenstart_ipv6.diff and disabled IPv6 support due to
+  massive problems with nss_ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1030363200">- ldap_user.dif: slapd is now run a the user/group ldap (Bugzilla
+  ID#17697)</changelog>
+<changelog author="- rhafer@suse.de" date="1030104000">- updated to version 2.1.4, which fixes tons of bugs
+- added damoenstart_ipv6.diff (slapd was not starting when
+  configured to listen on IPv4 and IPv6 interfaces, as done by the
+  start script)
+- added README.SuSE with some hints about the bdb-backend
+- updated filelist to include only the man pages of the backends,
+  that were built</changelog>
+<changelog author="- rhafer@suse.de" date="1029412800">- removed termcap and readline from neededforbuild</changelog>
+<changelog author="- rhafer@suse.de" date="1028808000">- enabled {CRYPT} passwords
+- update filelist (added new manpages)</changelog>
+<changelog author="- rhafer@suse.de" date="1027598400">- patches for 64 bit architectures</changelog>
+<changelog author="- rhafer@suse.de" date="1027080000">- update to 2.1.3</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- fix openldap2-devel requires</changelog>
+<changelog author="- rhafer@suse.de" date="1025784000">- switched back from cyrus-sasl2 to cyrus-sasl</changelog>
+<changelog author="- rhafer@suse.de" date="1025697600">- updated to OpenLDAP 2.1.2
+- added the OpenLDAP Administration Guide
+- enabled additional backends (ldap, meta, monitor)</changelog>
+<changelog author="- olh@suse.de" date="1023710400">- hack build/ltconfig to build shared libs on ppc64</changelog>
+<changelog author="- rhafer@suse.de" date="1023278400">- created /etc/sysconfig/openldap and OPENLDAP_START_LDAPS variable
+  to enable ldap over ssl support</changelog>
+<changelog author="- rhafer@suse.de" date="1015502400">- Fix for Bugzilla ID#14569 (added cyrus-sasl-devel openssl-devel
+  to the &quot;Requires&quot; Section of the -devel subpackage)</changelog>
+<changelog author="- rhafer@suse.de" date="1014033600">- updated to the latest STABLE release (2.0.23) which fixes some
+  nasty bugs see ITS #1562,#1582,#1577,#1578</changelog>
+<changelog author="- rhafer@suse.de" date="1013083200">- updated to the latest release (which fixes a index corruption
+  bug)
+- cleanup in neededforbuild
+- small fixes for the init-scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1011268800">- updated to the latest stable release (2.0.21)</changelog>
+<changelog author="- egmont@suselinux.hu" date="1011182400">- removed periods and colons from startup/shutdown messages</changelog>
+<changelog author="- rhafer@suse.de" date="1011096000">- updated to v2.0.20 (which fixes a security hole in ACL
+  processing)</changelog>
+<changelog author="- rhafer@suse.de" date="1010750400">- converted archive to bzip2
+- makes use of %{_libdir} now
+- set CFLAGS to -O0 for archs ia64, s390(x) and alpha otherwise
+  the test suite fails on these archs
+- changed slapd.conf to store the database under /var/lib/ldap
+  (this patch was missing in the last versions by accident)</changelog>
+<changelog author="- rhafer@suse.de" date="1010404800">- update to v2.0.19</changelog>
+<changelog author="- rhafer@suse.de" date="1007640000">- eliminated START_LDAP, START_SLURPD variables in rc.config
+- created separate init script for slurpd
+- moved init scripts from dif to separate source tgz</changelog>
+<changelog author="- choeger@suse.de" date="1004097600">- update to v2.0.18</changelog>
+<changelog author="- choeger@suse.de" date="1003147200">- update to v2.0.17
+  added a sleep to the restart section
+  moved some manpages to the client package</changelog>
+<changelog author="- choeger@suse.de" date="1001937600">- update to v2.0.15</changelog>
+<changelog author="- choeger@suse.de" date="1000296000">- backported the full bugfix from openldap-2.0.14</changelog>
+<changelog author="- choeger@suse.de" date="1000209600">- Bugfix for slurpd millionth second bug (ITS#1323)</changelog>
+<changelog author="- choeger@suse.de" date="1000123200">- moved ldapfilter.conf ldaptemplates.conf ldapsearchprefs.conf
+  to openldap2-client package</changelog>
+<changelog author="- choeger@suse.de" date="999518400">- update to version 2.0.12</changelog>
+<changelog author="- choeger@suse.de" date="994075200">- bugfix: init script was not LSB compliant, Bugzilla ID#9072</changelog>
+<changelog author="- ro@suse.de" date="992952000">- fixed for autoconf again</changelog>
+<changelog author="- choeger@suse.de" date="992606400">- update to 2.0.11
+- removed autoconf in specfile, because it doesn't work</changelog>
+<changelog author="- choeger@suse.de" date="990619200">- update to version 2.0.10 (minor fixes)</changelog>
+<changelog author="- choeger@suse.de" date="990532800">- update to version 2.0.9</changelog>
+<changelog author="- choeger@suse.de" date="988027200">- removed kerberos support
+- added aci support</changelog>
+<changelog author="- choeger@suse.de" date="987768000">- added kerberos support</changelog>
+<changelog author="- choeger@suse.de" date="986472000">- moved section 5 and 8 manpages to the server part of package</changelog>
+<changelog author="- kukuk@suse.de" date="984571200">- Move *.so links into -devel package
+- -devel requires -client</changelog>
+<changelog author="- choeger@suse.de" date="984052800">- split up into openldap2-client and -devel</changelog>
+<changelog author="- ro@suse.de" date="983275200">- changed neededforbuild &lt;cyrus-sasl&gt; to &lt;cyrus-sasl cyrus-sasl-devel&gt;</changelog>
+<changelog author="- ro@suse.de" date="982929600">- added readline/readline-devel to neededforbuild (split from bash)</changelog>
+<changelog author="- choeger@suse.de" date="978609600">- bugfix: slapd.conf rename /var/lib/openldap-ldbm to
+  /var/lib/ldap
+  init script: use $remote_fs</changelog>
+<changelog author="- olh@suse.de" date="978436800">- use script name in %post</changelog>
+<changelog author="- choeger@suse.de" date="976190400">- bugfix from Andreas Jaeger:
+  workaround for glibc2.2, detach</changelog>
+<changelog author="- ro@suse.de" date="975672000">- hacked configure for apparently broken pthread</changelog>
+<changelog author="- ro@suse.de" date="975672000">- fixed spec</changelog>
+<changelog author="- choeger@suse.de" date="974980800">- made configs %config(noreplace) (Bug 4112)
+- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="974894400">- adopted new init scheme</changelog>
+<changelog author="- choeger@suse.de" date="974289600">- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="973857600">- added buildroot</changelog>
+<changelog author="- choeger@suse.de" date="973598400">- long package name
+- new version, 2.0.7</changelog>
+<changelog author="- choeger@suse.de" date="970833600">- first package of openldap2 (v2.0.6)</changelog>
+</package>
+
+
+
+
+
+<package pkgid="2727339181872edbaf13c007c607bc11a14d292c" name="openldap2" arch="src">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+<changelog author="- rhafer@suse.de" date="1147262400">- Really apply the patch for Bug#160566
+- slapd could crash while processing queries with pre-/postread
+  controls (Bug#173877, ITS#4532)</changelog>
+<changelog author="- rhafer@suse.de" date="1143201600">- Backported fix from CVS for occasional crashes in referral
+  chasing code (as used in e.g. back-meta/back-ldap).
+  (Bug: #160566, ITS: #4448)</changelog>
+<changelog author="- rhafer@suse.de" date="1142251200">- openldap2 must obsolete -back-monitor and -back-ldap to have them
+  removed during update (Bug: #157576)</changelog>
+<changelog author="- rhafer@suse.de" date="1140177600">- Add &quot;external&quot; to the list of supported SASL mechanisms
+  (Bug: #151771)</changelog>
+<changelog author="- rhafer@suse.de" date="1140091200">- Error out when conversion from old configfile to config database
+  fails (Bug: #135484,#135490 ITS: #4407)</changelog>
+<changelog author="- rhafer@suse.de" date="1139832000">- Don't ignore non-read/write epoll events (Bug: #149993,
+  ITS: #4395)
+- Added update message to /usr/share/update-messages/en/ and enable
+  it, when update did not succeed.</changelog>
+<changelog author="- rhafer@suse.de" date="1139486400">- OPENLDAP_CHOWN_DIRS honors databases defined in include files
+  (Bug: #135473)
+- Fixed version numbers in README.update
+- Fixed GSSAPI binds against Active Directory (Bug: #149390)</changelog>
+<changelog author="- rhafer@suse.de" date="1138968000">- Cleaned up update procedure
+- man-pages updates and fixes (Fate: #6365)</changelog>
+<changelog author="- rhafer@suse.de" date="1138363200">- Updated to 2.3.19 (Bug #144371)</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rhafer@suse.de" date="1138190400">- Updated Admin Guide to latest version
+- build slapcat from openldap-2.2.24 and install it to
+  /usr/sbin/openldap-2.2-slapcat to be able to migrate from
+  OpenLDAP 2.2.
+- removed slapd-backbdb-dbupgrade which is no longer needed
+- attempt to dump/reload bdb databases in %{post}
+- Update notes in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1137153600">- New sysconfig variable OPENLDAP_KRB5_KEYTAB
+- Cleanup in default configuration and init scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1136980800">- Updated to 2.3.17
+- Remove OPENLDAP_RUN_DB_RECOVER from sysconfig file in %post
+  slapd does now automatically recover the database if needed
+- Removed unneeded README.SuSE
+- Small adjustments to the default DB_CONFIG file</changelog>
+<changelog author="- rhafer@suse.de" date="1136808000">- Updated to 2.3.16</changelog>
+<changelog author="- rhafer@suse.de" date="1134993600">- Fixed filelist (slapd-hdb man-page was missing)</changelog>
+<changelog author="- rhafer@suse.de" date="1134129600">- Fixed build on x86_64</changelog>
+<changelog author="- rhafer@suse.de" date="1133956800">- Merged -back-ldap and -back-monitor subpackages into the main
+  package and don't build them as dynamic modules anymore.
+- updated to OpenLDAP 2.3.13</changelog>
+<changelog author="- rhafer@suse.de" date="1133179200">- updated to OpenLDAP 2.3.12</changelog>
+<changelog author="- rhafer@suse.de" date="1130328000">- updated to OpenLDAP 2.3.11
+- removed the &quot;LDAP_DEPRECATED&quot; workaround</changelog>
+<changelog author="- rhafer@suse.de" date="1127736000">- Add &quot;LDAP_DEPRECATED&quot; to ldap.h for now</changelog>
+<changelog author="- rhafer@suse.de" date="1127476800">- updated to OpenLDAP 2.3.7</changelog>
+<changelog author="- rhafer@suse.de" date="1124193600">- allow start_tls while chasing referrals (Bug #94355, ITS #3791)</changelog>
+<changelog author="- rhafer@suse.de" date="1120478400">- devel-subpackage requires openldap2-client of the same version
+  (Bugzilla: #93579)</changelog>
+<changelog author="- uli@suse.de" date="1120132800">- build with -fPIE (not -fpie) to avoid GOT overflow on s390*</changelog>
+<changelog author="- rhafer@suse.de" date="1119441600">- build the server packages with -fpie/-pie</changelog>
+<changelog author="- rhafer@suse.de" date="1118836800">- updated to 2.2.27</changelog>
+<changelog author="- rhafer@suse.de" date="1117022400">- libldap-gethostbyname_r.dif: Use gethostbyname_r instead of
+  gethostbyname in libldap. Should fix host lookups through
+  nss_ldap (Bugzilla: #76173)</changelog>
+<changelog author="- rhafer@suse.de" date="1115985600">- Updated to 2.2.26
+- made /%{_libdir}]/sasl2/slapd.conf %config(noreplace)</changelog>
+<changelog author="- rhafer@suse.de" date="1114689600">- Added /%{_libdir}]/sasl2/slapd.conf to avoid warnings about
+  unconfigured OTP mechanism (Bugzilla: #80588)</changelog>
+<changelog author="- rhafer@suse.de" date="1113307200">- added minimal timeout to startproc in init-script to let it
+  report the &quot;failed&quot; status correctly in case of misconfiguration
+  (Bugzilla: #76393)</changelog>
+<changelog author="- rhafer@suse.de" date="1112616000">- crl-check.dif: Implements CRL checking on client and server side
+- use different base ports for differnt values of BUILD_INCARNATION
+  (/.buildenv) to allow parallel runs of the test-suite on a single
+  machine</changelog>
+<changelog author="- uli@suse.de" date="1112616000">- force yielding-select test to yes (test occasionally hangs QEMU)</changelog>
+<changelog author="- uli@suse.de" date="1112356800">- disable test suite on ARM (hangs QEMU)</changelog>
+<changelog author="- rhafer@suse.de" date="1112097600">- updated to 2.2.24
+- enabled back-hdb</changelog>
+<changelog author="- rhafer@suse.de" date="1109764800">- syncrepl.dif: merged latest syncrepl fixes (Bugzilla: #65928)
+- libldap-reinit-fdset.dif: Re-init fd_sets when select is
+  interupted (Bugzilla #50076, ITS: #3524)</changelog>
+<changelog author="- rhafer@suse.de" date="1108641600">- checkproc_before_recover.dif: Check if slapd is stopped before
+  running db_recover from the init script. (Bugzilla: #50962)</changelog>
+<changelog author="- rhafer@suse.de" date="1107259200">- Cleanup back-bdb databases in %post, db-4.3 changed the
+  transaction log format again.
+- cosmetic fixes in init script</changelog>
+<changelog author="- rhafer@suse.de" date="1106654400">- updated to 2.2.23
+- cleaned up #neededforbuild
+- package should also build on older SuSE Linux releases now
+- increased killproc timeout in init-script (Bugzilla: #47227)</changelog>
+<changelog author="- rhafer@suse.de" date="1105617600">- updated to 2.2.20
+- Removed unneeded dependencies</changelog>
+<changelog author="- kukuk@suse.de" date="1102680000">- don't install *.la files</changelog>
+<changelog author="- rhafer@suse.de" date="1100088000">- updated to 2.2.18
+- use kerberos-devel-packages in neededforbuild</changelog>
+<changelog author="- ro@suse.de" date="1096027200">- re-arranged specfile to sequence (header (package/descr)* rest)
+  so the checking parser is not confused ...</changelog>
+<changelog author="- rhafer@suse.de" date="1096027200">- Added pre_checkin.sh to generate a separate openldap2-client
+  spec-file from which the openldap2-client and openldap2-devel
+  subpackages are built. Should reduce build time for libldap as
+  the test-suite is only executed in openldap2.spec.</changelog>
+<changelog author="- rhafer@suse.de" date="1094817600">- libldap-result.dif: ldapsearch was hanging in select() when
+  retrieving results from eDirectory through a StartTLS protected
+  connection (Bugzilla #44942)</changelog>
+<changelog author="- dobey@suse.de" date="1092052800">- added ntlm support</changelog>
+<changelog author="- rhafer@suse.de" date="1091534400">- updated to 2.2.16
+- Updated ACLs in slapd_conf.dif to disable default read access
+  to the &quot;userPKCS12&quot; Attribute
+- rc-check-conn.diff: When starting slapd wait until is accepts
+  connections, or 10 seconds at maximum (Bugzilla #41354)
+- Backported -o slp={on|off} feature from OpenLDAP Head and added
+  new sysconfig variable (OPENLDAP_REGISTER_SLP) to be able
+  to switch SLP registration on and off. (Bugzilla #39865)
+- removed unneeded README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1083326400">- updated to 2.2.11
+- remove SLES8 update specific stuff
+- Bugzilla #39652: Updated slapd_conf.dif to contain basic access
+  control
+- Bugzilla #39468: Added missing items to yast.schema
+- fixed strict-aliasing compiler warnings (strict-aliasing.dif)</changelog>
+<changelog author="- coolo@suse.de" date="1083240000">- build with several jobs if available</changelog>
+<changelog author="- rhafer@suse.de" date="1082376000">- ldapi_url.dif: Fixed paths for LDAPI-socket, pid-file and
+  args-file (Bugzilla #38790)
+- ldbm_modrdn.dif: Fixed back-ldbm modrdn indexing bug (ITS #3059,
+  Bugzilla #38915)
+- modify_check_duplicates.dif: check for duplicate attribute
+  values in modify requests (ITS #3066/#3097, Bugzilla #38607)
+- updated and renamed yast2userconfig.schema to yast.schema as it
+  contains more that only user configuration now
+- syncrepl.dif: addtional fixes for syncrepl (ITS #3055, #3056)
+- test_syncrepl_timeout: increased sleep timeout in syncrepl
+  testsuite</changelog>
+<changelog author="- rhafer@suse.de" date="1080820800">- added &quot;TLS_REQCERT allow&quot; to /etc/openldap/ldap.conf, to make
+  START_TLS work without access to the CA Certificate.
+  (Bugzilla: #37393)</changelog>
+<changelog author="- rhafer@suse.de" date="1080302400">- fixed filelist
+- check-build.sh (build on kernel &gt;= 2.6.4 hosts only)
+- yast2user.schema / slapd.conf fixed (#37076)
+- don't check for TLS-options is init-script anymore (#33560)
+- fixed various typos in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1079524800">- fixed build of openldap-2.1-slapcat (using correct db41 include
+  files, build backends as on sles8)
+- attempt to update bdb database and reindex ldbm database in %{post}
+- Update notes in README.update
+- better default configuration (including default DB_CONFIG file)
+- misc updates for the YaST schema
+- fixed crasher in syncrepl-code (syncrepl.dif)</changelog>
+<changelog author="- schwab@suse.de" date="1079438400">- Fix type mismatch.</changelog>
+<changelog author="- rhafer@suse.de" date="1078228800">- updated to 2.2.6
+- build a openldap-2.1-slapcat from 2.1.25 sources  to be able to
+  migrate from SLES8 and SL 9.0</changelog>
+<changelog author="- ro@suse.de" date="1077192000">- added check-build.sh (build on 2.6 hosts only)</changelog>
+<changelog author="- rhafer@suse.de" date="1075982400">- updated to 2.2.5
+- adjusted rfc2307bis.schema to support UTF-8 values in most
+  attributes
+- enabled proxycache-overlay (wiht fix to work with back-ldbm)</changelog>
+<changelog author="- rhafer@suse.de" date="1073995200">- updated to 2.2.4
+- updated Admin Guide to most recent version</changelog>
+<changelog author="- adrian@suse.de" date="1073736000">- add %defattr
+- fix build as user</changelog>
+<changelog author="- rhafer@suse.de" date="1070884800">- updated to 2.1.25
+- small fixes for the YaST user schema</changelog>
+<changelog author="- rhafer@suse.de" date="1068552000">- enabled SLP-support</changelog>
+<changelog author="- kukuk@suse.de" date="1066392000">- Remove unused des from neededforbuild</changelog>
+<changelog author="- mt@suse.de" date="1062504000">- Bugzilla #29859: fixed typo in sysconfig metadata,
+  usage of OPENLDAP_LDAPS_INTERFACES in init script
+- added /usr/lib/sasl2/slapd.conf permissions handling
+- added sysconfig variable OPENLDAP_SLAPD_PARAMS=&quot;&quot;
+  to support additional slapd start parameters
+- added sysconfig variable OPENLDAP_START_LDAPI=NO/yes
+  for ldapi:/// (LDAP over IPC) URLs</changelog>
+<changelog author="- rhafer@suse.de" date="1060862400">- added activation metadata to sysconfig template (Bugzilla #28911)
+- removed lint from specfile</changelog>
+<changelog author="- rhafer@suse.de" date="1060257600">- added %stop_on_removal and %restart_on_update calls
+- bdb_addcnt.dif fixes a possible endless loop in id2entry()
+- addonschema.tar.gz: some extra Schema files (YaST, RFC2307bis)</changelog>
+<changelog author="- rhafer@suse.de" date="1058356800">- removed fillup_only and call fillup_and_insserv correctly
+- new Options in sysconfig.openldap: OPENLDAP_LDAP_INTERFACES,
+  OPENLDAP_LDAPS_INTERFACES and OPENLDAP_RUN_DB_RECOVER</changelog>
+<changelog author="- rhafer@suse.de" date="1057060800">- updated to 2.1.22
+- updated Admin Guide to most recent version
+- build librewrite with -fPIC</changelog>
+<changelog author="- rhafer@suse.de" date="1055764800">- updated to 2.1.21</changelog>
+<changelog author="- ro@suse.de" date="1055332800">- fixed requires lines</changelog>
+<changelog author="- rhafer@suse.de" date="1053950400">- don't link back-ldap against librewrite.a, it's already linked
+  into slapd (package should build on non-i386 Archs again)</changelog>
+<changelog author="- rhafer@suse.de" date="1053691200">- fixed dynamic build of back-ldap
+- new subpackage back-ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1053432000">- updated to version 2.1.20
+- enabled dynamic backend modules
+- new subpackages back-perl, back-meta and back-monitor
+- remove unpacked files from BuildRoot</changelog>
+<changelog author="- rhafer@suse.de" date="1052481600">- updated to version 2.1.19</changelog>
+<changelog author="- ro@suse.de" date="1050494400">- fixed requires for devel-package ...</changelog>
+<changelog author="- ro@suse.de" date="1050408000">- fixed neededforbuild</changelog>
+<changelog author="- kukuk@suse.de" date="1045137600">- Enable IPv6 again</changelog>
+<changelog author="- rhafer@suse.de" date="1044964800">- added /etc/openldap to filelist</changelog>
+<changelog author="- rhafer@suse.de" date="1044273600">- switch default backend to ldbm</changelog>
+<changelog author="- ro@suse.de" date="1044187200">- fixed requires for devel package (cyrus-sasl2-devel)</changelog>
+<changelog author="- rhafer@suse.de" date="1044014400">- liblber.dif: Fixes two bugs in liblber by which remote attackers
+  could crash the LDAP server (Bugzilla #22469, OpenLDAP ITS #2275
+  and #2280)</changelog>
+<changelog author="- choeger@suse.de" date="1042545600">- build using sasl2</changelog>
+<changelog author="- rhafer@suse.de" date="1042459200">- updated to version 2.1.12
+- added metadata to sysconfig template (Bug: #22666)</changelog>
+<changelog author="- rhafer@suse.de" date="1038484800">- updated to version 2.1.8
+- added additional fix of 64bit archs
+- added secpatch.dif to fix setuid issues in libldap</changelog>
+<changelog author="- rhafer@suse.de" date="1031313600">- fix for Bugzilla ID #18981, chown to OPENLDAP_USER didn't work
+  with multiple database backend directories</changelog>
+<changelog author="- rhafer@suse.de" date="1030968000">- removed damoenstart_ipv6.diff and disabled IPv6 support due to
+  massive problems with nss_ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1030363200">- ldap_user.dif: slapd is now run a the user/group ldap (Bugzilla
+  ID#17697)</changelog>
+<changelog author="- rhafer@suse.de" date="1030104000">- updated to version 2.1.4, which fixes tons of bugs
+- added damoenstart_ipv6.diff (slapd was not starting when
+  configured to listen on IPv4 and IPv6 interfaces, as done by the
+  start script)
+- added README.SuSE with some hints about the bdb-backend
+- updated filelist to include only the man pages of the backends,
+  that were built</changelog>
+<changelog author="- rhafer@suse.de" date="1029412800">- removed termcap and readline from neededforbuild</changelog>
+<changelog author="- rhafer@suse.de" date="1028808000">- enabled {CRYPT} passwords
+- update filelist (added new manpages)</changelog>
+<changelog author="- rhafer@suse.de" date="1027598400">- patches for 64 bit architectures</changelog>
+<changelog author="- rhafer@suse.de" date="1027080000">- update to 2.1.3</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- fix openldap2-devel requires</changelog>
+<changelog author="- rhafer@suse.de" date="1025784000">- switched back from cyrus-sasl2 to cyrus-sasl</changelog>
+<changelog author="- rhafer@suse.de" date="1025697600">- updated to OpenLDAP 2.1.2
+- added the OpenLDAP Administration Guide
+- enabled additional backends (ldap, meta, monitor)</changelog>
+<changelog author="- olh@suse.de" date="1023710400">- hack build/ltconfig to build shared libs on ppc64</changelog>
+<changelog author="- rhafer@suse.de" date="1023278400">- created /etc/sysconfig/openldap and OPENLDAP_START_LDAPS variable
+  to enable ldap over ssl support</changelog>
+<changelog author="- rhafer@suse.de" date="1015502400">- Fix for Bugzilla ID#14569 (added cyrus-sasl-devel openssl-devel
+  to the &quot;Requires&quot; Section of the -devel subpackage)</changelog>
+<changelog author="- rhafer@suse.de" date="1014033600">- updated to the latest STABLE release (2.0.23) which fixes some
+  nasty bugs see ITS #1562,#1582,#1577,#1578</changelog>
+<changelog author="- rhafer@suse.de" date="1013083200">- updated to the latest release (which fixes a index corruption
+  bug)
+- cleanup in neededforbuild
+- small fixes for the init-scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1011268800">- updated to the latest stable release (2.0.21)</changelog>
+<changelog author="- egmont@suselinux.hu" date="1011182400">- removed periods and colons from startup/shutdown messages</changelog>
+<changelog author="- rhafer@suse.de" date="1011096000">- updated to v2.0.20 (which fixes a security hole in ACL
+  processing)</changelog>
+<changelog author="- rhafer@suse.de" date="1010750400">- converted archive to bzip2
+- makes use of %{_libdir} now
+- set CFLAGS to -O0 for archs ia64, s390(x) and alpha otherwise
+  the test suite fails on these archs
+- changed slapd.conf to store the database under /var/lib/ldap
+  (this patch was missing in the last versions by accident)</changelog>
+<changelog author="- rhafer@suse.de" date="1010404800">- update to v2.0.19</changelog>
+<changelog author="- rhafer@suse.de" date="1007640000">- eliminated START_LDAP, START_SLURPD variables in rc.config
+- created separate init script for slurpd
+- moved init scripts from dif to separate source tgz</changelog>
+<changelog author="- choeger@suse.de" date="1004097600">- update to v2.0.18</changelog>
+<changelog author="- choeger@suse.de" date="1003147200">- update to v2.0.17
+  added a sleep to the restart section
+  moved some manpages to the client package</changelog>
+<changelog author="- choeger@suse.de" date="1001937600">- update to v2.0.15</changelog>
+<changelog author="- choeger@suse.de" date="1000296000">- backported the full bugfix from openldap-2.0.14</changelog>
+<changelog author="- choeger@suse.de" date="1000209600">- Bugfix for slurpd millionth second bug (ITS#1323)</changelog>
+<changelog author="- choeger@suse.de" date="1000123200">- moved ldapfilter.conf ldaptemplates.conf ldapsearchprefs.conf
+  to openldap2-client package</changelog>
+<changelog author="- choeger@suse.de" date="999518400">- update to version 2.0.12</changelog>
+<changelog author="- choeger@suse.de" date="994075200">- bugfix: init script was not LSB compliant, Bugzilla ID#9072</changelog>
+<changelog author="- ro@suse.de" date="992952000">- fixed for autoconf again</changelog>
+<changelog author="- choeger@suse.de" date="992606400">- update to 2.0.11
+- removed autoconf in specfile, because it doesn't work</changelog>
+<changelog author="- choeger@suse.de" date="990619200">- update to version 2.0.10 (minor fixes)</changelog>
+<changelog author="- choeger@suse.de" date="990532800">- update to version 2.0.9</changelog>
+<changelog author="- choeger@suse.de" date="988027200">- removed kerberos support
+- added aci support</changelog>
+<changelog author="- choeger@suse.de" date="987768000">- added kerberos support</changelog>
+<changelog author="- choeger@suse.de" date="986472000">- moved section 5 and 8 manpages to the server part of package</changelog>
+<changelog author="- kukuk@suse.de" date="984571200">- Move *.so links into -devel package
+- -devel requires -client</changelog>
+<changelog author="- choeger@suse.de" date="984052800">- split up into openldap2-client and -devel</changelog>
+<changelog author="- ro@suse.de" date="983275200">- changed neededforbuild &lt;cyrus-sasl&gt; to &lt;cyrus-sasl cyrus-sasl-devel&gt;</changelog>
+<changelog author="- ro@suse.de" date="982929600">- added readline/readline-devel to neededforbuild (split from bash)</changelog>
+<changelog author="- choeger@suse.de" date="978609600">- bugfix: slapd.conf rename /var/lib/openldap-ldbm to
+  /var/lib/ldap
+  init script: use $remote_fs</changelog>
+<changelog author="- olh@suse.de" date="978436800">- use script name in %post</changelog>
+<changelog author="- choeger@suse.de" date="976190400">- bugfix from Andreas Jaeger:
+  workaround for glibc2.2, detach</changelog>
+<changelog author="- ro@suse.de" date="975672000">- hacked configure for apparently broken pthread</changelog>
+<changelog author="- ro@suse.de" date="975672000">- fixed spec</changelog>
+<changelog author="- choeger@suse.de" date="974980800">- made configs %config(noreplace) (Bug 4112)
+- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="974894400">- adopted new init scheme</changelog>
+<changelog author="- choeger@suse.de" date="974289600">- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="973857600">- added buildroot</changelog>
+<changelog author="- choeger@suse.de" date="973598400">- long package name
+- new version, 2.0.7</changelog>
+<changelog author="- choeger@suse.de" date="970833600">- first package of openldap2 (v2.0.6)</changelog>
+</package>
+
+
+
+
+
+<package pkgid="3bb100100080c39f059b055ff2f8e96135f5e721" name="openldap2" arch="x86_64">
+<version epoch="0" ver="2.3.19" rel="18.3"/>
+<changelog author="- rhafer@suse.de" date="1147262400">- Really apply the patch for Bug#160566
+- slapd could crash while processing queries with pre-/postread
+  controls (Bug#173877, ITS#4532)</changelog>
+<changelog author="- rhafer@suse.de" date="1143201600">- Backported fix from CVS for occasional crashes in referral
+  chasing code (as used in e.g. back-meta/back-ldap).
+  (Bug: #160566, ITS: #4448)</changelog>
+<changelog author="- rhafer@suse.de" date="1142251200">- openldap2 must obsolete -back-monitor and -back-ldap to have them
+  removed during update (Bug: #157576)</changelog>
+<changelog author="- rhafer@suse.de" date="1140177600">- Add &quot;external&quot; to the list of supported SASL mechanisms
+  (Bug: #151771)</changelog>
+<changelog author="- rhafer@suse.de" date="1140091200">- Error out when conversion from old configfile to config database
+  fails (Bug: #135484,#135490 ITS: #4407)</changelog>
+<changelog author="- rhafer@suse.de" date="1139832000">- Don't ignore non-read/write epoll events (Bug: #149993,
+  ITS: #4395)
+- Added update message to /usr/share/update-messages/en/ and enable
+  it, when update did not succeed.</changelog>
+<changelog author="- rhafer@suse.de" date="1139486400">- OPENLDAP_CHOWN_DIRS honors databases defined in include files
+  (Bug: #135473)
+- Fixed version numbers in README.update
+- Fixed GSSAPI binds against Active Directory (Bug: #149390)</changelog>
+<changelog author="- rhafer@suse.de" date="1138968000">- Cleaned up update procedure
+- man-pages updates and fixes (Fate: #6365)</changelog>
+<changelog author="- rhafer@suse.de" date="1138363200">- Updated to 2.3.19 (Bug #144371)</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- rhafer@suse.de" date="1138190400">- Updated Admin Guide to latest version
+- build slapcat from openldap-2.2.24 and install it to
+  /usr/sbin/openldap-2.2-slapcat to be able to migrate from
+  OpenLDAP 2.2.
+- removed slapd-backbdb-dbupgrade which is no longer needed
+- attempt to dump/reload bdb databases in %{post}
+- Update notes in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1137153600">- New sysconfig variable OPENLDAP_KRB5_KEYTAB
+- Cleanup in default configuration and init scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1136980800">- Updated to 2.3.17
+- Remove OPENLDAP_RUN_DB_RECOVER from sysconfig file in %post
+  slapd does now automatically recover the database if needed
+- Removed unneeded README.SuSE
+- Small adjustments to the default DB_CONFIG file</changelog>
+<changelog author="- rhafer@suse.de" date="1136808000">- Updated to 2.3.16</changelog>
+<changelog author="- rhafer@suse.de" date="1134993600">- Fixed filelist (slapd-hdb man-page was missing)</changelog>
+<changelog author="- rhafer@suse.de" date="1134129600">- Fixed build on x86_64</changelog>
+<changelog author="- rhafer@suse.de" date="1133956800">- Merged -back-ldap and -back-monitor subpackages into the main
+  package and don't build them as dynamic modules anymore.
+- updated to OpenLDAP 2.3.13</changelog>
+<changelog author="- rhafer@suse.de" date="1133179200">- updated to OpenLDAP 2.3.12</changelog>
+<changelog author="- rhafer@suse.de" date="1130328000">- updated to OpenLDAP 2.3.11
+- removed the &quot;LDAP_DEPRECATED&quot; workaround</changelog>
+<changelog author="- rhafer@suse.de" date="1127736000">- Add &quot;LDAP_DEPRECATED&quot; to ldap.h for now</changelog>
+<changelog author="- rhafer@suse.de" date="1127476800">- updated to OpenLDAP 2.3.7</changelog>
+<changelog author="- rhafer@suse.de" date="1124193600">- allow start_tls while chasing referrals (Bug #94355, ITS #3791)</changelog>
+<changelog author="- rhafer@suse.de" date="1120478400">- devel-subpackage requires openldap2-client of the same version
+  (Bugzilla: #93579)</changelog>
+<changelog author="- uli@suse.de" date="1120132800">- build with -fPIE (not -fpie) to avoid GOT overflow on s390*</changelog>
+<changelog author="- rhafer@suse.de" date="1119441600">- build the server packages with -fpie/-pie</changelog>
+<changelog author="- rhafer@suse.de" date="1118836800">- updated to 2.2.27</changelog>
+<changelog author="- rhafer@suse.de" date="1117022400">- libldap-gethostbyname_r.dif: Use gethostbyname_r instead of
+  gethostbyname in libldap. Should fix host lookups through
+  nss_ldap (Bugzilla: #76173)</changelog>
+<changelog author="- rhafer@suse.de" date="1115985600">- Updated to 2.2.26
+- made /%{_libdir}]/sasl2/slapd.conf %config(noreplace)</changelog>
+<changelog author="- rhafer@suse.de" date="1114689600">- Added /%{_libdir}]/sasl2/slapd.conf to avoid warnings about
+  unconfigured OTP mechanism (Bugzilla: #80588)</changelog>
+<changelog author="- rhafer@suse.de" date="1113307200">- added minimal timeout to startproc in init-script to let it
+  report the &quot;failed&quot; status correctly in case of misconfiguration
+  (Bugzilla: #76393)</changelog>
+<changelog author="- rhafer@suse.de" date="1112616000">- crl-check.dif: Implements CRL checking on client and server side
+- use different base ports for differnt values of BUILD_INCARNATION
+  (/.buildenv) to allow parallel runs of the test-suite on a single
+  machine</changelog>
+<changelog author="- uli@suse.de" date="1112616000">- force yielding-select test to yes (test occasionally hangs QEMU)</changelog>
+<changelog author="- uli@suse.de" date="1112356800">- disable test suite on ARM (hangs QEMU)</changelog>
+<changelog author="- rhafer@suse.de" date="1112097600">- updated to 2.2.24
+- enabled back-hdb</changelog>
+<changelog author="- rhafer@suse.de" date="1109764800">- syncrepl.dif: merged latest syncrepl fixes (Bugzilla: #65928)
+- libldap-reinit-fdset.dif: Re-init fd_sets when select is
+  interupted (Bugzilla #50076, ITS: #3524)</changelog>
+<changelog author="- rhafer@suse.de" date="1108641600">- checkproc_before_recover.dif: Check if slapd is stopped before
+  running db_recover from the init script. (Bugzilla: #50962)</changelog>
+<changelog author="- rhafer@suse.de" date="1107259200">- Cleanup back-bdb databases in %post, db-4.3 changed the
+  transaction log format again.
+- cosmetic fixes in init script</changelog>
+<changelog author="- rhafer@suse.de" date="1106654400">- updated to 2.2.23
+- cleaned up #neededforbuild
+- package should also build on older SuSE Linux releases now
+- increased killproc timeout in init-script (Bugzilla: #47227)</changelog>
+<changelog author="- rhafer@suse.de" date="1105617600">- updated to 2.2.20
+- Removed unneeded dependencies</changelog>
+<changelog author="- kukuk@suse.de" date="1102680000">- don't install *.la files</changelog>
+<changelog author="- rhafer@suse.de" date="1100088000">- updated to 2.2.18
+- use kerberos-devel-packages in neededforbuild</changelog>
+<changelog author="- ro@suse.de" date="1096027200">- re-arranged specfile to sequence (header (package/descr)* rest)
+  so the checking parser is not confused ...</changelog>
+<changelog author="- rhafer@suse.de" date="1096027200">- Added pre_checkin.sh to generate a separate openldap2-client
+  spec-file from which the openldap2-client and openldap2-devel
+  subpackages are built. Should reduce build time for libldap as
+  the test-suite is only executed in openldap2.spec.</changelog>
+<changelog author="- rhafer@suse.de" date="1094817600">- libldap-result.dif: ldapsearch was hanging in select() when
+  retrieving results from eDirectory through a StartTLS protected
+  connection (Bugzilla #44942)</changelog>
+<changelog author="- dobey@suse.de" date="1092052800">- added ntlm support</changelog>
+<changelog author="- rhafer@suse.de" date="1091534400">- updated to 2.2.16
+- Updated ACLs in slapd_conf.dif to disable default read access
+  to the &quot;userPKCS12&quot; Attribute
+- rc-check-conn.diff: When starting slapd wait until is accepts
+  connections, or 10 seconds at maximum (Bugzilla #41354)
+- Backported -o slp={on|off} feature from OpenLDAP Head and added
+  new sysconfig variable (OPENLDAP_REGISTER_SLP) to be able
+  to switch SLP registration on and off. (Bugzilla #39865)
+- removed unneeded README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1083326400">- updated to 2.2.11
+- remove SLES8 update specific stuff
+- Bugzilla #39652: Updated slapd_conf.dif to contain basic access
+  control
+- Bugzilla #39468: Added missing items to yast.schema
+- fixed strict-aliasing compiler warnings (strict-aliasing.dif)</changelog>
+<changelog author="- coolo@suse.de" date="1083240000">- build with several jobs if available</changelog>
+<changelog author="- rhafer@suse.de" date="1082376000">- ldapi_url.dif: Fixed paths for LDAPI-socket, pid-file and
+  args-file (Bugzilla #38790)
+- ldbm_modrdn.dif: Fixed back-ldbm modrdn indexing bug (ITS #3059,
+  Bugzilla #38915)
+- modify_check_duplicates.dif: check for duplicate attribute
+  values in modify requests (ITS #3066/#3097, Bugzilla #38607)
+- updated and renamed yast2userconfig.schema to yast.schema as it
+  contains more that only user configuration now
+- syncrepl.dif: addtional fixes for syncrepl (ITS #3055, #3056)
+- test_syncrepl_timeout: increased sleep timeout in syncrepl
+  testsuite</changelog>
+<changelog author="- rhafer@suse.de" date="1080820800">- added &quot;TLS_REQCERT allow&quot; to /etc/openldap/ldap.conf, to make
+  START_TLS work without access to the CA Certificate.
+  (Bugzilla: #37393)</changelog>
+<changelog author="- rhafer@suse.de" date="1080302400">- fixed filelist
+- check-build.sh (build on kernel &gt;= 2.6.4 hosts only)
+- yast2user.schema / slapd.conf fixed (#37076)
+- don't check for TLS-options is init-script anymore (#33560)
+- fixed various typos in README.update</changelog>
+<changelog author="- rhafer@suse.de" date="1079524800">- fixed build of openldap-2.1-slapcat (using correct db41 include
+  files, build backends as on sles8)
+- attempt to update bdb database and reindex ldbm database in %{post}
+- Update notes in README.update
+- better default configuration (including default DB_CONFIG file)
+- misc updates for the YaST schema
+- fixed crasher in syncrepl-code (syncrepl.dif)</changelog>
+<changelog author="- schwab@suse.de" date="1079438400">- Fix type mismatch.</changelog>
+<changelog author="- rhafer@suse.de" date="1078228800">- updated to 2.2.6
+- build a openldap-2.1-slapcat from 2.1.25 sources  to be able to
+  migrate from SLES8 and SL 9.0</changelog>
+<changelog author="- ro@suse.de" date="1077192000">- added check-build.sh (build on 2.6 hosts only)</changelog>
+<changelog author="- rhafer@suse.de" date="1075982400">- updated to 2.2.5
+- adjusted rfc2307bis.schema to support UTF-8 values in most
+  attributes
+- enabled proxycache-overlay (wiht fix to work with back-ldbm)</changelog>
+<changelog author="- rhafer@suse.de" date="1073995200">- updated to 2.2.4
+- updated Admin Guide to most recent version</changelog>
+<changelog author="- adrian@suse.de" date="1073736000">- add %defattr
+- fix build as user</changelog>
+<changelog author="- rhafer@suse.de" date="1070884800">- updated to 2.1.25
+- small fixes for the YaST user schema</changelog>
+<changelog author="- rhafer@suse.de" date="1068552000">- enabled SLP-support</changelog>
+<changelog author="- kukuk@suse.de" date="1066392000">- Remove unused des from neededforbuild</changelog>
+<changelog author="- mt@suse.de" date="1062504000">- Bugzilla #29859: fixed typo in sysconfig metadata,
+  usage of OPENLDAP_LDAPS_INTERFACES in init script
+- added /usr/lib/sasl2/slapd.conf permissions handling
+- added sysconfig variable OPENLDAP_SLAPD_PARAMS=&quot;&quot;
+  to support additional slapd start parameters
+- added sysconfig variable OPENLDAP_START_LDAPI=NO/yes
+  for ldapi:/// (LDAP over IPC) URLs</changelog>
+<changelog author="- rhafer@suse.de" date="1060862400">- added activation metadata to sysconfig template (Bugzilla #28911)
+- removed lint from specfile</changelog>
+<changelog author="- rhafer@suse.de" date="1060257600">- added %stop_on_removal and %restart_on_update calls
+- bdb_addcnt.dif fixes a possible endless loop in id2entry()
+- addonschema.tar.gz: some extra Schema files (YaST, RFC2307bis)</changelog>
+<changelog author="- rhafer@suse.de" date="1058356800">- removed fillup_only and call fillup_and_insserv correctly
+- new Options in sysconfig.openldap: OPENLDAP_LDAP_INTERFACES,
+  OPENLDAP_LDAPS_INTERFACES and OPENLDAP_RUN_DB_RECOVER</changelog>
+<changelog author="- rhafer@suse.de" date="1057060800">- updated to 2.1.22
+- updated Admin Guide to most recent version
+- build librewrite with -fPIC</changelog>
+<changelog author="- rhafer@suse.de" date="1055764800">- updated to 2.1.21</changelog>
+<changelog author="- ro@suse.de" date="1055332800">- fixed requires lines</changelog>
+<changelog author="- rhafer@suse.de" date="1053950400">- don't link back-ldap against librewrite.a, it's already linked
+  into slapd (package should build on non-i386 Archs again)</changelog>
+<changelog author="- rhafer@suse.de" date="1053691200">- fixed dynamic build of back-ldap
+- new subpackage back-ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1053432000">- updated to version 2.1.20
+- enabled dynamic backend modules
+- new subpackages back-perl, back-meta and back-monitor
+- remove unpacked files from BuildRoot</changelog>
+<changelog author="- rhafer@suse.de" date="1052481600">- updated to version 2.1.19</changelog>
+<changelog author="- ro@suse.de" date="1050494400">- fixed requires for devel-package ...</changelog>
+<changelog author="- ro@suse.de" date="1050408000">- fixed neededforbuild</changelog>
+<changelog author="- kukuk@suse.de" date="1045137600">- Enable IPv6 again</changelog>
+<changelog author="- rhafer@suse.de" date="1044964800">- added /etc/openldap to filelist</changelog>
+<changelog author="- rhafer@suse.de" date="1044273600">- switch default backend to ldbm</changelog>
+<changelog author="- ro@suse.de" date="1044187200">- fixed requires for devel package (cyrus-sasl2-devel)</changelog>
+<changelog author="- rhafer@suse.de" date="1044014400">- liblber.dif: Fixes two bugs in liblber by which remote attackers
+  could crash the LDAP server (Bugzilla #22469, OpenLDAP ITS #2275
+  and #2280)</changelog>
+<changelog author="- choeger@suse.de" date="1042545600">- build using sasl2</changelog>
+<changelog author="- rhafer@suse.de" date="1042459200">- updated to version 2.1.12
+- added metadata to sysconfig template (Bug: #22666)</changelog>
+<changelog author="- rhafer@suse.de" date="1038484800">- updated to version 2.1.8
+- added additional fix of 64bit archs
+- added secpatch.dif to fix setuid issues in libldap</changelog>
+<changelog author="- rhafer@suse.de" date="1031313600">- fix for Bugzilla ID #18981, chown to OPENLDAP_USER didn't work
+  with multiple database backend directories</changelog>
+<changelog author="- rhafer@suse.de" date="1030968000">- removed damoenstart_ipv6.diff and disabled IPv6 support due to
+  massive problems with nss_ldap</changelog>
+<changelog author="- rhafer@suse.de" date="1030363200">- ldap_user.dif: slapd is now run a the user/group ldap (Bugzilla
+  ID#17697)</changelog>
+<changelog author="- rhafer@suse.de" date="1030104000">- updated to version 2.1.4, which fixes tons of bugs
+- added damoenstart_ipv6.diff (slapd was not starting when
+  configured to listen on IPv4 and IPv6 interfaces, as done by the
+  start script)
+- added README.SuSE with some hints about the bdb-backend
+- updated filelist to include only the man pages of the backends,
+  that were built</changelog>
+<changelog author="- rhafer@suse.de" date="1029412800">- removed termcap and readline from neededforbuild</changelog>
+<changelog author="- rhafer@suse.de" date="1028808000">- enabled {CRYPT} passwords
+- update filelist (added new manpages)</changelog>
+<changelog author="- rhafer@suse.de" date="1027598400">- patches for 64 bit architectures</changelog>
+<changelog author="- rhafer@suse.de" date="1027080000">- update to 2.1.3</changelog>
+<changelog author="- kukuk@suse.de" date="1025870400">- fix openldap2-devel requires</changelog>
+<changelog author="- rhafer@suse.de" date="1025784000">- switched back from cyrus-sasl2 to cyrus-sasl</changelog>
+<changelog author="- rhafer@suse.de" date="1025697600">- updated to OpenLDAP 2.1.2
+- added the OpenLDAP Administration Guide
+- enabled additional backends (ldap, meta, monitor)</changelog>
+<changelog author="- olh@suse.de" date="1023710400">- hack build/ltconfig to build shared libs on ppc64</changelog>
+<changelog author="- rhafer@suse.de" date="1023278400">- created /etc/sysconfig/openldap and OPENLDAP_START_LDAPS variable
+  to enable ldap over ssl support</changelog>
+<changelog author="- rhafer@suse.de" date="1015502400">- Fix for Bugzilla ID#14569 (added cyrus-sasl-devel openssl-devel
+  to the &quot;Requires&quot; Section of the -devel subpackage)</changelog>
+<changelog author="- rhafer@suse.de" date="1014033600">- updated to the latest STABLE release (2.0.23) which fixes some
+  nasty bugs see ITS #1562,#1582,#1577,#1578</changelog>
+<changelog author="- rhafer@suse.de" date="1013083200">- updated to the latest release (which fixes a index corruption
+  bug)
+- cleanup in neededforbuild
+- small fixes for the init-scripts</changelog>
+<changelog author="- rhafer@suse.de" date="1011268800">- updated to the latest stable release (2.0.21)</changelog>
+<changelog author="- egmont@suselinux.hu" date="1011182400">- removed periods and colons from startup/shutdown messages</changelog>
+<changelog author="- rhafer@suse.de" date="1011096000">- updated to v2.0.20 (which fixes a security hole in ACL
+  processing)</changelog>
+<changelog author="- rhafer@suse.de" date="1010750400">- converted archive to bzip2
+- makes use of %{_libdir} now
+- set CFLAGS to -O0 for archs ia64, s390(x) and alpha otherwise
+  the test suite fails on these archs
+- changed slapd.conf to store the database under /var/lib/ldap
+  (this patch was missing in the last versions by accident)</changelog>
+<changelog author="- rhafer@suse.de" date="1010404800">- update to v2.0.19</changelog>
+<changelog author="- rhafer@suse.de" date="1007640000">- eliminated START_LDAP, START_SLURPD variables in rc.config
+- created separate init script for slurpd
+- moved init scripts from dif to separate source tgz</changelog>
+<changelog author="- choeger@suse.de" date="1004097600">- update to v2.0.18</changelog>
+<changelog author="- choeger@suse.de" date="1003147200">- update to v2.0.17
+  added a sleep to the restart section
+  moved some manpages to the client package</changelog>
+<changelog author="- choeger@suse.de" date="1001937600">- update to v2.0.15</changelog>
+<changelog author="- choeger@suse.de" date="1000296000">- backported the full bugfix from openldap-2.0.14</changelog>
+<changelog author="- choeger@suse.de" date="1000209600">- Bugfix for slurpd millionth second bug (ITS#1323)</changelog>
+<changelog author="- choeger@suse.de" date="1000123200">- moved ldapfilter.conf ldaptemplates.conf ldapsearchprefs.conf
+  to openldap2-client package</changelog>
+<changelog author="- choeger@suse.de" date="999518400">- update to version 2.0.12</changelog>
+<changelog author="- choeger@suse.de" date="994075200">- bugfix: init script was not LSB compliant, Bugzilla ID#9072</changelog>
+<changelog author="- ro@suse.de" date="992952000">- fixed for autoconf again</changelog>
+<changelog author="- choeger@suse.de" date="992606400">- update to 2.0.11
+- removed autoconf in specfile, because it doesn't work</changelog>
+<changelog author="- choeger@suse.de" date="990619200">- update to version 2.0.10 (minor fixes)</changelog>
+<changelog author="- choeger@suse.de" date="990532800">- update to version 2.0.9</changelog>
+<changelog author="- choeger@suse.de" date="988027200">- removed kerberos support
+- added aci support</changelog>
+<changelog author="- choeger@suse.de" date="987768000">- added kerberos support</changelog>
+<changelog author="- choeger@suse.de" date="986472000">- moved section 5 and 8 manpages to the server part of package</changelog>
+<changelog author="- kukuk@suse.de" date="984571200">- Move *.so links into -devel package
+- -devel requires -client</changelog>
+<changelog author="- choeger@suse.de" date="984052800">- split up into openldap2-client and -devel</changelog>
+<changelog author="- ro@suse.de" date="983275200">- changed neededforbuild &lt;cyrus-sasl&gt; to &lt;cyrus-sasl cyrus-sasl-devel&gt;</changelog>
+<changelog author="- ro@suse.de" date="982929600">- added readline/readline-devel to neededforbuild (split from bash)</changelog>
+<changelog author="- choeger@suse.de" date="978609600">- bugfix: slapd.conf rename /var/lib/openldap-ldbm to
+  /var/lib/ldap
+  init script: use $remote_fs</changelog>
+<changelog author="- olh@suse.de" date="978436800">- use script name in %post</changelog>
+<changelog author="- choeger@suse.de" date="976190400">- bugfix from Andreas Jaeger:
+  workaround for glibc2.2, detach</changelog>
+<changelog author="- ro@suse.de" date="975672000">- hacked configure for apparently broken pthread</changelog>
+<changelog author="- ro@suse.de" date="975672000">- fixed spec</changelog>
+<changelog author="- choeger@suse.de" date="974980800">- made configs %config(noreplace) (Bug 4112)
+- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="974894400">- adopted new init scheme</changelog>
+<changelog author="- choeger@suse.de" date="974289600">- fixed neededforbuild</changelog>
+<changelog author="- choeger@suse.de" date="973857600">- added buildroot</changelog>
+<changelog author="- choeger@suse.de" date="973598400">- long package name
+- new version, 2.0.7</changelog>
+<changelog author="- choeger@suse.de" date="970833600">- first package of openldap2 (v2.0.6)</changelog>
+</package>
+
+
+
+
+<package pkgid="7ba58f2b9498981c5f20d25f9675a6592317b694" name="dhcp" arch="i586">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+
+<package pkgid="739feea694870b250262a846af418e4c3d887ecd" name="dhcp" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+
+<package pkgid="7042e04a0b649bcc0a2100ddde62e8fb1ce82927" name="dhcp" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="21.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="22a69d9e4b792e588b4542659fa4ac329fd9e5a8" name="dhcp" arch="i586">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="f9e69cf37731c8b7323c34d46d20b444d551c765" name="dhcp-client" arch="i586">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="f6ba046b24618a07a8b0cc1477e039a8150c0ab1" name="dhcp" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="729e9b2704cab26322040c44f0315280c9db0eab" name="dhcp-client" arch="ppc">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="b9a718ca537188add526485aceba4592a2b84ca7" name="dhcp" arch="src">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="5b8842037e72ca22fa32df2516962822e4c6a313" name="dhcp" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+
+<package pkgid="a45eeef08edd16af1b70ca359d1032cf01e28de3" name="dhcp-client" arch="x86_64">
+<version epoch="0" ver="3.0.3" rel="23.1"/>
+<changelog author="- rml@suse.de" date="1146744000">- Add &quot;-H&quot; flag for setting hostname (Novell major bug #139532)</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- fix two further include paths in dhcpctl.3 and omapi.3</changelog>
+<changelog author="- poeml@suse.de" date="1143633600">- package the static libdst.a library [#158271]
+- fix the include path in dhcpctl.3 and omapi.3 [#158271]</changelog>
+<changelog author="- mls@suse.de" date="1138363200">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- poeml@suse.de" date="1138190400">- dereference links when copying stuff into the chroot jail [#145169]</changelog>
+<changelog author="- thoenig@suse.de" date="1138017600">- dropped dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch.  Correct
+  solution is being implemented in NetworkManager</changelog>
+<changelog author="- thoenig@suse.de" date="1137240000">- replaced 'nis-domain-servers' by 'nis-servers' in
+  dhcp-3.0.3-dhclient-nis-01-thoenig.patch (follow-up #134160)</changelog>
+<changelog author="- thoenig@suse.de" date="1137153600">- add 'nis-domain' and 'nis-domain-servers' to 'request'
+  dhclient.conf (dhcp-3.0.3-dhclient-nis-01-thoenig.patch).  If
+  the DHCP reply contains information about NIS, NM will set those.
+  (#134160)
+- extended /sbin/dhclient-script to set domain name and host name.
+  This will only happen if the relevant options in
+  /etc/sysconfig/network/dhcp are set.
+  (dhcp-3.0.3-dhclient-nm_active-01-thoenig.patch) (#134160)</changelog>
+<changelog author="- poeml@suse.de" date="1133179200">- compile with -fsigned-char on ppc/ppc64, avoiding the
+  dhclient.conf parse error &quot;expecting a statement&quot; [#134590]</changelog>
+<changelog author="- ro@suse.de" date="1127736000">- define LDAP_DEPRECATED in CFLAGS</changelog>
+<changelog author="- poeml@suse.de" date="1123070400">- update to 3.0.3
+  * A bug was fixed in BOOTPREQUEST handling code wherein stale
+  references to host records would be left behind on leases that
+  were not allocated to the client currently booting (eg in the
+  case where the host was denied booting).
+  * The dhcpd.conf.5 manpage was updated to be more clear in
+  regards to multiple host declarations (thanks to Vincent
+  McIntyre).  'Interim' style dynamic updates were also
+  retouched.
+  * dhclient.conf documentation for interface {} was updated to
+  reflect recent discussion on the dhcp-hackers mailing list.
+- update ldap patch, patches merged upstream
+- compile with LPF instead of bsd sockets. Provide optional binary
+  compiled with bsd sockets.
+- README: describe how to serve option 119 (searchlist), add dns
+  compression tool</changelog>
+<changelog author="- hare@suse.de" date="1121169600">- build with pie/PIE depending on architecture.</changelog>
+<changelog author="- gekker@suse.de" date="1120132800">- Add -DEXTENDED_NEW_OPTION_INFO to CFLAGS for rml</changelog>
+<changelog author="- gekker@suse.de" date="1119960000">- Add support for dhcdbd, patches from RH via rml</changelog>
+<changelog author="- ro@suse.de" date="1119268800">- build with pie/fpie</changelog>
+<changelog author="- kukuk@suse.de" date="1118664000">- Don't use kernel types in user space</changelog>
+<changelog author="- poeml@suse.de" date="1112961600">- update to 3.0.3b1 release. Changes since 3.0.2:
+  * A bug was fixed where a server might load balance a DHCP REQUEST to its
+  peer after already choosing not to load balance the preceeding DISCOVER.
+  The peer cannot allocate the originating server's lease.
+  * In the case where a secondary server lost its stable storage while the
+  primary was still in communications-interrupted, and came back online,
+  the lease databases would not be fully transferred to the secondary.
+  This was due to the secondary errantly sending an extra UPDREQ message
+  when the primary made its state transition to PARTNER-DOWN known.
+  * The package will now compile cleanly in gcc 3.3 and 3.4.  As a side effect,
+  lease structures will be 9 bytes smaller on all platforms.  Thanks to
+  Jason Vas Dias at Redhat.
+  * Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+  properly restricted to only detecting broadcast interfaces.  Thanks
+  to a patch from Jason Vas Dias at RedHat.
+  * decode_udp_ip_header was changed so that the IP address was copied out
+  to a variable, rather than referenced by a pointer.  This enforces 4-byte
+  alignment of the 32-bit IP address value.  Thanks to a patch from Dr.
+  Peter Poeml.
+  * An incorrect log message was corrected thanks to a patch from
+  Dr. Peter Poeml.
+  * A bug in DDNS was repaired, where if the server's first DDNS action was
+  a DDNS removal rather than a DDNS update, the resolver library's
+  retransmit timer and retry timer was set to the default, implying a
+  15 second timeout interval.  Which is a little excessive in a synchronous,
+  single-threaded system.  In all cases, ISC DHCP should now hold fast to
+  a 1-second timeout, trying only once.
+  * The siaddr field was being improperly set to the server-identifier when
+  responding to DHCP messages.  RFC2131 clarified the siaddr field as
+  meaning the 'next server in the bootstrap process', eg a tftp server.
+  The siaddr field is now left zeroed unless next-server is configured.
+  * mockup_lease() could have returned in an error condition (or in the
+  condition where no fixed-address was found matching the shared
+  network) with stale references to a host record.  This is probably not
+  a memory leak since host records generally never die anyway.
+  * A bug was repaired where failover servers would let stale client identifiers
+  persist on leases that were reallocated to new clients not sending an id.
+  * Binding scopes (&quot;set var = value;&quot;) are now removed from leases allocated
+  by failover peers if the lease had expired.  This should help reduce the
+  number of stale binding scopes on leases.
+  * A small memory leak was closed involving client identifiers larger than
+  7 bytes, and failover.
+  * Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+  cause an internal function to overflow heap.  Thanks to Jason Vas Dias
+  at Redhat.
+  * Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+  or 'NUMBER_OR_NAME' was repaired.  Hexadecimal parsing is affected, and
+  should work better.
+  * In several cases, parse warnings were being issued before the lexical
+  token had been advanced to the token whose value was causing an error...
+  causing parse warnings to claim the problem is on the wrong token.
+  * Host declarations matching on client identifier for dynamic leases will
+  no longer match fixed-address host declarations (this is now identical
+  to behaviour for host records matching on hardware address).
+- print error if binary DHCPD_BINARY is not found [#76392]
+- remove patches incorporated upstreams
+- update ssh forced command example in dhcpsync man page</changelog>
+<changelog author="- poeml@suse.de" date="1108987200">- update to 3.0.2 release. Changes since 3.0.2rc3:
+  * A previously undocumented configuration directive,
+  'local-address', was documented in the dhcpd.conf manpage.</changelog>
+<changelog author="- mt@suse.de" date="1107864000">- Bug #49433: try to reconnect to ldap server if it was down;
+  ignore SIGPIPE while ldap_unbind called on closed handle.
+  = new patch file: dhcp-3.0.2-ldap-reconnect.mt.dif.gz</changelog>
+<changelog author="- poeml@suse.de" date="1102420800">- update to 3.0.2rc3. Changes since rc2:
+  * Two variables introduced in 3.0.2b1 were used without being
+  initialized in the case where neither the FILE nor SNAME fields
+  were available for overloading.  This was repaired.
+  * A heretofore believed to be impossible corner case of the
+  option overloading implementation turned out to be possible
+  (&quot;Unable to sort overloaded options after 10 tries.&quot;).  The
+  implementation was reworked to consider the case of an option
+  so large it would require more than three chunks to fit.
+  * Many other instances of variables being used without being
+  initialized were repaired.
+  * An uninitialized variable in omapi_io_destroy() led to the
+  discovery that this function may result in orphaned pointers
+  (and hence, a memory leak).
+- refresh the unaligned.patch</changelog>
+<changelog author="- poeml@suse.de" date="1101816000">- update to 3.0.2rc2. Changes since 3.0.1:
+  * allocate_lease() was rewritten to repair a bug in which the server would
+  try to allocate an ABANDONED lease when FREE leases were available.
+  * Some dhcp-eval.5 manpage formatting was repaired.
+  * A bug was fixed in the server's 'option overloading' implementation,
+  where options loaded into the 'file' and 'sname' packet fields were
+  not aligned precisely as rfc2131 dictates.
+  * The FreeBSD client script was changed to support the case where a domain
+  name was not provided by the server.
+  * A memory leak in 'omshell' per each command line parsed was
+  repaired, thanks to a patch from Jarkko Torppa.
+  * Log functions writing to stderr were adjusted to use the STDERR_FILENO
+  system definition rather than '2'.  This is a no-op for 90% of platforms.
+  * One call to trace_write_packet_iov() counted the number of io vectors
+  incorrectly, causing inconsistent tracefiles.  This was fixed.
+  * Some expression parse failure memory leaks were closed.
+  * A host byte order problem in tracefiles was repaired.
+  * Pools configured in DHCPD for failover possessing permission lists that
+  previously were assumed to not include dyanmic bootp clients are now
+  a little more pessimistic.  The result is, dhcpd will nag you about just
+  about most pools that possess a 'allow' statement with no 'deny' that
+  would definitely match a dynamic bootp client.
+  * The 'ddns-update-style' configuration warning bit now insists that
+  the configuration be globally scoped.
+  * Two memory leaks in dhclient were closed thanks to a patch from Felix
+  Farkas.
+  * Some minor but excellently pedantic documentation errors were fixed
+  thanks to a patch from Thomas Klausner.
+  * Bugs in operator precedence in executable statements have been repaired
+  once again.  More legal syntaxes should be parsed legally.
+  * Failing to initialize a tracefile for any reason if a tracefile was
+  specified is now a fatal error.  Thanks to a patch from Albert Herranz.
+  * Corrected a bug in which the number of leases transferred as calculated
+  by the failover primary and sent to peers in POOLRESP responses may be
+  incorrect.  This value is not believed to be used by other failover
+  implementations, excepting perhaps as logged information.
+  * Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+  sending POOLREQ messages instead of POOLRESP mesasges.  This message
+  was essentially ignored since failover secondaries effectively do not
+  respond to POOLREQ messages.
+  * Type definitions for various bitwidths of integers in the sunos5-5
+  build of ISC DHCP have been fixed.  It should compile and run more
+  easily when built in 64-bit for this platform.
+  * &quot;allow known-clients;&quot; is now a legal syntax, to avoid confusion.
+  * If one dhcp server chooses to 'load balance' a request to its failover
+  peer, it first checks to see if it believes said peer has a free
+  lease to allocate before ignoring the DISCOVER.
+  * log() was logging a work buffer, rather than the value returned by
+  executing the statements configured by the user.  In some cases,
+  the work buffer and the intended results were the same.  In some other
+  cases, they were not.  This was fixed thanks to a patch from Gunnar
+  Fjone and directconnect.no.
+  * Compiler warnings for some string type conversions was fixed, thanks
+  to Andreas Gustafsson.
+  * The netbsd build environments were simplified to one, in which
+-Wconversion is not used, thanks to Andreas Gustafsson.
+  * How randomness in the backoff-cutoff dhclient configuration variable
+  is implemented was better documented in the manpage, and the behaviour
+  of dhclient in REQUEST timeout handling was changed to match that of
+  DISCOVER timeout handling.
+  * Omapi was hardened against clients that pass in null values, thanks
+  to a patch from Mark Jason Dominus.
+  * A bug was fixed in dhclient that kept it from doing client-side
+  ddns updates.  Thanks to a patch from Andreas Gustafsson, which
+  underwent some modification after review by Jason Vas Dias.
+  * Failover implementations disconnected due to the network between
+  them (rather than one of the two shutting down) will now try to
+  re-establish the failover connection every 5 seconds, rather than
+  to simply try once and give up until one of them is restarted.
+  Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+  by Greger V. Teigre which led to an enhancement to it.
+  * A problem that kept DHCP Failover secondaries from tearing down
+  ddns records was repaired.  Thanks to a patch from Ulf Ekberg from
+  Infoblox.
+  * 64bit pointer sizes are detected properly on FreeBSD now.
+  * A bug was repaired where the DHCP server would leave stale references
+  to host records on leases it once thought about offering to certain
+  clients.  The result would be to apply host and 'known' scopes to the
+  wrong clients (possibly denying booting).  NOTE:  The 'mis-host' patch
+  that was being circulated as a workaround is not the way this bug was
+  fixed.  If you were a victim of this bug in 3.0.1, you are cautioned
+  to proceed carefully and see if it fixes your problem.
+  * A bug was repaired in the server's DHCPINFORM handling, where it
+  tried to divine the client's address from the source packet and
+  would get it wrong.  Thanks to Anshuman Singh Rawat.
+  * A log message was introduced to help illuminate the case where the
+  server was unable to find a lease to assign to any BOOTP client.
+  Thanks to Daniel Baker.
+  * A minor dhcpd.conf.5 manpage error was fixed.
+- update ldap patch (11/8/2004 version)</changelog>
+<changelog author="- ro@suse.de" date="1100174400">- fixed file list for devel package</changelog>
+<changelog author="- poeml@suse.de" date="1095940800">- sysconfig.dhcpd, sysconfig.dhcrelay: give examples how to use
+  configuration names instead of interface names</changelog>
+<changelog author="- poeml@suse.de" date="1091707200">- update to 3.0.1
+  * The global variable 'cur_time' was centralized and is now
+  uniformly of a type #defined in system-dependent headers. It
+  had previously been defined in one of many places as a 32-bit
+  value, and this causes mayhem on 64-bit big endian systems. It
+  probably wasn't too healthy on little endian systems either.
+  * A printf format string error introduced in rc14 was repaired.
+  * AIX system-dependent header file was altered to only define
+  NO_SNPRINTF if the condition used to #ifdef in vsnprintf in
+  AIX' header files is false.
+  * The Alpha/OSF system-dependent header file was altered to
+  define NO_SNPRINTF on OS revisions older than 4.0G.
+  * omapip/test.c had string.h added to its includes.
+- drop obsolete dhcp-curtimetype.patch
+- cope with missing files during chroot setup (e.g., if no
+  resolv.conf exists) [#40728]
+- remove duplicated option &quot;-cf&quot; from usage output
+- add notes about the used raw socket API to README</changelog>
+<changelog author="- poeml@suse.de" date="1089979200">- update to 3.0.1rc14
+- remove obsolete patches and adapt dhcp-3.0.1rc13-tmpfile.dif
+- dhcpsync: use try-restart (so the server isn't started if it has
+  been stopped)
+- remove notify messages that are sent to root
+- check if dhcpd was active at boot time before update and
+  restore runlevel links if needed [#41215], and PreRequires for
+  that</changelog>
+<changelog author="- poeml@suse.de" date="1087214400">- security fixes [#41975]:
+- fix buffer overflow in the DHCP server that can be exploited by
+  the client by specifying multiple 'hostnames' to execute
+  arbitrary code or at least crash the server. VU#317350
+- add patch to use vsnprintf() instead of vsprintf() calls.
+  VU#654390</changelog>
+<changelog author="- poeml@suse.de" date="1084536000">- fix sysconfig comment and DHCPD_RUN_AS default [#40174]</changelog>
+<changelog author="- poeml@suse.de" date="1084449600">- improve security of the chroot jail setup by creating a dedicated
+  user id for the server, and move the leases database into a
+  subdirectory (/var/lib/dhcp/db). With the exception of that
+  subdirectory the chroot jail is now owned by root. [#40174]  Use
+  mkstemp to create temporary files. [#40267]
+- don't use startproc to start dhcpd, because startproc waits a
+  fixed time (100 msec) until it decides whether the service is
+  running or not. Now that dhcpd might have to contact an LDAP
+  server first to read its configuration, starting up can take
+  longer than that, and the init script would falsely report
+  &quot;success&quot; even when the server cannot start up due to broken
+  configuration or non-existant interfaces. Increasing the
+  startproc timeout (-t) is not a real alternative because, because
+  it would imply a fixed dely to the init script, and it might
+  still be too short.  [#40350]</changelog>
+<changelog author="- poeml@suse.de" date="1083672000">- convert configuration names in DHCPD_INTERFACE /
+  DHCRELAY_INTERFACES into interface names [#39718]
+- fix service restart for the case where the binary has been
+  switched for backward compatibility during updating.
+- do not change DHCPD_BINARY for backward compatibility if updating
+  from 9.0. This and the last change complete the fix for [#38422]
+  and take care of updates from 8.1-9.1 with and without YOU
+  updates.</changelog>
+<changelog author="- poeml@suse.de" date="1083326400">- additionally package the dhcpd binary that uses the Linux packet
+  filter API. New option DHCPD_BINARY in sysconfig.dhcpd. [#38422]
+- when updating from a previous package using LPF API, retain the
+  old behaviour. Fix init script so that 'stop' works also after a
+  switch of DHCPD_BINARY.</changelog>
+<changelog author="- mt@suse.de" date="1082635200">- updated to dhcp-3.0.1rc13-ldap-patch also obsolating the
+  patches: dhcp-ldap-fix01.dif, dhcpd-conf-to-ldap.pl.dif
+- added dhcp-3.0.1rc13-ldap.mt.dif, providing diverse fixes
+  and basic failover support for server/ldap.c
+- added dhcpd-conf-to-ldap.mt.dif providing failover support
+  to dhcpd.conf convert script</changelog>
+<changelog author="- mt@suse.de" date="1080216000">- applied dhcp-3.0.1rc12-ldap-patch adding support to store
+  dhcp configuration in ldap (incl. draft ldap schema).
+  further patches:
+- dhcp-ldap-fix01.dif: fixes for server/ldap.c (debuging
+  output, support for block statements, ...)
+- dhcpd-conf-to-ldap.pl.dif: fixes for convert script</changelog>
+<changelog author="- poeml@suse.de" date="1077710400">- the genDDNSkey script has been moved to the bind-utils package
+- update the DDNS-howto.txt
+- package leases.awk (dhcpd.leases analyzer) (courtesy of Jeff Wilson)
+- update to 3.0.1rc13
+- Fixed a bug in omapi lease lookup function, to form the
+  hardware address for the hash lookup correctly
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG
+  only
+- Fixed a case where leases read from the leases database do not
+  properly over-ride previously read leases.
+- Fixed a bug where dhcrelay was sending relayed responses back
+  to the broadcast address, but with the source's unicast mac
+  address.  Should now conform to rfc2131 section 4.1.
+- Fixed a crash bug in dhclient where dhcpd servers that do not
+  provide renewal times results in an FPE.  As a side effect,
+  dhclient can now properly handle 0xFFFFFFFF (-1) expiry times
+  supplied by servers.
+- dhcpctl.3 manpage was tweaked.
+- the files CHANGES and COPYRIGHT have vanished, package LICENSE
+  instead</changelog>
+<changelog author="- adrian@suse.de" date="1073822400">- build as user</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- if starting dhcpd in chroot jail, and a pid file is present in
+  the jail, and the pid file does not contain a pid of a running
+  dhcpd process, but that of another _running_ process, remove
+  that pid file. [#32603]
+- fix typo in dhcp.LIESMICH
+- DDNS-howto.txt: adjust changed path
+- DDNS-howto.txt: instead of the shell variables (they were copy
+  and paste'd from a script), use a real example (makes it easier)
+- add a comment in sysconfig.dhcpd that entire directories may be
+  included
+- dhcpsync: if run from the commandline, do not use an identity
+  that ssh-agent may hold, but use $KEY instead
+- dhcpsync.8: add a note about a know limitation</changelog>
+<changelog author="- poeml@suse.de" date="1069156800">- fix wrong ServiceRestart tags in sysconfig/dhcrelay [#32062]</changelog>
+<changelog author="- uli@suse.de" date="1066392000">- fixed data type mismatch in libomapi, only harmful on 64-bit
+  BE systems (ppc64, s390x, bug #32123)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- update to 3.0.1rc12
+- a failover bug relating to identifying peers by name length
+  instead of by name was fixed
+- declaring failover configs within shared-network statements
+  should no longer result in error
+- a problem with lease expiry times in failover configurations
+  was fixed
+- reverse dns PTR record updates with values containing spaces
+  are now permitted
+- problems with long option processing fixed
+- fixes to minires so that updates of KEY records will work
+- memory leak in configuration parsing closed
+- non-broadcast or point-to-point interfaces are now ignored
+- options not yet known by the dhcpd or dhclient now appear as
+  e.g. &quot;unknown-144&quot; rather than &quot;#144&quot; in the leases file, to
+  avoid the hash marks
+- dhclient no longer uses shell commands to kill another instance
+  of itself, it sends the signal directly.
+- the -nw command line option to dhclient now works
+- dhcp-3.0.1rc10-dhcrelay-limit-hopcount.dif included upstreams
+- added contrib/ms2isc (converts Microsoft DHCP server configuration)</changelog>
+<changelog author="- poeml@suse.de" date="1063022400">- mark dhclient's lease database %config(noreplace)</changelog>
+<changelog author="- kukuk@suse.de" date="1062590400">- Really fix [#29405], server should not provide and obsolete dhcp.</changelog>
+<changelog author="- poeml@suse.de" date="1061985600">- don't provide/require dhcp-base. Require dhcp instead [#29405]</changelog>
+<changelog author="- poeml@suse.de" date="1061899200">- add Config: syslog-ng to sysconfig.syslog-dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="1060948800">- use -Wall -Wno-unused
+- add -fno-strict-aliasing, due to warnings about code where
+  dereferencing type-punned pointers will break strict aliasing
+- add activation metadata to sysconfig template [#28864, [#28865],
+  [#28950]</changelog>
+<changelog author="- poeml@suse.de" date="1060689600">- rc.dhcpd, rc.dhcrelay: implement try-restart correctly
+- cleaned up the root mail, and the READMEs [#27214], [#26266]
+- send the root mail only on update [#27214]
+- have no default value in /etc/sysconfig/dhcpd:DHCPD_INTERFACE
+- in client's %post, send a mail only when rc.config is encountered
+- clean buildroot, but not in chroot buildsystem
+- the SuSE string is now replaced by UnitedLinux where appropriate
+- rename the &quot;dhcp-base&quot; package to &quot;dhcp&quot;, so there is a binary
+  package matching the name of the source package [#17668]
+- use the lately added macros only on newer distributions</changelog>
+<changelog author="- poeml@suse.de" date="1059566400">- new macros for stop/restart of services on rpm update/removal</changelog>
+<changelog author="- poeml@suse.de" date="1059393600">- when copying include files into the chroot jail, create
+  subdirectories as needed, thus retaining the path to the files</changelog>
+<changelog author="- poeml@suse.de" date="1059307200">- don't explicitely strip binaries since RPM handles it, and may
+  keep the stripped information somewhere</changelog>
+<changelog author="- poeml@suse.de" date="1055764800">- add some notes to DDNS-howto.txt, kindly provided by Andrew Beames
+- fix typo in genDDNSKey.sh</changelog>
+<changelog author="- mmj@suse.de" date="1053518400">- Implement try-restart correctly in init-script</changelog>
+<changelog author="- poeml@suse.de" date="1053345600">- update to 3.0.1rc11, relevant fixes are
+- Potential buffer overflows in minires repaired.
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+  that worked before are now detected and produce errs, some legal syntaxes
+  that errored before will now work properly.
+- Some search-and-replace errors that caused some options to change their
+  names was repaired.
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+  module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+  configuration file.
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- Missing non-optional failover peer configurations will now result in a soft
+  error rather than a null dereference.
+- use BSD sockets instead of LPF (makes iptables filtering of
+  packages possible for server and relay. It doesn't work on the
+  client, though, so that one requires seperate compilation.) See
+  Message-Id: &lt;5.1.0.14.0.20030408175011.00b9c7c0@pop.itd.nrl.navy.mil&gt;</changelog>
+<changelog author="- poeml@suse.de" date="1047556800">- rcdhcpd, rcdcrelay: do not write the startup log to a world
+  writable directory [#25241]</changelog>
+<changelog author="- poeml@suse.de" date="1046692800">- don't try to copy libraries into the chroot jail that do not
+  exist (any longer) [#24533]
+- remove the %ghost filelist entries for pid files and chroot jail
+  contents [#20030]. Clean up the libraries from the jail when the
+  server is stopped.
+- dhcrelay: add patch from Florian Lohoff (slightly modified),
+  that makes the maximal hop count of forwarded packages
+  configurable (-c maxcount), sets the default to 4, and rejects
+  packages with a hop count higher than maxcount (CAN-2003-0039,
+  http://www.kb.cert.org/vuls/id/149953). Add a variable to
+  /etc/sysconfig/dhcrelay to pass such additional options.</changelog>
+<changelog author="- mmj@suse.de" date="1045051200">- Added sysconfig metadata [#22631] [#22632] [#22696]</changelog>
+<changelog author="- okir@suse.de" date="1039521600">- Added security patch from ISC</changelog>
+<changelog author="- poeml@suse.de" date="1039089600">- update to 3.0.1rc10. relevant fixes:
+- A Linux-specific Token Ring detection problem was fixed.
+- Hashes removed from as-yet-unknown agent options, having those
+  options appear in reality before we know about them will no
+  longer produce self-corrupting lease databases.
+- dhclient will use the proper port numbers now when using the -g
+  option.
+- A order-of-operations bug with 2 match clauses in 1 class
+  statement is fixed thanks to a patch from Andrew Matheson.
+- A fix to the dhcp ack process which makes certain group options
+  will be included in the first DHCPOFFER message was made thanks
+  to a patch from Ling Gou.
+- A few memory leaks were repaired thanks to patches from Bill
+  Squier at ReefEdge, Inc.  (groo@reefedge.com).
+- A fix for shared-networks that sometimes give clients options
+  for the wrong subnets (in particular, 'option routers') was
+  applied, thanks to Ted Lemon for the patch.
+- Omshell's handling of dotted octets as values was changed such
+  that dots one after the other produce zero values in the
+  integer string.
+- due to the upstream fixes: drop the reactivate-tr-support.dif and
+  format.dif
+- retrofitted the (server) package to work for old distributions
+  down to 7.2</changelog>
+<changelog author="- schwab@suse.de" date="1038571200">- Fix unaligned access.</changelog>
+<changelog author="- poeml@suse.de" date="1036411200">- update DDNS-howto.txt for BIND9
+- add genDDNSKey.sh to create a key for BIND8/9
+- add comments about DDNS to the dhcpd.conf [#18419], and
+  directives to disable DDNS by default
+- change defaults in the sample configuration</changelog>
+<changelog author="- poeml@suse.de" date="1030622400">- fix permissions of man pages</changelog>
+<changelog author="- poeml@suse.de" date="1029672000">- re-add token ring support that got lost (&quot;tr0:unknown hardware
+  address type 800&quot;). With 2.4 kernel, ARPHRD_IEEE802 (6) has been
+  renamed to ARPHRD_IEEE802_TR (800). Known bug in 3.0.1rc9.
+- move PreReq tag to the subpackages, where it is actually needed
+  [#17822, #17821]</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- dhcp-client: add missing Requires on /usr/bin/host</changelog>
+<changelog author="- poeml@suse.de" date="1029153600">- Fix requires of dhcp-devel subpackage
+- add some helpful scripts, courtesy of Kevin C. Miller</changelog>
+<changelog author="- poeml@suse.de" date="1028203200">- use PreReq</changelog>
+<changelog author="- poeml@suse.de" date="1026907200">- add a sysconfig.syslog-dhcpd template to make syslogd open an
+  additional socket (inside the chroot dir of dhcpd)</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- fix typo in %post, introduced with last change</changelog>
+<changelog author="- poeml@suse.de" date="1026388800">- add Version: tags to the subpackages to satisfy the build system,
+  because dhcp has no main package [#16318]
+- run in chroot and as user nobody per default
+- fix wrong pathnames in mail to root [#15601]
+- install example dhcpd.conf [#9122]
+- improve example configuration files [#12563]
+- init scripts: update INIT INFO, using the new tags from
+  /etc/init.d/skeleton</changelog>
+<changelog author="- poeml@suse.de" date="1021982400">- dhclient-script:
+- source the right sysconfig files (/etc/sysconfig/network/)
+  [#15871]
+- use KEEP_SEARCHLIST option (thanks Sumit Bose)
+- improve the indentation</changelog>
+<changelog author="- poeml@suse.de" date="1021550400">- add documentation about configuration for dynamical DNS updates</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- fix last change (rediff dhcp-3.0.1rc9.format.dif)</changelog>
+<changelog author="- poeml@suse.de" date="1021291200">- update to 3.0.1rc9
+- fixes a format string vulnerability in the server that could
+  lead to a remote root compromise
+  (see http://www.cert.org/advisories/CA-2002-12.html)
+- fixes a memory leak in the client and some other minor bugs
+- fix some printf arguments in server/omapi.c
+- fix small typo (x390x -&gt; s390x)</changelog>
+<changelog author="- sf@suse.de" date="1020081600">- changed Makefile.conf to be able to add LIBDIR
+- added LIBDIR to make install to put libs into the correct path
+- use -DPTRSIZE_64BIT on x86_64</changelog>
+<changelog author="- poeml@suse.de" date="1019476800">- update to 3.0.1rc8. Most significant changes are (see RELNOTES):
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET
+  state to be renewed.
+- Implement lease stealing for cases where the primary has fewer
+  leases than the secondary, as called for by the standard.
+- Fix a bug where if an option universe contained no options, the
+  DHCP server could dump core (Walter Steiner).
+- Fix a bug in the handling of encapsulated options.
+- Fix an uninitialized memory bug in the DHCP client.
+- use -DPTRSIZE_64BIT on x390x and ppc64, too
+- create /etc/resolv.conf with a file mask of 644, regardless of
+  the umask [Bug #15915]. Patch by Joerg Mayer.
+- the scripts dir is now called CLIENTBINDIR in the Makefiles, and
+  correctly set to /sbin --&gt; drop 2 hunks from dhcp-3.0rc10.dif</changelog>
+<changelog author="- ro@suse.de" date="1017144000">- Fix handling of initscript links and START_* variable [Bug #13755]</changelog>
+<changelog author="- poeml@suse.de" date="1013342400">- drop the sysconfig/network/dhcp template. It's in the syconfig
+  package now.
+- strip /sbin/dhclient</changelog>
+<changelog author="- poeml@suse.de" date="1012824000">- rename dhcp subpackage to dhcp-base, add dhcp-server subpackage
+- rename dhclient to dhcp-client and dhcrelay to dhcp-relay
+- remove Conflicts tag dhclient &lt;-&gt; dhcpcd
+- use %defattr(-, root, root) for all subpackages
+- update copyright info (GmbH --&gt; AG)
+- update sysconfig.dhclient (.dhcp-dhclient now), and let it be
+  filled up into /etc/sysconfig/network/config</changelog>
+<changelog author="- poeml@suse.de" date="1012392000">- add /sbin/dhclient, accidentally deleted from filelist lately</changelog>
+<changelog author="- ro@suse.de" date="1012132800">- remove START_DHCPD on update
+- use fillup_only where no initscript is handled</changelog>
+<changelog author="- poeml@suse.de" date="1012132800">- use %_lib and %_libdir
+- update rc.dhcpd to use %_libdir when setting up chroot dir
+- dhcpsync: name of slave can be given as argument; update man page
+- rc.dhcpd: no longer source rc.config
+- don't try insserv on dhclient init script -- it's dropped
+- tell fillup to use &quot;dhcpd&quot; instead of the package name (dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011960000">- update to 3.0.1rc6
+- Fix the off-by-one error in the MAC-address checking code for
+  DHCPRELEASE that was added in 3.0.1rc5.
+- Fix a bug where client-specific information was not being
+  discarded from the lease when it expired or was released,
+  resulting in problems if the lease was reallocated to a
+  different client.
+- merge pools if possible
+- workaround for some Lexmark printers that send a double-NUL-
+  terminated host-name option, which would break DNS updates.
+- no longer log fallback_discard messages
+- dhcp-3.0.1rc5-release.dif obsolete hereby
+- drop dhclient init script (obsoleted by /sbin/if*-dhcp)</changelog>
+<changelog author="- poeml@suse.de" date="1011096000">- update to 3.0.1rc5
+- Fix a bug that would cause the DHCP server to spin if asked to
+  parse a certain kind of incorrect statement.
+- Fix a related bug that would prevent an error from being
+  reported in the same case.
+- Additional documentation.
+- Make sure that the hardware address matches the lease when
+  processing a DHCPRELEASE message.
+- add dhcp-3.0.1rc5-release.dif that corrects an error by one in
+  the code that finds a lease that is being RELEASEd
+- use ddns-update-style interim instead of ad-hoc when testing
+- make sure that dhcpd is started after xntpd (failover needs
+  correct system time)
+- drop version 2 of dhcpd and dhcrelay</changelog>
+<changelog author="- ro@suse.de" date="1008244800">- removed START_ variables, moved rc.config.d -&gt; sysconfig</changelog>
+<changelog author="- poeml@suse.de" date="1005048000">- update to 3.0.1rc4
+- add dhcpsync and dhcpync.8 (script to sync DHCP failover config.)
+- update rc.dhclient script from the one used in the dhcpcd package
+- client: don't check if a device is there; terminate anyway
+- small addition to the examples; update README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004529600">- update to 3.0.1rc2
+- add a README.upgrade</changelog>
+<changelog author="- poeml@suse.de" date="1004011200">- update to 3.0.1rc1
+- remove our #undef use_LPF patch for 2.0pl5; it seems to cause
+  problems (stopping responding) with more than one network card
+- mark /etc/dhclient.conf with noreplace tag</changelog>
+<changelog author="- poeml@suse.de" date="1000641600">- fix stupid bug in rc.dhcpd where rc.config is sourced too late</changelog>
+<changelog author="- poeml@suse.de" date="1000468800">- fix #9962 where &quot;exit 1&quot; instead of &quot;return&quot; in dhclient-script
+  would confuse dhclient (which then DECLINEd the lease)</changelog>
+<changelog author="- poeml@suse.de" date="999000000">- make sure that files are really copied to the chroot dir</changelog>
+<changelog author="- poeml@suse.de" date="998913600">- add libnss_dns6.so.2 as ghost to the file list to remove it
+  from the chroot dir when uninstalling the package
+- rc.dhcpd: remove empty pid files to avoid warnings by
+  checkproc/killproc (dhcpd sometimes leaves them if it does not
+  want to start due to wrong syntax)
+- rc.dhcpd: to save time, source rc.config only when necessary
+- add dhcpd.conf examples</changelog>
+<changelog author="- poeml@suse.de" date="998654400">- update to 3.0rc12 (fixes some failover state transitions; other
+  failover fixes; always returns a subnet selection option if one
+  is sent)
+- change dhclient-script to ignore lines that are commented out
+  when grepping for variables and eval-ing them</changelog>
+<changelog author="- poeml@suse.de" date="995284800">- add filedes.dif that gives scripts executed from dhclient-script
+  their own filedescriptors (patch by Brian Somers
+  &lt;brian@Awfulhak.org&gt;)
+- correct typo in rc.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="994075200">- update to 3.0rc10
+- change default in rc.config.d.dhcrelay
+- add /usr/sbin/svtest, /usr/bin/omshell, and omshell man pages
+- new variable in rc.dhcpd.config: $DHCPD_CONF_INCLUDE_FILES, for
+  dhcpd.conf include files to be copied to $chroot/etc/</changelog>
+<changelog author="- poeml@suse.de" date="990532800">- update to 3.0rc7 (failover and OMAPI fixes, see RELNOTES)</changelog>
+<changelog author="- poeml@suse.de" date="990014400">- on 64 bit archs, define -DPTRSIZE_64BIT
+- fix missing include</changelog>
+<changelog author="- poeml@suse.de" date="989582400">- if resolv.conf does not exist, touch it; so that there is a file
+  to back up and restore later and the temporary resolv.conf would
+  not persist after stopping the client [#8078]
+- use the modify_resolvconf tool to cleanup old backup files before
+  starting the daemon, because it does it intelligently [#8077]</changelog>
+<changelog author="- poeml@suse.de" date="989323200">- don't provide empty /etc/rc.config.d/dhcpd.rc.config because that
+  inhibits the correct removal of variables from rc.config
+- mention correct version numbers in mail to root (now using
+  version macro)
+- fix a typo and a nonsense comment in rc.config.d.dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="989236800">- update to 3.0rc4 (bugfixes)
+- add empty dir /var/lib/dhcp/dev and documentation about how to
+  ensure that logging from the chroot jail works [#6906]</changelog>
+<changelog author="- poeml@suse.de" date="988113600">- update to 3.0rc2pl1: fixes bugs in the failover implementation
+  and a memory smash that happens when fixed-address leases are
+  used
+- Read dhcp client script hooks if they exist, rather than only if
+  they're executable.
+- new file: 3.0b1 lease conversion script</changelog>
+<changelog author="- poeml@suse.de" date="987336000">- Init scripts: get try-restart (&quot;restart when running&quot;) right
+- client:
+- dhclient-script is now correctly installed to /sbin (thus,
+  don't mv dhclient-script from /etc/ to /sbin/, thereby
+  overwriting it with the one from v2)
+- move rcdhclient conveniency link to /sbin/ (same as in dhcpcd)
+- update info header for resolv.conf acc. to guidelines
+- server:
+- don't run in chroot environment and as nobody by default
+- add missing %postun for subpackages to rearrange runlevel
+  links after deinstalling</changelog>
+<changelog author="- poeml@suse.de" date="986817600">- update to 3.0b2pl24
+- don't use rc_status -u in init scripts (option was dropped)
+- always run test of dhcpd</changelog>
+<changelog author="- poeml@suse.de" date="985780800">- update to 3.0b2pl18
+  * trim chroot/non-root patch and the other security patches into
+  dhcp-3.0b2pl18.paranoia.dif
+  * build stable version of server (2.0pl5) and include the binary
+  as well as the man pages with '-2' suffix (same for dhcrelay)
+- split off subpackages: dhcrelay, dhcp-devel
+- reworked all init scripts
+  * adhere to LSB and use new rc.status functions
+  * rc.dhcpd: at start, copy conf file and libs to chroot dir
+  * rc.dhcpd: add syntax check
+  * rc.dhcrelay: make interface configurable
+  * rc.dhclient: improve resolv.conf handling
+- dhclient: catch TERM to restore resolv.conf before quitting
+- create /etc/rc.config.d/dhcrelay.rc.config
+- create /etc/rc.config.d/dhclient.rc.config
+- clean up Provides/Conflicts
+- rework SuSE-fillup templates (and rename them)
+- mark libraries for chroot dir as %ghost
+- when ABUILD_RUN_TEST_SUITES is true, start dhcpd for a simple
+  test</changelog>
+<changelog author="- poeml@suse.de" date="984744000">- add dhcpd-thomas.diff from &lt;thomas@suse.de&gt;
+  * query for the real UID and not for the effective UID
+  * drop supplementary GID's
+  * avoid potential buffer overflow
+- copy dhcpd.conf instead of moving it
+- add $syslog to Required-Start in server init script
+- fix Required-Start in client init script
+- bzipped sources</changelog>
+<changelog author="- poeml@suse.de" date="980942400">- dhcpd.conf will no longer be installed in /etc/ but placed in the
+  docdir, since it is a nonfunctional example file
+- test for etc/SuSE-release in %post
+- fix removal of variables from rc.config which failed sometimes
+- update {README,LIESMICH}.SuSE</changelog>
+<changelog author="- poeml@suse.de" date="980769600">- added paranoia patch by Ari Edelkind to allow dhcpd run chrooted
+  in /var/lib/dhcp and as nobody/nogroup. Both is optional.
+- moved dhcpd.conf to /var/lib/dhcp/etc/. The file will also be
+  moved by %post
+- moved rc.config options to rc.config.d/dhcpd.rc.config
+  (existing variables are moved there by %post)
+- added some syntax checking via undocumented -t switch, and write
+  log file during startup
+- renamed start script from dhcp to dhcpd
+- removed /var/run/dhcpd.pid from the package
+- tag some %configs with (noreplace)
+- use BuildRoot
+- added &quot;Provides: dhcp2&quot;+&quot;Conflicts: dhcp3&quot; in anticipation of v3
+- added {README,LIESMICH}.SuSE and the paranoia patch to the docs</changelog>
+<changelog author="- draht@suse.de" date="979646400">- format string security bugs in syslog(3) calls fixed.</changelog>
+<changelog author="- poeml@suse.de" date="979214400">- in runlevel 2, start only the client, not the server/relay
+- tell insserv to start after $named
+- improved comments</changelog>
+<changelog author="- fober@suse.de" date="978609600">- package dhclient requires net-tools, not net_tool
+- removed superfluous Provides dhclient in package dhclient</changelog>
+<changelog author="- poeml@suse.de" date="975499200">- Update to dhcp-2.0pl5.tar.gz
+- This includes a security fix that applies to the DHCP client *only*</changelog>
+<changelog author="- poeml@suse.de" date="975412800">- adapted spec file to use /etc/init.d for the scripts instead of
+  /sbin/init.d and let insserv create the links
+- extracted source files from diff and placed them separately
+- included paranoia (non-root/chroot) patch by ari edelkind. This
+  needs testing, and possibly an adapted start script</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Fix argument type of dhcp_option_ev_name.</changelog>
+<changelog author="- schwab@suse.de" date="964440000">- Set DEBUG, not COPTS.</changelog>
+<changelog author="- zoz@suse.de" date="964094400">- updated to dhcp-2.0pl3</changelog>
+<changelog author="- schwab@suse.de" date="964008000">- Fix handling of abandoned leases with BOOTP.
+- Properly handle default lease timeout.</changelog>
+<changelog author="- werner@suse.de" date="963576000">- make dchpd quiet</changelog>
+<changelog author="- zoz@suse.de" date="963489600">- changed test for availability of device in rcdhlient:
+  now using ifconfig, so automatically loading of modules
+  will be triggered (Bug 3415)
+- patched dhclient.c do to a possible root exploit bug
+  (patch from Pavel Kankovsky &lt;peak@argo.troja.mff.cuni.cz&gt;)
+  Still to be improved, waiting for Ted Lemon to rework it.</changelog>
+<changelog author="- zoz@suse.de" date="963316800">- reworked rcdhclient once again.</changelog>
+<changelog author="- zoz@suse.de" date="962712000">- update to dhcp-2.0.pl2
+- dhclient: hostname will only be set, if there is a
+  DHCLIENT_SET_HOSTNAME=yes (default =no)
+  in /etc/rc.config. (fixes bug 2807 and 3146)</changelog>
+<changelog author="- zoz@suse.de" date="962107200">- update to dhcp-2.0.pl1
+- moved /var/state/dhcp to /var/lib/dhcp
+- moved manpages to %{_mandir}
+- changed rcdhclient: DHCLIENT is obsolete now. It will be started
+  if it finds any IFCONFIG_x=dhcpclient</changelog>
+<changelog author="- schwab@suse.de" date="955368000">- Treat Linux 2.3 as linux-2.2 configuration.</changelog>
+<changelog author="- grimmer@suse.de" date="948974400">- added &quot;Provides: dhcp_client&quot; and &quot;Conflicts: dhcpcd&quot; to
+  dhclient section in spec file
+- added &quot;Provides: dhcp_server&quot; to dhcp section
+- corrected typo in rc.config variables
+- added Group Tag and version macro to spec file
+- changed Summary: to &quot;ISC DHCP client&quot;
+- moved man pages to /usr/share/man</changelog>
+<changelog author="- rolf@suse.de" date="942840000">- now set hostname in dhclient-script [BUG#1262]</changelog>
+<changelog author="- rolf@suse.de" date="941803200">- reduced waiting time to 1 second
+- wait 5 seconds after dhclient start to acquire an IP adress so the
+  following scripts have a working network setup</changelog>
+<changelog author="- rolf@suse.de" date="941716800">- changes from Josh for @home cablenet</changelog>
+<changelog author="- rolf@suse.de" date="941112000">- added changes by Lenz Grimmer to use
+  ifconfig $NETDEV 0.0.0.0 up
+  for device setup</changelog>
+<changelog author="- rolf@suse.de" date="940852800">- applied patch of Bernhard Bender &lt;Bernhard.Bender@elsa.de&gt;
+  to use the correct interface.
+- added client latency time and rc.config entry</changelog>
+<changelog author="- bs@suse.de" date="938433600">- fixed requirements for sub packages</changelog>
+<changelog author="- bs@suse.de" date="937224000">- ran old prepare_spec on spec file to switch to new prepare_spec.</changelog>
+<changelog author="- bs@suse.de" date="932385600">- changed comment for rc.config</changelog>
+<changelog author="- bs@suse.de" date="932385600">- fix from werner@suse.de for /sbin/init.d/dhclient</changelog>
+<changelog author="- ro@suse.de" date="932126400">- added new dhclient-script from werner</changelog>
+<changelog author="- rolf@suse.de" date="930139200">- new version 2.0
+- apply fix from Michael Hasenstein</changelog>
+<changelog author="- ro@suse.de" date="920894400">- fixed man5-path</changelog>
+<changelog author="- rolf@suse.de" date="920030400">- new version 2.0b1pl16 (stable beta)
+- leases are now stored in /var/state/dhcp/ (thanks to Ted Lemmon)
+- correct paths in manpages
+- PID files as %ghost in filelist</changelog>
+<changelog author="- rolf@suse.de" date="919252800">- new version 2.0b1pl13</changelog>
+<changelog author="- rolf@suse.de" date="913204800">- added    /usr/sbin/rcdhcp
+  /usr/sbin/rcdhcrelay
+  /usr/sbin/rcdhclient</changelog>
+<changelog author="- rolf@suse.de" date="911908800">- new init scripts for SuSE Linux 6.0</changelog>
+<changelog author="- bs@suse.de" date="910872000">- minor changes for new rpm</changelog>
+<changelog author="- rolf@suse.de" date="906638400">- new version 2.0b1pl6 (stable beta)
+- now with dhcp client and dhcp relay agent
+- added init scripts for relay agent and client
+- changed from $NETDEV_0 to $DHCPD_INTERFACE</changelog>
+<changelog author="- rolf@suse.de" date="898862400">- new version 1.0pl2 fixes two potential input buffer overrun problems
+  that were missed in Patchlevel 1</changelog>
+<changelog author="- rolf@suse.de" date="895492800">- new security patch 1.0pl1 included
+  changed /sbin/init.d/dhcp to run on $NETDEV_0</changelog>
+<changelog author="- rolf@suse.de" date="881755200">- new version 1.0.0  this is not beta any more!</changelog>
+<changelog author="- rolf@suse.de" date="877003200">- switched to dhcp.spec instead of Makefile.Linux</changelog>
+<changelog author="- rolf@suse.de" date="873979200">- Upddate to Version 5 beta 16 and made entry for rc.config and
+  /sbin/init.d for startup/shutdown
+  There is no dhcp client in this package anymore.</changelog>
+<changelog author="- rolf@suse.de" date="866116800">- build the package for the first time</changelog>
+</package>
+
+
+<package pkgid="1cd7ea460f5e4210df54699831f528287bb918f8" name="libextractor" arch="i586">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+<changelog author="- ke@suse.de" date="1148040000">- Fix heap overflow in the asf plugin (CVE-2006-2458) [# 176280].
+- Fix heap overflow in the qt plugin (CVE-2006-2458) [# 176280].</changelog>
+<changelog author="- ke@suse.de" date="1140436800">- Update to version 0.5.10:
+  Fix buffer overflows of the pdf plugin (xpdf derived code).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ke@suse.de" date="1137067200">- Update to version 0.5.9.</changelog>
+<changelog author="- ke@suse.de" date="1134043200">- Update to version 0.5.8.</changelog>
+<changelog author="- ke@suse.de" date="1132574400">- Update to version 0.5.7.
+- basicio-ns-fix.diff: Remove extra qualification.</changelog>
+<changelog author="- dmueller@suse.de" date="1127908800">- add norootforbuild</changelog>
+<changelog author="- ke@suse.de" date="1121860800">- Add libvorbis libvorbis-devel libogg-devel to neededforbuild.</changelog>
+<changelog author="- ke@suse.de" date="1121688000">- Update to version 0.5.2.</changelog>
+<changelog author="- ke@suse.de" date="1119528000">- Update to version 0.5.0:
+  * xpdf related security fixes.
+- Apply libextractor-destdir.diff; for now, do not try to build the
+  Python extension.</changelog>
+<changelog author="- ke@suse.de" date="1104321600">- Update to version 0.4.0:
+  * Improve MP3 support (ID3 tags)
+  * PDF fixes.
+  * Better UTF-8 support.</changelog>
+<changelog author="- ke@suse.de" date="1098100800">- Update to version 0.3.9.</changelog>
+<changelog author="- ke@suse.de" date="1097841600">- Update to version 0.3.8.</changelog>
+<changelog author="- ro@suse.de" date="1097582400">- remove extra version line in devel subpackage</changelog>
+<changelog author="- ke@suse.de" date="1093608000">- New package: version 0.3.4.</changelog>
+</package>
+
+
+<package pkgid="06a9e560a069498df7bbb3a8978b4a6dc9e03164" name="libextractor" arch="ppc">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+<changelog author="- ke@suse.de" date="1148040000">- Fix heap overflow in the asf plugin (CVE-2006-2458) [# 176280].
+- Fix heap overflow in the qt plugin (CVE-2006-2458) [# 176280].</changelog>
+<changelog author="- ke@suse.de" date="1140436800">- Update to version 0.5.10:
+  Fix buffer overflows of the pdf plugin (xpdf derived code).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ke@suse.de" date="1137067200">- Update to version 0.5.9.</changelog>
+<changelog author="- ke@suse.de" date="1134043200">- Update to version 0.5.8.</changelog>
+<changelog author="- ke@suse.de" date="1132574400">- Update to version 0.5.7.
+- basicio-ns-fix.diff: Remove extra qualification.</changelog>
+<changelog author="- dmueller@suse.de" date="1127908800">- add norootforbuild</changelog>
+<changelog author="- ke@suse.de" date="1121860800">- Add libvorbis libvorbis-devel libogg-devel to neededforbuild.</changelog>
+<changelog author="- ke@suse.de" date="1121688000">- Update to version 0.5.2.</changelog>
+<changelog author="- ke@suse.de" date="1119528000">- Update to version 0.5.0:
+  * xpdf related security fixes.
+- Apply libextractor-destdir.diff; for now, do not try to build the
+  Python extension.</changelog>
+<changelog author="- ke@suse.de" date="1104321600">- Update to version 0.4.0:
+  * Improve MP3 support (ID3 tags)
+  * PDF fixes.
+  * Better UTF-8 support.</changelog>
+<changelog author="- ke@suse.de" date="1098100800">- Update to version 0.3.9.</changelog>
+<changelog author="- ke@suse.de" date="1097841600">- Update to version 0.3.8.</changelog>
+<changelog author="- ro@suse.de" date="1097582400">- remove extra version line in devel subpackage</changelog>
+<changelog author="- ke@suse.de" date="1093608000">- New package: version 0.3.4.</changelog>
+</package>
+
+
+<package pkgid="e42db911c0eee82d350b06c1fd2de7951a700a28" name="libextractor" arch="src">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+<changelog author="- ke@suse.de" date="1148040000">- Fix heap overflow in the asf plugin (CVE-2006-2458) [# 176280].
+- Fix heap overflow in the qt plugin (CVE-2006-2458) [# 176280].</changelog>
+<changelog author="- ke@suse.de" date="1140436800">- Update to version 0.5.10:
+  Fix buffer overflows of the pdf plugin (xpdf derived code).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ke@suse.de" date="1137067200">- Update to version 0.5.9.</changelog>
+<changelog author="- ke@suse.de" date="1134043200">- Update to version 0.5.8.</changelog>
+<changelog author="- ke@suse.de" date="1132574400">- Update to version 0.5.7.
+- basicio-ns-fix.diff: Remove extra qualification.</changelog>
+<changelog author="- dmueller@suse.de" date="1127908800">- add norootforbuild</changelog>
+<changelog author="- ke@suse.de" date="1121860800">- Add libvorbis libvorbis-devel libogg-devel to neededforbuild.</changelog>
+<changelog author="- ke@suse.de" date="1121688000">- Update to version 0.5.2.</changelog>
+<changelog author="- ke@suse.de" date="1119528000">- Update to version 0.5.0:
+  * xpdf related security fixes.
+- Apply libextractor-destdir.diff; for now, do not try to build the
+  Python extension.</changelog>
+<changelog author="- ke@suse.de" date="1104321600">- Update to version 0.4.0:
+  * Improve MP3 support (ID3 tags)
+  * PDF fixes.
+  * Better UTF-8 support.</changelog>
+<changelog author="- ke@suse.de" date="1098100800">- Update to version 0.3.9.</changelog>
+<changelog author="- ke@suse.de" date="1097841600">- Update to version 0.3.8.</changelog>
+<changelog author="- ro@suse.de" date="1097582400">- remove extra version line in devel subpackage</changelog>
+<changelog author="- ke@suse.de" date="1093608000">- New package: version 0.3.4.</changelog>
+</package>
+
+
+<package pkgid="39753714ea4afc56bb957742bc74e31126beb474" name="libextractor" arch="x86_64">
+<version epoch="0" ver="0.5.10" rel="12.2"/>
+<changelog author="- ke@suse.de" date="1148040000">- Fix heap overflow in the asf plugin (CVE-2006-2458) [# 176280].
+- Fix heap overflow in the qt plugin (CVE-2006-2458) [# 176280].</changelog>
+<changelog author="- ke@suse.de" date="1140436800">- Update to version 0.5.10:
+  Fix buffer overflows of the pdf plugin (xpdf derived code).</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- ke@suse.de" date="1137067200">- Update to version 0.5.9.</changelog>
+<changelog author="- ke@suse.de" date="1134043200">- Update to version 0.5.8.</changelog>
+<changelog author="- ke@suse.de" date="1132574400">- Update to version 0.5.7.
+- basicio-ns-fix.diff: Remove extra qualification.</changelog>
+<changelog author="- dmueller@suse.de" date="1127908800">- add norootforbuild</changelog>
+<changelog author="- ke@suse.de" date="1121860800">- Add libvorbis libvorbis-devel libogg-devel to neededforbuild.</changelog>
+<changelog author="- ke@suse.de" date="1121688000">- Update to version 0.5.2.</changelog>
+<changelog author="- ke@suse.de" date="1119528000">- Update to version 0.5.0:
+  * xpdf related security fixes.
+- Apply libextractor-destdir.diff; for now, do not try to build the
+  Python extension.</changelog>
+<changelog author="- ke@suse.de" date="1104321600">- Update to version 0.4.0:
+  * Improve MP3 support (ID3 tags)
+  * PDF fixes.
+  * Better UTF-8 support.</changelog>
+<changelog author="- ke@suse.de" date="1098100800">- Update to version 0.3.9.</changelog>
+<changelog author="- ke@suse.de" date="1097841600">- Update to version 0.3.8.</changelog>
+<changelog author="- ro@suse.de" date="1097582400">- remove extra version line in devel subpackage</changelog>
+<changelog author="- ke@suse.de" date="1093608000">- New package: version 0.3.4.</changelog>
+</package>
+
+<package pkgid="a144f4f4e4dd6a949f7b0ca7c79c3bb8a2e56851" name="ivman" arch="i586">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+<changelog author="- hvogel@suse.de" date="1148040000">- make the default userconfigs the same as the system configs so
+  ivman started as user uses halmount too. [#173524]</changelog>
+<changelog author="- lnussel@suse.de" date="1142510400">- halmount.py:
+  * don't use obsolete volume.policy.desired_mount_point (#158266)
+  * quote unicode characters/catch exception if python doesn't like
+  the string (#158266)</changelog>
+<changelog author="- lnussel@suse.de" date="1140609600">- halmount.py:
+  * let hal choose the mountpoint if it says mountpoint
+  invalid (#146306)
+  * catch dbus errors upon connect to avoid ugly message if hal is
+  not running/not ready yet
+- install symlink halmount -&gt; halmount.py to match config file</changelog>
+<changelog author="- ro@suse.de" date="1139832000">- specfile cleanup</changelog>
+<changelog author="- hvogel@suse.de" date="1139832000">- Initial package, version 0.6.9</changelog>
+</package>
+
+<package pkgid="140b8a8de66f5f1ba8d8160f2ee2054e6fd7e138" name="ivman" arch="ppc">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+<changelog author="- hvogel@suse.de" date="1148040000">- make the default userconfigs the same as the system configs so
+  ivman started as user uses halmount too. [#173524]</changelog>
+<changelog author="- lnussel@suse.de" date="1142510400">- halmount.py:
+  * don't use obsolete volume.policy.desired_mount_point (#158266)
+  * quote unicode characters/catch exception if python doesn't like
+  the string (#158266)</changelog>
+<changelog author="- lnussel@suse.de" date="1140609600">- halmount.py:
+  * let hal choose the mountpoint if it says mountpoint
+  invalid (#146306)
+  * catch dbus errors upon connect to avoid ugly message if hal is
+  not running/not ready yet
+- install symlink halmount -&gt; halmount.py to match config file</changelog>
+<changelog author="- ro@suse.de" date="1139832000">- specfile cleanup</changelog>
+<changelog author="- hvogel@suse.de" date="1139832000">- Initial package, version 0.6.9</changelog>
+</package>
+
+<package pkgid="8210ad822c977350b7682b02e42934f2dee22ab5" name="ivman" arch="src">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+<changelog author="- hvogel@suse.de" date="1148040000">- make the default userconfigs the same as the system configs so
+  ivman started as user uses halmount too. [#173524]</changelog>
+<changelog author="- lnussel@suse.de" date="1142510400">- halmount.py:
+  * don't use obsolete volume.policy.desired_mount_point (#158266)
+  * quote unicode characters/catch exception if python doesn't like
+  the string (#158266)</changelog>
+<changelog author="- lnussel@suse.de" date="1140609600">- halmount.py:
+  * let hal choose the mountpoint if it says mountpoint
+  invalid (#146306)
+  * catch dbus errors upon connect to avoid ugly message if hal is
+  not running/not ready yet
+- install symlink halmount -&gt; halmount.py to match config file</changelog>
+<changelog author="- ro@suse.de" date="1139832000">- specfile cleanup</changelog>
+<changelog author="- hvogel@suse.de" date="1139832000">- Initial package, version 0.6.9</changelog>
+</package>
+
+<package pkgid="4430628c022d87831285e2c8b42c26638b182987" name="ivman" arch="x86_64">
+<version epoch="0" ver="0.6.9" rel="16.3"/>
+<changelog author="- hvogel@suse.de" date="1148040000">- make the default userconfigs the same as the system configs so
+  ivman started as user uses halmount too. [#173524]</changelog>
+<changelog author="- lnussel@suse.de" date="1142510400">- halmount.py:
+  * don't use obsolete volume.policy.desired_mount_point (#158266)
+  * quote unicode characters/catch exception if python doesn't like
+  the string (#158266)</changelog>
+<changelog author="- lnussel@suse.de" date="1140609600">- halmount.py:
+  * let hal choose the mountpoint if it says mountpoint
+  invalid (#146306)
+  * catch dbus errors upon connect to avoid ugly message if hal is
+  not running/not ready yet
+- install symlink halmount -&gt; halmount.py to match config file</changelog>
+<changelog author="- ro@suse.de" date="1139832000">- specfile cleanup</changelog>
+<changelog author="- hvogel@suse.de" date="1139832000">- Initial package, version 0.6.9</changelog>
+</package>
+<package pkgid="5f64cb2a850f614871e7ce39e5927f2f16b138c7" name="util-linux-crypto" arch="i586">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+<changelog author="- hvogel@suse.de" date="1147780800">- Fix cryptsetup to work when the device does not exist yet
+  [#175931]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mmj@suse.de" date="1134993600">- Remove symlinks to hashalot we don't want</changelog>
+<changelog author="- hvogel@suse.de" date="1129204800">- Fix uninitialized var in dmconvert. Add
+  * dmconvert-0.2-uninitialized.patch
+- Fix return value in cryptsetup. Add
+  * cryptsetup-0.1-retval.patch</changelog>
+<changelog author="- hvogel@suse.de" date="1120046400">- Link cryptsetup static so it can be in /sbin and you can get
+  /usr over nfs or even crypted</changelog>
+<changelog author="- hvogel@suse.de" date="1115640000">- New package, Version 2.12q</changelog>
+</package>
+<package pkgid="e63397586ea3e175876cc4dd476e847eea0e0f2e" name="util-linux-crypto" arch="ppc">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+<changelog author="- hvogel@suse.de" date="1147780800">- Fix cryptsetup to work when the device does not exist yet
+  [#175931]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mmj@suse.de" date="1134993600">- Remove symlinks to hashalot we don't want</changelog>
+<changelog author="- hvogel@suse.de" date="1129204800">- Fix uninitialized var in dmconvert. Add
+  * dmconvert-0.2-uninitialized.patch
+- Fix return value in cryptsetup. Add
+  * cryptsetup-0.1-retval.patch</changelog>
+<changelog author="- hvogel@suse.de" date="1120046400">- Link cryptsetup static so it can be in /sbin and you can get
+  /usr over nfs or even crypted</changelog>
+<changelog author="- hvogel@suse.de" date="1115640000">- New package, Version 2.12q</changelog>
+</package>
+<package pkgid="316f825d4fd1220fda20df3e4018da6e4ebbc076" name="util-linux-crypto" arch="src">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+<changelog author="- hvogel@suse.de" date="1147780800">- Fix cryptsetup to work when the device does not exist yet
+  [#175931]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mmj@suse.de" date="1134993600">- Remove symlinks to hashalot we don't want</changelog>
+<changelog author="- hvogel@suse.de" date="1129204800">- Fix uninitialized var in dmconvert. Add
+  * dmconvert-0.2-uninitialized.patch
+- Fix return value in cryptsetup. Add
+  * cryptsetup-0.1-retval.patch</changelog>
+<changelog author="- hvogel@suse.de" date="1120046400">- Link cryptsetup static so it can be in /sbin and you can get
+  /usr over nfs or even crypted</changelog>
+<changelog author="- hvogel@suse.de" date="1115640000">- New package, Version 2.12q</changelog>
+</package>
+<package pkgid="8b428d265f0998310d65412f56babd53d4bced53" name="util-linux-crypto" arch="x86_64">
+<version epoch="0" ver="2.12a" rel="14.2"/>
+<changelog author="- hvogel@suse.de" date="1147780800">- Fix cryptsetup to work when the device does not exist yet
+  [#175931]</changelog>
+<changelog author="- mls@suse.de" date="1138190400">- converted neededforbuild to BuildRequires</changelog>
+<changelog author="- mmj@suse.de" date="1134993600">- Remove symlinks to hashalot we don't want</changelog>
+<changelog author="- hvogel@suse.de" date="1129204800">- Fix uninitialized var in dmconvert. Add
+  * dmconvert-0.2-uninitialized.patch
+- Fix return value in cryptsetup. Add
+  * cryptsetup-0.1-retval.patch</changelog>
+<changelog author="- hvogel@suse.de" date="1120046400">- Link cryptsetup static so it can be in /sbin and you can get
+  /usr over nfs or even crypted</changelog>
+<changelog author="- hvogel@suse.de" date="1115640000">- New package, Version 2.12q</changelog>
+</package>
+</otherdata>
diff --git a/devel/devel.dmacvicar/repodata/patch-avahi-1399.xml b/devel/devel.dmacvicar/repodata/patch-avahi-1399.xml
new file mode 100644 (file)
index 0000000..577123d
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="60fad57135fa06d1b6c5c28ffe97fdd9"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="avahi-1399"
+    timestamp="1147788939"
+    engine="1.0">
+  <yum:name>avahi</yum:name>
+  <summary lang="en">buffer overflow in avahi</summary>
+  <summary lang="de">Pufferüberlauf in avahi</summary>
+  <description lang="en">A local exploitable buffer overflow has been found in 
+avahi. The impact is low since avahi is running in a chroot 
+environment.
+</description>
+  <description lang="de">Ein lokal ausnutzbarer Pufferüberlauf wurde in avahi 
+gefunden Da avahi in einer chroot-Umgebung läuft,ist das 
+Risiko eines Angriffes gering. 
+</description>
+  <yum:version ver="1399" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="avahi" epoch="0" ver="0.6.5" rel="29.3" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>avahi</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="0.6.5" rel="29.3"/>
+      <checksum type="sha" pkgid="YES">0591487b293027292fb55d3fd3402e5dd2cb4184</checksum>
+      <time file="1147913766" build="1147788939"/>
+      <size package="228836" installed="734250" archive="747084"/>
+      <location href="rpm/i586/avahi-0.6.5-29.3.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="avahi" epoch="0" ver="0.6.5" rel="29.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="avahi"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/avahi-0.6.5-29.3.i586.patch.rpm"/>
+          <checksum type="sha">3bec2355538bf10ce08107e128b95447</checksum>
+          <time file="1147915506" build="1147788939"/>
+          <size package="110247" archive="267560"/>
+          <base-version epoch="0" ver="0.6.5" rel="27"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>avahi</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.6.5" rel="29.3"/>
+      <checksum type="sha" pkgid="YES">4af6068aa9c160924eba7014a5885c2767b51fb0</checksum>
+      <time file="1147913823" build="1147790854"/>
+      <size package="253253" installed="842286" archive="855120"/>
+      <location href="rpm/ppc/avahi-0.6.5-29.3.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="avahi" epoch="0" ver="0.6.5" rel="29.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="avahi"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/avahi-0.6.5-29.3.ppc.patch.rpm"/>
+          <checksum type="sha">c3700a5370c2014d510d4ef4d7e3353e</checksum>
+          <time file="1147915510" build="1147790854"/>
+          <size package="121499" archive="325288"/>
+          <base-version epoch="0" ver="0.6.5" rel="26"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>avahi</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.6.5" rel="29.3"/>
+      <checksum type="sha" pkgid="YES">a46d02c6fcf43387c9424c35933f9c0476d35a4b</checksum>
+      <time file="1147913752" build="1147789031"/>
+      <size package="249764" installed="783150" archive="796004"/>
+      <location href="rpm/x86_64/avahi-0.6.5-29.3.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="avahi" epoch="0" ver="0.6.5" rel="29.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="avahi"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/avahi-0.6.5-29.3.x86_64.patch.rpm"/>
+          <checksum type="sha">e7703d263ff0d05e63001a95ce6ab932</checksum>
+          <time file="1147915513" build="1147789031"/>
+          <size package="127129" archive="297188"/>
+          <base-version epoch="0" ver="0.6.5" rel="27"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-dhcdbd-1315.xml b/devel/devel.dmacvicar/repodata/patch-dhcdbd-1315.xml
new file mode 100644 (file)
index 0000000..608066a
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="670b3304059e53645aa19d532356bd5a"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="dhcdbd-1315"
+    timestamp="1147273419"
+    engine="1.0">
+  <yum:name>dhcdbd</yum:name>
+  <summary lang="en">bugfix for dhcdbd (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <summary lang="de">bugfix für dhcdbd (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <description lang="en">Support DHCLIENT_HOSTNAME_OPTION, the ability to send the 
+hostname to the DHCP server, in NetworkManager. 
+</description>
+  <description lang="de">Die Unterstützung für DHCLIENT_HOSTNAME_OPTION wurde 
+hinzugefügt, um den Hostnamen auch von NetworkManager aus 
+an den DHCP server senden zu können. 
+</description>
+  <yum:version ver="1315" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="dhcdbd" epoch="0" ver="1.12" rel="14.2" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcdbd</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="1.12" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">23b988b5b6e50d7cfda50dda22508c5f1dd07307</checksum>
+      <time file="1147274480" build="1147273419"/>
+      <size package="60155" installed="175265" archive="176720"/>
+      <location href="rpm/i586/dhcdbd-1.12-14.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcdbd" epoch="0" ver="1.12" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcdbd"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/dhcdbd-1.12-14.2.i586.patch.rpm"/>
+          <checksum type="sha">2ee00cdff1bfb0caf08b4162f1787a0e</checksum>
+          <time file="1147275004" build="1147273419"/>
+          <size package="47088" archive="131344"/>
+          <base-version epoch="0" ver="1.12" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/dhcdbd-1.12-14_14.2.i586.delta.rpm"/>
+          <checksum type="sha">800c3459957baa6f75645c8a7f407595b929f264</checksum>
+          <time file="1148038804" build="1147273419"/>
+          <size package="37582" archive="0"/>
+          <base-version epoch="0" ver="1.12" rel="14" md5sum="13a13849796c7b16d84498f1fb590388" buildtime="1145893101" sequence_info="dhcdbd-1.12-14-18c0c084a7fb92cbfdad425c0b22a9d13510"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcdbd</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="1.12" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">da37d6c81230024f202fbb92107ab88ade872bd3</checksum>
+      <time file="1147274525" build="1147273485"/>
+      <size package="64668" installed="184913" archive="186368"/>
+      <location href="rpm/ppc/dhcdbd-1.12-14.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcdbd" epoch="0" ver="1.12" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcdbd"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/dhcdbd-1.12-14.2.ppc.patch.rpm"/>
+          <checksum type="sha">2462a9b820e3add6ef6f128390dfefd1</checksum>
+          <time file="1147275005" build="1147273485"/>
+          <size package="51632" archive="140992"/>
+          <base-version epoch="0" ver="1.12" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/dhcdbd-1.12-14_14.2.ppc.delta.rpm"/>
+          <checksum type="sha">38b0c5609512ac8ccfa0954303b71400e4f87d14</checksum>
+          <time file="1148038805" build="1147273485"/>
+          <size package="36168" archive="0"/>
+          <base-version epoch="0" ver="1.12" rel="14" md5sum="ca86a16d80546ea015da13233a31ef5e" buildtime="1145894258" sequence_info="dhcdbd-1.12-14-e23f21581c611f4a36414cf188b923da3510"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcdbd</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="1.12" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">bca8cb431261bf677ea69c848c1d54a34b16189a</checksum>
+      <time file="1147274612" build="1147273521"/>
+      <size package="65787" installed="198761" archive="200216"/>
+      <location href="rpm/x86_64/dhcdbd-1.12-14.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcdbd" epoch="0" ver="1.12" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcdbd"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/dhcdbd-1.12-14.2.x86_64.patch.rpm"/>
+          <checksum type="sha">5ddb79da7ff32e0c9a31e5350058dae3</checksum>
+          <time file="1147275006" build="1147273521"/>
+          <size package="52648" archive="154840"/>
+          <base-version epoch="0" ver="1.12" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/dhcdbd-1.12-14_14.2.x86_64.delta.rpm"/>
+          <checksum type="sha">e6b80c922688259f32b449d14cafae5ff6d659d3</checksum>
+          <time file="1148038807" build="1147273521"/>
+          <size package="41147" archive="0"/>
+          <base-version epoch="0" ver="1.12" rel="14" md5sum="beceac027987af04f2a75886daf5a1aa" buildtime="1145892918" sequence_info="dhcdbd-1.12-14-d57ad777852228a9c6f50ddf5ef7a3503510"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-dhcp-1316.xml b/devel/devel.dmacvicar/repodata/patch-dhcp-1316.xml
new file mode 100644 (file)
index 0000000..0884b92
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="1f629c038766cae9904eb7722a03aa3d"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="dhcp-1316"
+    timestamp="1147273495"
+    engine="1.0">
+  <yum:name>dhcp</yum:name>
+  <summary lang="en">bugfix for dhcp (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <summary lang="de">bugfix für dhcp (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <description lang="en">Support DHCLIENT_HOSTNAME_OPTION, the ability to send the 
+hostname to the DHCP server, in NetworkManager. 
+</description>
+  <description lang="de">Die Unterstützung für DHCLIENT_HOSTNAME_OPTION wurde  
+hinzugefügt, um den Hostnamen auch von NetworkManager aus 
+an den DHCP server senden zu können.
+</description>
+  <yum:version ver="1316" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="dhcp" epoch="0" ver="3.0.3" rel="21.1" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="3.0.3" rel="21.1"/>
+      <checksum type="sha" pkgid="YES">7ba58f2b9498981c5f20d25f9675a6592317b694</checksum>
+      <time file="1147274483" build="1147273495"/>
+      <size package="416888" installed="1304256" archive="1308864"/>
+      <location href="rpm/i586/dhcp-3.0.3-21.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="21.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/dhcp-3.0.3-21.1.i586.patch.rpm"/>
+          <checksum type="sha">cccdf85a31026de86c5a79313458f88b</checksum>
+          <time file="1147275009" build="1147273495"/>
+          <size package="47792" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="3.0.3" rel="21.1"/>
+      <checksum type="sha" pkgid="YES">739feea694870b250262a846af418e4c3d887ecd</checksum>
+      <time file="1147274527" build="1147273702"/>
+      <size package="426085" installed="1364756" archive="1369364"/>
+      <location href="rpm/ppc/dhcp-3.0.3-21.1.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="21.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/dhcp-3.0.3-21.1.ppc.patch.rpm"/>
+          <checksum type="sha">f0ffa669a79ed37a10aff19f1af2db9a</checksum>
+          <time file="1147275010" build="1147273702"/>
+          <size package="47776" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="3.0.3" rel="21.1"/>
+      <checksum type="sha" pkgid="YES">7042e04a0b649bcc0a2100ddde62e8fb1ce82927</checksum>
+      <time file="1147274618" build="1147273648"/>
+      <size package="437486" installed="1373452" archive="1378060"/>
+      <location href="rpm/x86_64/dhcp-3.0.3-21.1.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="21.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/dhcp-3.0.3-21.1.x86_64.patch.rpm"/>
+          <checksum type="sha">f9d50cdf5b11787f81483cb6df5e5b28</checksum>
+          <time file="1147275011" build="1147273648"/>
+          <size package="47740" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-dhcp-1424.xml b/devel/devel.dmacvicar/repodata/patch-dhcp-1424.xml
new file mode 100644 (file)
index 0000000..95ab0c4
--- /dev/null
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="76b5b9d93d79077b59b23acb70fdcacb"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="dhcp-1424"
+    timestamp="1147706169"
+    engine="1.0">
+  <yum:name>dhcp</yum:name>
+  <summary lang="en">bugfix for dhcp (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <summary lang="de">bugfix für dhcp (DHCLIENT_HOSTNAME_OPTION)</summary>
+  <description lang="en">Support DHCLIENT_HOSTNAME_OPTION, the ability to send the 
+hostname to the DHCP server, in NetworkManager.
+</description>
+  <description lang="de">Die Unterstützung für DHCLIENT_HOSTNAME_OPTION wurde  
+hinzugefügt, um den Hostnamen auch von NetworkManager aus 
+an den DHCP server senden zu können.
+</description>
+  <yum:version ver="1424" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="dhcp" epoch="0" ver="3.0.3" rel="23.1" flags="EQ"/>
+  <rpm:entry kind="atom" name="dhcp-client" epoch="0" ver="3.0.3" rel="23.1" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">22a69d9e4b792e588b4542659fa4ac329fd9e5a8</checksum>
+      <time file="1148255884" build="1147706169"/>
+      <size package="416936" installed="1304256" archive="1308864"/>
+      <location href="rpm/i586/dhcp-3.0.3-23.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/dhcp-3.0.3-23.1.i586.patch.rpm"/>
+          <checksum type="sha">e007eecc428490c79053aad071c24b48</checksum>
+          <time file="1148257806" build="1147706169"/>
+          <size package="47815" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+          <base-version epoch="0" ver="3.0.3" rel="21.1"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">f6ba046b24618a07a8b0cc1477e039a8150c0ab1</checksum>
+      <time file="1148256054" build="1147712767"/>
+      <size package="426388" installed="1364756" archive="1369364"/>
+      <location href="rpm/ppc/dhcp-3.0.3-23.1.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/dhcp-3.0.3-23.1.ppc.patch.rpm"/>
+          <checksum type="sha">7ff0334f230c714d8c061abaca434ecb</checksum>
+          <time file="1148257811" build="1147712767"/>
+          <size package="47799" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+          <base-version epoch="0" ver="3.0.3" rel="21.1"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">5b8842037e72ca22fa32df2516962822e4c6a313</checksum>
+      <time file="1148255437" build="1147706078"/>
+      <size package="436505" installed="1373452" archive="1378060"/>
+      <location href="rpm/x86_64/dhcp-3.0.3-23.1.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/dhcp-3.0.3-23.1.x86_64.patch.rpm"/>
+          <checksum type="sha">3bd502ca55bbd1ae94d3faeede5b8eca</checksum>
+          <time file="1148257815" build="1147706078"/>
+          <size package="47763" archive="124"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+          <base-version epoch="0" ver="3.0.3" rel="21.1"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp-client</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">f9e69cf37731c8b7323c34d46d20b444d551c765</checksum>
+      <time file="1148255884" build="1147706169"/>
+      <size package="242150" installed="439911" archive="441192"/>
+      <location href="rpm/i586/dhcp-client-3.0.3-23.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp-client" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp-client"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/dhcp-client-3.0.3-23.1.i586.patch.rpm"/>
+          <checksum type="sha">feedb57544ee7616617c1b90181ab7e4</checksum>
+          <time file="1148257819" build="1147706169"/>
+          <size package="220283" archive="414972"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/dhcp-client-3.0.3-21_23.1.i586.delta.rpm"/>
+          <checksum type="sha">a85b3b1b9bbad78484a55b3c4d16324abd1730eb</checksum>
+          <time file="1148257820" build="1147706169"/>
+          <size package="61141" archive="0"/>
+          <base-version epoch="0" ver="3.0.3" rel="21" md5sum="0dcadc45c0f7b181275e74101f7dbe32" buildtime="1146555332" sequence_info="dhcp-client-3.0.3-21-180aa472147d1b6a5aa6c290d019324f0160"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp-client</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">729e9b2704cab26322040c44f0315280c9db0eab</checksum>
+      <time file="1148256055" build="1147712767"/>
+      <size package="254375" installed="507931" archive="509212"/>
+      <location href="rpm/ppc/dhcp-client-3.0.3-23.1.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp-client" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp-client"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/dhcp-client-3.0.3-23.1.ppc.patch.rpm"/>
+          <checksum type="sha">a2f0b0e15fc09b0e743a4ea4dce1ad30</checksum>
+          <time file="1148257821" build="1147712767"/>
+          <size package="232620" archive="482992"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/dhcp-client-3.0.3-21_23.1.ppc.delta.rpm"/>
+          <checksum type="sha">d80d445a449f897d417a118c9a76cdd7d599968b</checksum>
+          <time file="1148257822" build="1147712767"/>
+          <size package="65205" archive="0"/>
+          <base-version epoch="0" ver="3.0.3" rel="21" md5sum="469761c9318b57189a606febdcd745b3" buildtime="1146553692" sequence_info="dhcp-client-3.0.3-21-0fc4d3e276cacd8ab30de5750e48e1c20160"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dhcp-client</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="3.0.3" rel="23.1"/>
+      <checksum type="sha" pkgid="YES">a45eeef08edd16af1b70ca359d1032cf01e28de3</checksum>
+      <time file="1148255438" build="1147706078"/>
+      <size package="264996" installed="508115" archive="509396"/>
+      <location href="rpm/x86_64/dhcp-client-3.0.3-23.1.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dhcp-client" epoch="0" ver="3.0.3" rel="23.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dhcp-client"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/dhcp-client-3.0.3-23.1.x86_64.patch.rpm"/>
+          <checksum type="sha">d5be6ddbe808900dd4bdba0fed6a066d</checksum>
+          <time file="1148257823" build="1147706078"/>
+          <size package="243059" archive="483176"/>
+          <base-version epoch="0" ver="3.0.3" rel="21"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/dhcp-client-3.0.3-21_23.1.x86_64.delta.rpm"/>
+          <checksum type="sha">dd33007b25dc39fb6411aa3860eb5dd119933e26</checksum>
+          <time file="1148257824" build="1147706078"/>
+          <size package="64250" archive="0"/>
+          <base-version epoch="0" ver="3.0.3" rel="21" md5sum="f03a8de7a1ac0fec4cc86fed45d001df" buildtime="1146556239" sequence_info="dhcp-client-3.0.3-21-fe4e08f12079b634cf19fcbad1273c6d0160"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-dovecot-1398.xml b/devel/devel.dmacvicar/repodata/patch-dovecot-1398.xml
new file mode 100644 (file)
index 0000000..30b04ef
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="5c3f732a6632891d2ca0ccff2efc1e44"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="dovecot-1398"
+    timestamp="1147881431"
+    engine="1.0">
+  <yum:name>dovecot</yum:name>
+  <summary lang="en">dovecot security update</summary>
+  <summary lang="de">dovecot Sicherheitsupdate</summary>
+  <description lang="en">Users could potentially find out mailbox names of other 
+users (CVE-2006-2414).
+</description>
+  <description lang="de">Benutzer konnten unter Umständen die Mailboxnamen anderer 
+Benutzer herausfinden (CVE-2006-2414).
+</description>
+  <yum:version ver="1398" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="dovecot" epoch="0" ver="1.0.beta3" rel="13.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dovecot</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="1.0.beta3" rel="13.2"/>
+      <checksum type="sha" pkgid="YES">4d7988178018b1d5af490ac899d1cdfd2419ff52</checksum>
+      <time file="1147913770" build="1147881431"/>
+      <size package="1262520" installed="3054198" archive="3064772"/>
+      <location href="rpm/i586/dovecot-1.0.beta3-13.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dovecot" epoch="0" ver="1.0.beta3" rel="13.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dovecot"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/dovecot-1.0.beta3-13.2.i586.patch.rpm"/>
+          <checksum type="sha">f7acbb686cdea8a5fbf8f975cbde5c74</checksum>
+          <time file="1147914005" build="1147881431"/>
+          <size package="696344" archive="1833332"/>
+          <base-version epoch="0" ver="1.0.beta3" rel="13"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dovecot</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="1.0.beta3" rel="13.2"/>
+      <checksum type="sha" pkgid="YES">1c550fddb834bce56275ba603276b27296e03cf7</checksum>
+      <time file="1147913832" build="1147881837"/>
+      <size package="1380495" installed="3828854" archive="3839428"/>
+      <location href="rpm/ppc/dovecot-1.0.beta3-13.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dovecot" epoch="0" ver="1.0.beta3" rel="13.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dovecot"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/dovecot-1.0.beta3-13.2.ppc.patch.rpm"/>
+          <checksum type="sha">d6cd013a774a038178b6e120b584188b</checksum>
+          <time file="1147914011" build="1147881837"/>
+          <size package="834160" archive="2286128"/>
+          <base-version epoch="0" ver="1.0.beta3" rel="13"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>dovecot</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="1.0.beta3" rel="13.2"/>
+      <checksum type="sha" pkgid="YES">57cd740de9f629fae4c0f6b665634ccfd40845fc</checksum>
+      <time file="1147913757" build="1147881014"/>
+      <size package="1409035" installed="3363960" archive="3374592"/>
+      <location href="rpm/x86_64/dovecot-1.0.beta3-13.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="dovecot" epoch="0" ver="1.0.beta3" rel="13.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="dovecot"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/dovecot-1.0.beta3-13.2.x86_64.patch.rpm"/>
+          <checksum type="sha">a354c695715c961588fab0a2fa3bc680</checksum>
+          <time file="1147914014" build="1147881014"/>
+          <size package="885715" archive="2212820"/>
+          <base-version epoch="0" ver="1.0.beta3" rel="13"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-ivman-1423.xml b/devel/devel.dmacvicar/repodata/patch-ivman-1423.xml
new file mode 100644 (file)
index 0000000..f506e72
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="c9571809db5751948a0142b8f281f8ad"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="ivman-1423"
+    timestamp="1148058366"
+    engine="1.0">
+  <yum:name>ivman</yum:name>
+  <summary lang="en">Fix user configuration</summary>
+  <summary lang="de">Fix User Konfiguration</summary>
+  <description lang="en">Change user configuration files to use halmount
+</description>
+  <description lang="de">Ändern der User Konfiguration um halmount zu benutzen
+</description>
+  <yum:version ver="1423" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="ivman" epoch="0" ver="0.6.9" rel="16.3" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>ivman</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="0.6.9" rel="16.3"/>
+      <checksum type="sha" pkgid="YES">a144f4f4e4dd6a949f7b0ca7c79c3bb8a2e56851</checksum>
+      <time file="1148234098" build="1148058366"/>
+      <size package="55707" installed="133453" archive="136676"/>
+      <location href="rpm/i586/ivman-0.6.9-16.3.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="ivman" epoch="0" ver="0.6.9" rel="16.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="ivman"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/ivman-0.6.9-16.3.i586.patch.rpm"/>
+          <checksum type="sha">1e6b27edc216857a6af8a3a751e34054</checksum>
+          <time file="1148243402" build="1148058366"/>
+          <size package="27015" archive="60352"/>
+          <base-version epoch="0" ver="0.6.9" rel="16"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/ivman-0.6.9-16_16.3.i586.delta.rpm"/>
+          <checksum type="sha">3304ebc17a624fd1df475472e4e94d7d3487c750</checksum>
+          <time file="1148243404" build="1148058366"/>
+          <size package="13947" archive="0"/>
+          <base-version epoch="0" ver="0.6.9" rel="16" md5sum="bc27b1b8bcd713e757deb4233bbf702c" buildtime="1146560193" sequence_info="ivman-0.6.9-16-33111e38212cfedff68daa2da5b04d4ba147"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>ivman</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.6.9" rel="16.3"/>
+      <checksum type="sha" pkgid="YES">140b8a8de66f5f1ba8d8160f2ee2054e6fd7e138</checksum>
+      <time file="1148241823" build="1148061883"/>
+      <size package="60255" installed="141761" archive="144984"/>
+      <location href="rpm/ppc/ivman-0.6.9-16.3.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="ivman" epoch="0" ver="0.6.9" rel="16.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="ivman"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/ivman-0.6.9-16.3.ppc.patch.rpm"/>
+          <checksum type="sha">1a058bef110839d5a7b5115274d8a893</checksum>
+          <time file="1148243404" build="1148061883"/>
+          <size package="31243" archive="68660"/>
+          <base-version epoch="0" ver="0.6.9" rel="16"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/ivman-0.6.9-16_16.3.ppc.delta.rpm"/>
+          <checksum type="sha">04c544e152f614cd44ec745475f285d4a3264fe0</checksum>
+          <time file="1148243405" build="1148061883"/>
+          <size package="14178" archive="0"/>
+          <base-version epoch="0" ver="0.6.9" rel="16" md5sum="234f238b545b92698d92225c7d983d61" buildtime="1146613209" sequence_info="ivman-0.6.9-16-ea00563ab2d4a64c97ecc35ed4171f25a147"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>ivman</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.6.9" rel="16.3"/>
+      <checksum type="sha" pkgid="YES">4430628c022d87831285e2c8b42c26638b182987</checksum>
+      <time file="1148233996" build="1148057434"/>
+      <size package="57980" installed="139137" archive="142360"/>
+      <location href="rpm/x86_64/ivman-0.6.9-16.3.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="ivman" epoch="0" ver="0.6.9" rel="16.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="ivman"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/ivman-0.6.9-16.3.x86_64.patch.rpm"/>
+          <checksum type="sha">51bb02fbb2f73139ccdd8466921641ae</checksum>
+          <time file="1148243405" build="1148057434"/>
+          <size package="29260" archive="66036"/>
+          <base-version epoch="0" ver="0.6.9" rel="16"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/ivman-0.6.9-16_16.3.x86_64.delta.rpm"/>
+          <checksum type="sha">7975c96ffab442225e4f2f8b55b97652cc2215bb</checksum>
+          <time file="1148243406" build="1148057434"/>
+          <size package="14402" archive="0"/>
+          <base-version epoch="0" ver="0.6.9" rel="16" md5sum="e199adfc9224ca62aa1b4244fb7de0a6" buildtime="1146560504" sequence_info="ivman-0.6.9-16-e8f1bf4113db053e2248a3fd12c05959a147"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-libextractor-1426.xml b/devel/devel.dmacvicar/repodata/patch-libextractor-1426.xml
new file mode 100644 (file)
index 0000000..b647e3c
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="6ed117457d9598f3beb36ca8cfccc40c"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="libextractor-1426"
+    timestamp="1148260482"
+    engine="1.0">
+  <yum:name>libextractor</yum:name>
+  <summary lang="en">Fix Heap Overflows</summary>
+  <summary lang="de">Fehlerbehebung für Heap-Overflows</summary>
+  <description lang="en">Fix heap overflow in the asf plugin (CVE-2006-2458) [# 
+176280]. Fix heap overflow in the qt plugin (CVE-2006-2458) 
+[# 176280].
+</description>
+  <description lang="de">Heap-Overflow im asf- und qt-Plugin behoben  
+(CVE-2006-2458) [# 176280].
+</description>
+  <yum:version ver="1426" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="libextractor" epoch="0" ver="0.5.10" rel="12.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>libextractor</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="0.5.10" rel="12.2"/>
+      <checksum type="sha" pkgid="YES">1cd7ea460f5e4210df54699831f528287bb918f8</checksum>
+      <time file="1148261052" build="1148260482"/>
+      <size package="48651" installed="102229" archive="104516"/>
+      <location href="rpm/i586/libextractor-0.5.10-12.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="libextractor" epoch="0" ver="0.5.10" rel="12.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="libextractor"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/libextractor-0.5.10-12.2.i586.patch.rpm"/>
+          <checksum type="sha">771dd5d2951b189b382aad55cad01d9e</checksum>
+          <time file="1148265018" build="1148260482"/>
+          <size package="19749" archive="30384"/>
+          <base-version epoch="0" ver="0.5.10" rel="12"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/libextractor-0.5.10-12_12.2.i586.delta.rpm"/>
+          <checksum type="sha">dec661473bbb323f2c54f827c5dc27d693af1771</checksum>
+          <time file="1148265021" build="1148260482"/>
+          <size package="16788" archive="0"/>
+          <base-version epoch="0" ver="0.5.10" rel="12" md5sum="5ff5d5ab88a480a57f57714a1b878eb2" buildtime="1146569511" sequence_info="libextractor-0.5.10-12-9a21f65b86976d7142fdd0da86179de74550"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>libextractor</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.5.10" rel="12.2"/>
+      <checksum type="sha" pkgid="YES">06a9e560a069498df7bbb3a8978b4a6dc9e03164</checksum>
+      <time file="1148263567" build="1148262928"/>
+      <size package="52836" installed="115549" archive="117836"/>
+      <location href="rpm/ppc/libextractor-0.5.10-12.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="libextractor" epoch="0" ver="0.5.10" rel="12.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="libextractor"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/libextractor-0.5.10-12.2.ppc.patch.rpm"/>
+          <checksum type="sha">e3b1036c3b3d4622091beb612ccdc180</checksum>
+          <time file="1148265023" build="1148262928"/>
+          <size package="23486" archive="43704"/>
+          <base-version epoch="0" ver="0.5.10" rel="12"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/libextractor-0.5.10-12_12.2.ppc.delta.rpm"/>
+          <checksum type="sha">56677aa78efbecca38aa5384ce900987f8748d1c</checksum>
+          <time file="1148265023" build="1148262928"/>
+          <size package="16745" archive="0"/>
+          <base-version epoch="0" ver="0.5.10" rel="12" md5sum="5369e6a677c196404e213e3b470cd096" buildtime="1146603796" sequence_info="libextractor-0.5.10-12-2ed500d96de814f3d347698ddabc6b344550"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>libextractor</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.5.10" rel="12.2"/>
+      <checksum type="sha" pkgid="YES">39753714ea4afc56bb957742bc74e31126beb474</checksum>
+      <time file="1148260861" build="1148260222"/>
+      <size package="49647" installed="110465" archive="112752"/>
+      <location href="rpm/x86_64/libextractor-0.5.10-12.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="libextractor" epoch="0" ver="0.5.10" rel="12.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="libextractor"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/libextractor-0.5.10-12.2.x86_64.patch.rpm"/>
+          <checksum type="sha">90159e73cc3a553eb108810f231de9db</checksum>
+          <time file="1148265025" build="1148260222"/>
+          <size package="20511" archive="38620"/>
+          <base-version epoch="0" ver="0.5.10" rel="12"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/libextractor-0.5.10-12_12.2.x86_64.delta.rpm"/>
+          <checksum type="sha">17c5fabe28b1efce8da88075d35fd2dab3db2237</checksum>
+          <time file="1148265025" build="1148260222"/>
+          <size package="16619" archive="0"/>
+          <base-version epoch="0" ver="0.5.10" rel="12" md5sum="3b5295d12e3337babf067efedd96fa66" buildtime="1146569041" sequence_info="libextractor-0.5.10-12-4da79b3829b6b12f5c7d5b25010176414550"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-nagios-www-1311.xml b/devel/devel.dmacvicar/repodata/patch-nagios-www-1311.xml
new file mode 100644 (file)
index 0000000..0414003
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="7bc197ba17b801b72ca8ab92ce624963"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="nagios-www-1311"
+    timestamp="1147228703"
+    engine="1.0">
+  <yum:name>nagios-www</yum:name>
+  <summary lang="en">An integer overflow has been fixed.</summary>
+  <summary lang="de">Ein Integer-Überlauf wurde behoben.</summary>
+  <description lang="en">An Integer-Overflow exists within the handling of HTTP 
+headers by CGIs. This could lead to arbitrary code 
+execution by remote attackers on behalf of the Nagios CGI 
+scripts. CVE-2006-2162 has been assigned to this issue.
+</description>
+  <description lang="de">Ein Integer-Überlauf im Handhaben von HTTP-Headern in den 
+Nagios-CGI Skripten kann dazu führen, dass entfernte 
+Angreifer beliebige Befehle ausführen können. CVE-2006-2162 
+wurde diesem Problem zugewiesen. 
+</description>
+  <yum:version ver="1311" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="nagios-www" epoch="0" ver="1.3" rel="14.1" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>nagios-www</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="1.3" rel="14.1"/>
+      <checksum type="sha" pkgid="YES">52f27233cfb8fc172c9660f1c6b5dadebdede30e</checksum>
+      <time file="1147251200" build="1147228703"/>
+      <size package="1664364" installed="4234110" archive="4279600"/>
+      <location href="rpm/i586/nagios-www-1.3-14.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="nagios-www" epoch="0" ver="1.3" rel="14.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="nagios-www"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/nagios-www-1.3-14.1.i586.patch.rpm"/>
+          <checksum type="sha">edb88b55df384a40f210459250de3034</checksum>
+          <time file="1147251689" build="1147228703"/>
+          <size package="552992" archive="2342772"/>
+          <base-version epoch="0" ver="1.3" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/nagios-www-1.3-14_14.1.i586.delta.rpm"/>
+          <checksum type="sha">96e180d6d614be1872aa5c3e04424fee826eaf7c</checksum>
+          <time file="1148032815" build="1147228703"/>
+          <size package="39644" archive="0"/>
+          <base-version epoch="0" ver="1.3" rel="14" md5sum="5d2588d70049b3db6c04fd0b50f3254b" buildtime="1146561118" sequence_info="nagios-www-1.3-14-e4908074e62df2db4b92375cd14cf9aa11ec40"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>nagios-www</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="1.3" rel="14.1"/>
+      <checksum type="sha" pkgid="YES">3bfcce7e5a0d0daf60bc1a89eb2d8e0a4efe6b8a</checksum>
+      <time file="1147251200" build="1147228735"/>
+      <size package="1754238" installed="4398762" archive="4444252"/>
+      <location href="rpm/x86_64/nagios-www-1.3-14.1.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="nagios-www" epoch="0" ver="1.3" rel="14.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="nagios-www"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/nagios-www-1.3-14.1.x86_64.patch.rpm"/>
+          <checksum type="sha">3cba70d8886758efbdbcf1f70c966409</checksum>
+          <time file="1147251693" build="1147228735"/>
+          <size package="649955" archive="2503172"/>
+          <base-version epoch="0" ver="1.3" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/nagios-www-1.3-14_14.1.x86_64.delta.rpm"/>
+          <checksum type="sha">412d4202ec170f48edb77464497a54b2d6e6e6a8</checksum>
+          <time file="1148032819" build="1147228735"/>
+          <size package="42063" archive="0"/>
+          <base-version epoch="0" ver="1.3" rel="14" md5sum="6c2c929f9f739db907847f9b212476d8" buildtime="1146561484" sequence_info="nagios-www-1.3-14-37198a00a2160ea5a679c3092dfed4df11ec40"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-openldap2-1323.xml b/devel/devel.dmacvicar/repodata/patch-openldap2-1323.xml
new file mode 100644 (file)
index 0000000..53a1bc6
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="abf31252ed400309bf499408f0e7251e"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="openldap2-1323"
+    timestamp="1147706746"
+    engine="1.0">
+  <yum:name>openldap2</yum:name>
+  <summary lang="en">Bugfix for the OpenLDAP Server</summary>
+  <summary lang="de">Bugfix für den OpenLDAP Server</summary>
+  <description lang="en">Fixes a problem that could crash the OpenLDAP server daemon 
+while processing Operations with Pre/PostRead LDAP-Controls.
+</description>
+  <description lang="de">Behebt einen Fehler bei der Verarbeitung von Operationen 
+mit Pre/PostRead LDAP-Controls, der zum Absturz des 
+OpenLDAP Servers führt.
+</description>
+  <yum:version ver="1323" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="openldap2" epoch="0" ver="2.3.19" rel="18.3" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openldap2</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="2.3.19" rel="18.3"/>
+      <checksum type="sha" pkgid="YES">a6a40ca62165ceddc09181a9c6d11243b544dbc3</checksum>
+      <time file="1147745477" build="1147706746"/>
+      <size package="1706969" installed="5482100" archive="5509968"/>
+      <location href="rpm/i586/openldap2-2.3.19-18.3.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openldap2" epoch="0" ver="2.3.19" rel="18.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openldap2"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/openldap2-2.3.19-18.3.i586.patch.rpm"/>
+          <checksum type="sha">b79f8ee16069090c7931609004e5e98c</checksum>
+          <time file="1147750816" build="1147706746"/>
+          <size package="1077478" archive="2783996"/>
+          <base-version epoch="0" ver="2.3.19" rel="18"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/openldap2-2.3.19-18_18.3.i586.delta.rpm"/>
+          <checksum type="sha">ee9c7a700a26f73dabc9332d228f7b16c22e8732</checksum>
+          <time file="1148038813" build="1147706746"/>
+          <size package="486980" archive="0"/>
+          <base-version epoch="0" ver="2.3.19" rel="18" md5sum="e88de7da42b581a44ebc35e5081cb48e" buildtime="1146557653" sequence_info="openldap2-2.3.19-18-e92967dcc82cb6d0e483bc4731897ae70292151c161b31c511f522"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openldap2</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="2.3.19" rel="18.3"/>
+      <checksum type="sha" pkgid="YES">2fe6c9ec6a1f7e52edc54154f70421ce9aeb5f96</checksum>
+      <time file="1147744126" build="1147716079"/>
+      <size package="1764143" installed="5750428" archive="5778296"/>
+      <location href="rpm/ppc/openldap2-2.3.19-18.3.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openldap2" epoch="0" ver="2.3.19" rel="18.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openldap2"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/openldap2-2.3.19-18.3.ppc.patch.rpm"/>
+          <checksum type="sha">968049aefe4a70662b290a524f0ed5ef</checksum>
+          <time file="1147750823" build="1147716079"/>
+          <size package="1144636" archive="3052324"/>
+          <base-version epoch="0" ver="2.3.19" rel="18"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/openldap2-2.3.19-18_18.3.ppc.delta.rpm"/>
+          <checksum type="sha">2f903011a428d02e2dd1f9b797f03b8b7be94631</checksum>
+          <time file="1148038820" build="1147716079"/>
+          <size package="500105" archive="0"/>
+          <base-version epoch="0" ver="2.3.19" rel="18" md5sum="d52fa2fff1d7872eb81d653eb656d58f" buildtime="1146556198" sequence_info="openldap2-2.3.19-18-8193c53e7d126ab27d4b28c46c1630040292151c161b31c511f522"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openldap2</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="2.3.19" rel="18.3"/>
+      <checksum type="sha" pkgid="YES">3bb100100080c39f059b055ff2f8e96135f5e721</checksum>
+      <time file="1147744064" build="1147706876"/>
+      <size package="1837678" installed="5571108" archive="5598976"/>
+      <location href="rpm/x86_64/openldap2-2.3.19-18.3.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openldap2" epoch="0" ver="2.3.19" rel="18.3" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openldap2"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/openldap2-2.3.19-18.3.x86_64.patch.rpm"/>
+          <checksum type="sha">1d540f58c683433a62666a930d68bd70</checksum>
+          <time file="1147750828" build="1147706876"/>
+          <size package="1201226" archive="2873004"/>
+          <base-version epoch="0" ver="2.3.19" rel="18"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/openldap2-2.3.19-18_18.3.x86_64.delta.rpm"/>
+          <checksum type="sha">42b738a7ac6bad0e2e7db74c7d45b2f5bc7ed7a2</checksum>
+          <time file="1148038827" build="1147706876"/>
+          <size package="513603" archive="0"/>
+          <base-version epoch="0" ver="2.3.19" rel="18" md5sum="05201a00416962d752711aed6fb9daf8" buildtime="1146558413" sequence_info="openldap2-2.3.19-18-4b66bd4865a20751ab5e1b648e06bb4a0292151c161b31c511f522"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-opera-1313.xml b/devel/devel.dmacvicar/repodata/patch-opera-1313.xml
new file mode 100644 (file)
index 0000000..7647f4b
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="f069efc5b0d465549009fad8fa9a4316"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="opera-1313"
+    timestamp="1147258124"
+    engine="1.0">
+  <yum:name>opera</yum:name>
+  <summary lang="en">Code execution via stylesheet attributes in Opera.</summary>
+  <summary lang="de">Code execution via stylesheet attributes in Opera.</summary>
+  <description lang="en">Integer signedness error in Opera before 8.54 allows remote 
+attackers to execute arbitrary code via long values in a 
+stylesheet attribute, which pass a length check. 
+(CVE-2006-1834)
+</description>
+  <description lang="de">Ein Integer Vorzeichenproblem in Opera vor 8.54 erlaubt 
+entfernten Angreifern Programmcode auszuführen in dem sehr 
+lange Werte in stylesheet Attributen verwendet werden, die 
+einen anderen Längencheck überstehen. (CVE-2006-1834)
+</description>
+  <yum:version ver="1313" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="opera" epoch="0" ver="8.54" rel="0.1" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>opera</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="8.54" rel="0.1"/>
+      <checksum type="sha" pkgid="YES">d401b0253012e812a395ddb043f5b0ab02eff59e</checksum>
+      <time file="1147261618" build="1147258124"/>
+      <size package="4517117" installed="11857070" archive="11875348"/>
+      <location href="rpm/i586/opera-8.54-0.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="opera" epoch="0" ver="8.54" rel="0.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="opera"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/opera-8.54-0.1.i586.patch.rpm"/>
+          <checksum type="sha">46dc8efd53e9e2a93d749a0452ea5aad</checksum>
+          <time file="1147262476" build="1147258124"/>
+          <size package="3349163" archive="7896092"/>
+          <base-version epoch="0" ver="8.52" rel="12"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>opera</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="8.54" rel="0.1"/>
+      <checksum type="sha" pkgid="YES">c67d4bea93ba82482aaff57cae548fb0da1e8f9d</checksum>
+      <time file="1147261403" build="1147258221"/>
+      <size package="4516942" installed="11857070" archive="11875348"/>
+      <location href="rpm/x86_64/opera-8.54-0.1.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="opera" epoch="0" ver="8.54" rel="0.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="opera"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/opera-8.54-0.1.x86_64.patch.rpm"/>
+          <checksum type="sha">8929703aa5b9d2f4562257f8010d6a8f</checksum>
+          <time file="1147262486" build="1147258221"/>
+          <size package="3348789" archive="7896092"/>
+          <base-version epoch="0" ver="8.52" rel="12"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-pdns-1314.xml b/devel/devel.dmacvicar/repodata/patch-pdns-1314.xml
new file mode 100644 (file)
index 0000000..ef7777d
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="bc8a401c7f8de71f0ad128e785961d5e"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="pdns-1314"
+    timestamp="1147258619"
+    engine="1.0">
+  <yum:name>pdns</yum:name>
+  <summary lang="en">pdns security update</summary>
+  <summary lang="de">pdns Sicherheitsupdate</summary>
+  <description lang="en">Remote attackers could crash the pdns server by sending 
+malformed packets (CVE-2006-2069).
+</description>
+  <description lang="de">Durch Senden von Fehlerhaften Paketen konnte der pdns 
+server von entfernten Angreifern zum Absturz gebracht 
+werden (CVE-2006-2069).
+</description>
+  <yum:version ver="1314" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="pdns" epoch="0" ver="2.9.19" rel="13.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>pdns</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="2.9.19" rel="13.2"/>
+      <checksum type="sha" pkgid="YES">47aa34762bfe1b236dc8f97ddbc6c71e1ae1184b</checksum>
+      <time file="1147265884" build="1147258619"/>
+      <size package="1120517" installed="3376502" archive="3395876"/>
+      <location href="rpm/i586/pdns-2.9.19-13.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="pdns" epoch="0" ver="2.9.19" rel="13.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="pdns"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/pdns-2.9.19-13.2.i586.patch.rpm"/>
+          <checksum type="sha">ad3f6271e84697aff4dd7b0207f9d7fc</checksum>
+          <time file="1147266006" build="1147258619"/>
+          <size package="811899" archive="2126308"/>
+          <base-version epoch="0" ver="2.9.19" rel="13"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>pdns</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="2.9.19" rel="13.2"/>
+      <checksum type="sha" pkgid="YES">cc316a1be4793728b58aa0d4f8d74cc334bf1cd9</checksum>
+      <time file="1147265815" build="1147258696"/>
+      <size package="1148560" installed="3406376" archive="3425788"/>
+      <location href="rpm/x86_64/pdns-2.9.19-13.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="pdns" epoch="0" ver="2.9.19" rel="13.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="pdns"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/pdns-2.9.19-13.2.x86_64.patch.rpm"/>
+          <checksum type="sha">bdc2f8d11af876316a0f2e5c0c5add1c</checksum>
+          <time file="1147266009" build="1147258696"/>
+          <size package="827124" archive="2139764"/>
+          <base-version epoch="0" ver="2.9.19" rel="13"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patch-util-linux-crypto-1425.xml b/devel/devel.dmacvicar/repodata/patch-util-linux-crypto-1425.xml
new file mode 100644 (file)
index 0000000..3774019
--- /dev/null
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="4e5938267b8a66f6dce40cbc9b78b444"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="util-linux-crypto-1425"
+    timestamp="1147966888"
+    engine="1.0">
+  <yum:name>util-linux-crypto</yum:name>
+  <summary lang="en">util-linux-crypto bugfi</summary>
+  <summary lang="de">util-linux-crypto bugfix</summary>
+  <description lang="en">Fix cryptsetup to work with no yet existing device maps.
+</description>
+  <description lang="de">Fehlerbehebung für cryptsetup um mit noch nicht 
+exisitierenden device maps umgehen zu können.
+</description>
+  <yum:version ver="1425" rel="0"/>
+  <rpm:requires>
+  <rpm:entry kind="atom" name="util-linux-crypto" epoch="0" ver="2.12a" rel="14.2" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>util-linux-crypto</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="2.12a" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">5f64cb2a850f614871e7ce39e5927f2f16b138c7</checksum>
+      <time file="1148255911" build="1147966888"/>
+      <size package="207839" installed="402027" archive="403292"/>
+      <location href="rpm/i586/util-linux-crypto-2.12a-14.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="util-linux-crypto" epoch="0" ver="2.12a" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="util-linux-crypto"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/util-linux-crypto-2.12a-14.2.i586.patch.rpm"/>
+          <checksum type="sha">30b8043a845059807fa9143ff8b4772c</checksum>
+          <time file="1148258418" build="1147966888"/>
+          <size package="192407" archive="368308"/>
+          <base-version epoch="0" ver="2.12a" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/i586/util-linux-crypto-2.12a-14_14.2.i586.delta.rpm"/>
+          <checksum type="sha">720aaaeac0a84ca19e752b654539a3ba12b67f7e</checksum>
+          <time file="1148258420" build="1147966888"/>
+          <size package="10777" archive="0"/>
+          <base-version epoch="0" ver="2.12a" rel="14" md5sum="83a7f533b8a6d5970e80715df24fb509" buildtime="1145762882" sequence_info="util-linux-crypto-2.12a-14-4c611a445229c2148fda85d26debd66e81"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>util-linux-crypto</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="2.12a" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">e63397586ea3e175876cc4dd476e847eea0e0f2e</checksum>
+      <time file="1148256126" build="1147966585"/>
+      <size package="217758" installed="423235" archive="424500"/>
+      <location href="rpm/ppc/util-linux-crypto-2.12a-14.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="util-linux-crypto" epoch="0" ver="2.12a" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="util-linux-crypto"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/util-linux-crypto-2.12a-14.2.ppc.patch.rpm"/>
+          <checksum type="sha">8c81cd1ebc9aeca0f695010a760122bb</checksum>
+          <time file="1148258422" build="1147966585"/>
+          <size package="191351" archive="358724"/>
+          <base-version epoch="0" ver="2.12a" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/ppc/util-linux-crypto-2.12a-14_14.2.ppc.delta.rpm"/>
+          <checksum type="sha">2a806b25b94eaa599c8f602e6fe03942748c23fb</checksum>
+          <time file="1148258423" build="1147966585"/>
+          <size package="9851" archive="0"/>
+          <base-version epoch="0" ver="2.12a" rel="14" md5sum="ae93291a537f6f0598ce5bc52e14b932" buildtime="1145892265" sequence_info="util-linux-crypto-2.12a-14-770561225a552b2c7734f7bff7688cf781"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>util-linux-crypto</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="2.12a" rel="14.2"/>
+      <checksum type="sha" pkgid="YES">8b428d265f0998310d65412f56babd53d4bced53</checksum>
+      <time file="1148255470" build="1147966522"/>
+      <size package="204809" installed="390923" archive="392188"/>
+      <location href="rpm/x86_64/util-linux-crypto-2.12a-14.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="util-linux-crypto" epoch="0" ver="2.12a" rel="14.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="util-linux-crypto"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/util-linux-crypto-2.12a-14.2.x86_64.patch.rpm"/>
+          <checksum type="sha">5f80ab1d3761dcaac049e6331b9bd832</checksum>
+          <time file="1148258424" build="1147966522"/>
+          <size package="183162" archive="332732"/>
+          <base-version epoch="0" ver="2.12a" rel="14"/>
+        </patchrpm>
+        <deltarpm>
+          <location href="rpm/x86_64/util-linux-crypto-2.12a-14_14.2.x86_64.delta.rpm"/>
+          <checksum type="sha">b3319ea2c1b0c42fcd68e718fd5d9438e8b4f6ae</checksum>
+          <time file="1148258425" build="1147966522"/>
+          <size package="12235" archive="0"/>
+          <base-version epoch="0" ver="2.12a" rel="14" md5sum="e33288f2fad8f1febf12058965814e60" buildtime="1145764685" sequence_info="util-linux-crypto-2.12a-14-9d6c221d486cd9c74bb6790eab5445af81"/>
+        </deltarpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/devel/devel.dmacvicar/repodata/patches.xml b/devel/devel.dmacvicar/repodata/patches.xml
new file mode 100644 (file)
index 0000000..ed324f2
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<patches xmlns="http://novell.com/package/metadata/suse/patches">
+  <patch id="avahi-1399">
+    <checksum type="sha">393b281520cdbf9a5d0d5c8895914fb50d6fa0d1</checksum>
+    <location href="repodata/patch-avahi-1399.xml"/>
+  </patch>
+  <patch id="nagios-www-1311">
+    <checksum type="sha">63d7c7b0cffdf4260b4f89871efb7ce453bc7013</checksum>
+    <location href="repodata/patch-nagios-www-1311.xml"/>
+  </patch>
+  <patch id="dhcdbd-1315">
+    <checksum type="sha">9918bf4f5570934b1f719e3ac317f220356ca383</checksum>
+    <location href="repodata/patch-dhcdbd-1315.xml"/>
+  </patch>
+  <patch id="dhcp-1316">
+    <checksum type="sha">8861b210749d672304aadae7b0c41621794d4f3b</checksum>
+    <location href="repodata/patch-dhcp-1316.xml"/>
+  </patch>
+  <patch id="dovecot-1398">
+    <checksum type="sha">2f69d65a2e22aa9ed236e5ead1db7f3bab4090f2</checksum>
+    <location href="repodata/patch-dovecot-1398.xml"/>
+  </patch>
+  <patch id="dhcp-1424">
+    <checksum type="sha">d873de1db8c957c39cca93e828b69f3a1e0f3b70</checksum>
+    <location href="repodata/patch-dhcp-1424.xml"/>
+  </patch>
+  <patch id="openldap2-1323">
+    <checksum type="sha">3d9171d6a9e6352ba77f735250757dca0b5c236a</checksum>
+    <location href="repodata/patch-openldap2-1323.xml"/>
+  </patch>
+  <patch id="opera-1313">
+    <checksum type="sha">e2664d7837b76981a86cb55ba242d163967e2ed2</checksum>
+    <location href="repodata/patch-opera-1313.xml"/>
+  </patch>
+  <patch id="pdns-1314">
+    <checksum type="sha">c3664684e735e36c928a4433aad7754181a018af</checksum>
+    <location href="repodata/patch-pdns-1314.xml"/>
+  </patch>
+  <patch id="libextractor-1426">
+    <checksum type="sha">d38edb46ea3f67e32942f3d9d6aae54e30aab210</checksum>
+    <location href="repodata/patch-libextractor-1426.xml"/>
+  </patch>
+  <patch id="ivman-1423">
+    <checksum type="sha">e0fbe295ccb485009a7a67d3cfadca7bb3c7f269</checksum>
+    <location href="repodata/patch-ivman-1423.xml"/>
+  </patch>
+  <patch id="util-linux-crypto-1425">
+    <checksum type="sha">574d75c7a19043fad974a9834bba46f74f542294</checksum>
+    <location href="repodata/patch-util-linux-crypto-1425.xml"/>
+  </patch>
+</patches>
diff --git a/devel/devel.dmacvicar/repodata/primary.xml.gz b/devel/devel.dmacvicar/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..22832ef
Binary files /dev/null and b/devel/devel.dmacvicar/repodata/primary.xml.gz differ
diff --git a/devel/devel.dmacvicar/repodata/repomd.xml b/devel/devel.dmacvicar/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..62881ae
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">7ae86874a55ad530365bc628d70895aa3326bfb2</checksum>
+    <timestamp>1148377817</timestamp>
+    <open-checksum type="sha">d94fea237ec937d9e0a7173192655f9c3a8feff7</open-checksum>
+  </data>
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">b217f997b92c1864f9fd0182f7b1add1c86bcf2d</checksum>
+    <timestamp>1148377817</timestamp>
+    <open-checksum type="sha">9796a2d1ca2bd7bbe9393d74c1a2dcde2aa89807</open-checksum>
+  </data>
+  <data type="patches">
+    <location href="repodata/patches.xml"/>
+    <checksum type="sha">a8257f48edbc6ec56aa9490acacaeecaafae1f2d</checksum>
+    <timestamp>1148377817</timestamp>
+    <open-checksum type="sha">a8257f48edbc6ec56aa9490acacaeecaafae1f2d</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">88eb9c75f5e7d9ee2db9faee5ec4e83026f3bd24</checksum>
+    <timestamp>1148377817</timestamp>
+    <open-checksum type="sha">76c223d9c262a46054b550246d70fc5cb1519aa8</open-checksum>
+  </data>
+</repomd>
diff --git a/devel/devel.dmacvicar/repodata/repomd.xml.asc b/devel/devel.dmacvicar/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..2dd0b25
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBEctraqE7a6JyACsoRAnwVAJ99UlSJksx6iYWr/GmkFMlvd1pidQCg
+gR4qP2Y3BBL6YYF7lQY4C9swhSs=
+=Tnl5
+-----END PGP SIGNATURE-----
diff --git a/devel/devel.dmacvicar/repodata/repomd.xml.key b/devel/devel.dmacvicar/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..91c316f
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/devel/devel.dmacvicar/rpmbuilder.cc b/devel/devel.dmacvicar/rpmbuilder.cc
new file mode 100644 (file)
index 0000000..c9b4edf
--- /dev/null
@@ -0,0 +1,160 @@
+#include <sys/time.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <zypp/base/Logger.h>
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+
+#include "zypp/Product.h"
+#include "zypp/Package.h"
+
+#include "zypp/TmpPath.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/ProgressData.h"
+#include "zypp/repo/yum/Downloader.h"
+
+#include "zypp/sat/Pool.h"
+
+#include "zypp/PoolQuery.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+using namespace zypp::filesystem;
+
+class RpmBuilder
+{
+public:
+    RpmBuilder()
+    {
+        Pathname top = _tmptop.path();
+        Pathname rcpath = _tmprc.path();
+        
+        assert_dir(top / "build" );
+        assert_dir(top / "rpms" );
+        
+        /* create a rpm configuration file and
+           setup the macros file */
+        std::ofstream rcfile(rcpath.c_str());
+        if (!rcfile)
+            ZYPP_THROW (Exception( "Can't open " + rcpath.asString() ) );
+        
+        rcfile << "macrofiles: " << _tmpmacros.path() << endl;
+        rcfile.close();
+    }
+    
+    Pathname rpmsDir() const
+    {
+        return _tmptop.path() / "rpms";
+    }
+    
+    void createRpmMetadata() const
+    {
+        const char* argv[] =
+        {
+            "createrepo",
+            rpmsDir().c_str(),
+            NULL
+        };
+        ExternalProgram prog(argv,ExternalProgram::Normal_Stderr, false, -1, true);
+        string line;
+        int count;
+        for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
+        {
+            cout << line;
+        }
+        prog.close();
+
+    }
+    
+    void buildSpec( const Pathname &spec )
+    {
+        Pathname basedir = spec.dirname();
+        Pathname rcpath = _tmprc.path();
+        Pathname macrospath = _tmpmacros.path();
+        
+        std::ofstream macrosfile(macrospath.c_str());
+        if (!macrosfile)
+            ZYPP_THROW (Exception( "Can't open " + macrospath.asString() ) );
+        
+        macrosfile << "%topdir " << _tmptop.path() << endl;
+        macrosfile << "%_builddir %{topdir}/build" << endl;
+        macrosfile << "%_rpmdir %{topdir}/rpms" << endl;
+        macrosfile << "%_srcrpmdir %{topdir}/rpms" << endl;
+        macrosfile << "%_sourcedir " << basedir << endl;
+        macrosfile << "%_specdir " << basedir << endl;
+        macrosfile.close();
+        
+        const char* argv[] =
+        {
+            "rpmbuild",
+            "--rcfile",
+            rcpath.c_str(),
+            "-bb",
+            //"--clean",
+            "--buildroot",
+            _tmpbuildroot.path().c_str(),
+            spec.c_str(),
+            NULL
+        };
+        ExternalProgram prog(argv,ExternalProgram::Normal_Stderr, false, -1, true);
+        string line;
+        int count;
+        for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
+        {
+            cout << line;
+        }
+        prog.close();
+
+    }
+    
+private:
+    TmpDir _tmptop;
+    TmpFile _tmprc;
+    TmpFile _tmpmacros;
+    TmpDir _tmpbuildroot;
+};
+
+int main(int argc, char **argv)
+{
+    try
+    {
+      ZYpp::Ptr z = getZYpp();
+    
+      //z->initializeTarget("/");
+      //z->target()->load();
+
+      //sat::Pool::instance().addRepoSolv("./foo.solv");
+
+//       for ( ResPool::const_iterator it = z->pool().begin(); it != z->pool().end(); ++it )
+//       {
+//         ResObject::constPtr res = it->resolvable();
+//         if ( res->name() == "kde4-kcolorchooser")
+//         {
+//           cout << res << endl;
+//           cout << res->summary() << " | " << res->size() << endl;
+//         }
+//       }
+
+      //query.execute("kde", &result_cb);
+      
+      RpmBuilder builder;
+      builder.buildSpec("/space/git/hwenable/spec/testdriver.spec");
+      builder.createRpmMetadata();
+      
+      
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      cout << e.msg() << endl;
+    }
+    
+    return 0;
+}
+
+
+
diff --git a/devel/devel.dmacvicar/testbed.cc b/devel/devel.dmacvicar/testbed.cc
new file mode 100644 (file)
index 0000000..f0ebf04
--- /dev/null
@@ -0,0 +1,81 @@
+#include <sys/time.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <zypp/base/Logger.h>
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+
+#include "zypp/Product.h"
+#include "zypp/Package.h"
+#include "zypp/Fetcher.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ProgressData.h"
+
+#include "zypp/sat/Pool.h"
+
+#include "zypp/ZYppCallbacks.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+using zypp::media::MediaChangeReport;
+
+
+bool result_cb( const ResObject::Ptr &r )
+{
+  cout << r << endl;
+}
+
+struct MediaChangeReportReceiver : public zypp::callback::ReceiveReport<MediaChangeReport>
+  {
+    virtual MediaChangeReport::Action
+    requestMedia(zypp::Url & url,
+                 unsigned                         mediumNr,
+                 const std::string &              label,
+                 MediaChangeReport::Error         error,
+                 const std::string &              description,
+                 const std::vector<std::string> & devices,
+                 unsigned int &                   index)
+    {
+      cout << std::endl;
+      MIL << "media problem, url: " << url.asString() << std::endl;
+      return MediaChangeReport::IGNORE;
+    }
+  };
+
+
+int main(int argc, char **argv)
+{
+    try
+    {
+      ZYpp::Ptr z = getZYpp();
+    
+      MediaChangeReportReceiver report;
+      report.connect();
+      
+
+      Fetcher fetcher;
+      MediaSetAccess access(Url("http://ftp.kernel.org/pub"));
+      filesystem::TmpDir tmp;
+      
+      OnMediaLocation loc;
+      loc.setLocation("/README2");
+      loc.setOptional(true);
+      
+      fetcher.enqueue(loc);
+      fetcher.start(tmp.path(), access);
+      
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      cout << e.msg() << endl;
+    }
+    
+    return 0;
+}
+
+
+
diff --git a/devel/devel.dmacvicar/zsync.cc b/devel/devel.dmacvicar/zsync.cc
new file mode 100644 (file)
index 0000000..cd151d1
--- /dev/null
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" {
+#include <zsync.h>
+}
+#include "zypp/base/Exception.h"
+#include "zypp/base/Logger.h"
+#include "zypp/Pathname.h"
+#include "zypp/ExternalProgram.cc"
+//#include 
+
+using namespace zypp;
+using namespace std;
+
+void read_seed_file(struct zsync_state* z, const Pathname &path )
+{
+  if (zsync_hint_decompress(z) && path.basename().size() > 3 && path.extension() == ".gz" )
+  {
+    FILE* f;
+    {
+      // ugh
+      char* cmd = (char *) malloc(6 + strlen(path.c_str())*2);
+
+      if (!cmd) return;
+
+      const char *fname = path.c_str();
+      strcpy(cmd,"zcat ");
+      {
+        int i,j;
+        for (i=0,j=5; fname[i]; i++)
+        {
+          if (!isalnum(fname[i])) cmd[j++] = '\\';
+            cmd[j++] = fname[i];
+        }
+        cmd[j] = 0;
+      }
+
+      //if (!no_progress) fprintf(stderr,"reading seed %s: ",cmd);
+      MIL << "Reading seed " << cmd << endl;
+      f = popen(cmd,"r");
+      free(cmd);
+    }
+
+    if (!f)
+    {
+      //perror("popen"); fprintf(stderr,"not using seed file %s\n",fname);
+      ZYPP_THROW(Exception("not using seed file"));
+    }
+    else
+    {
+      // 0 no progress
+      zsync_submit_source_file(z, f, 0);
+      if (pclose(f) != 0)
+      {
+        ZYPP_THROW(Exception("pclose"));
+        perror("close");
+      }
+    }
+  }
+  else
+  {
+    FILE* f = fopen(path.c_str(),"r");
+    MIL << "Reading seed " << path << endl;
+    if (!f) {
+      //perror("open"); fprintf(stderr,"not using seed file %s\n",fname);
+      ZYPP_THROW(Exception("open: " + path.asString()));
+    }
+    else
+    {
+      // 0 no progress
+      zsync_submit_source_file(z, f, 0);
+      if (fclose(f) != 0)
+      {
+        perror("close");
+      }
+    }
+  }
+  {
+    long long done,total;
+    zsync_progress(z, &done, &total);
+    MIL << "Read " << path << ". Target " << (100.0f * done)/total << " complete" << endl;
+  }
+}
+
+void figure_ranges(struct zsync_state* zs)
+{
+  //struct zsync_receiver* zr;
+  int num_ranges;
+  // it seems type is 1 for gz, 0 normal
+  off_t *ranges = zsync_needed_byte_ranges(zs, &num_ranges, 0);
+  int i=0;
+
+  MIL << "Need to get " << num_ranges << " ranges" << endl;
+
+  while ( i < 2*num_ranges )
+  {
+    int from = ranges[i];
+    MIL << "From: " << ranges[i] << " To: " << ranges[i+1] << endl;
+    i += 2;
+  }
+
+  free(ranges);
+}
+
+int main()
+{
+  Pathname root("/home/duncan/suse/metadata-diff");
+  struct zsync_state* zs;
+
+  FILE *f = fopen( (root+"/3/packages.zsync").c_str(), "r" );
+
+  if ((zs = zsync_begin(f)) == NULL)
+  {
+    exit(1);
+  }
+  
+  if (fclose(f) != 0)
+  {
+    perror("fclose"); exit(2);
+  }
+
+  read_seed_file( zs, root + "1/packages" );
+  figure_ranges(zs);
+
+  zsync_end(zs);
+  return 0;
+}
\ No newline at end of file
diff --git a/devel/devel.dmacvicar/zypp-lock.cc b/devel/devel.dmacvicar/zypp-lock.cc
new file mode 100644 (file)
index 0000000..8a8d6d5
--- /dev/null
@@ -0,0 +1,54 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <streambuf>
+
+#include "boost/filesystem/operations.hpp" // includes boost/filesystem/path.hpp
+#include "boost/filesystem/fstream.hpp"    // ditto
+
+#include <boost/iostreams/device/file_descriptor.hpp>
+
+#include <zypp/base/Logger.h>
+#include <zypp/Locale.h>
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+#include <zypp/TranslatedText.h>
+///////////////////////////////////////////////////////////////////
+
+#include <zypp/parser/yum/YUMParser.h>
+#include <zypp/base/Logger.h>
+#include <zypp/source/yum/YUMScriptImpl.h>
+#include <zypp/source/yum/YUMMessageImpl.h>
+#include <zypp/source/yum/YUMPackageImpl.h>
+#include <zypp/source/yum/YUMSourceImpl.h>
+
+#include <map>
+#include <set>
+
+#include <zypp/CapFactory.h>
+
+using namespace zypp::detail;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::parser::yum;
+using namespace zypp::source::yum;
+
+
+
+//using namespace DbXml;
+
+int main()
+{
+  try
+  { 
+    ZYpp::Ptr z = getZYpp();
+    //while(1);
+  }
+  catch (...)
+  {
+    MIL << "sorry" << std::endl;
+  } 
+}
+
+
diff --git a/devel/devel.jkupec/CMakeLists.txt b/devel/devel.jkupec/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b326079
--- /dev/null
@@ -0,0 +1,37 @@
+ADD_DEFINITIONS(
+  -DSRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
+  -DTESTS_SRC_DIR="${LIBZYPP_SOURCE_DIR}/tests"
+)
+
+SET( bridge_SRCS
+  bridge/bridge.cc
+  bridge/BaseImpl.cc
+  bridge/Derived.cc
+)
+
+SET( bridge_HEADERS
+  bridge/Base.h
+  bridge/BaseImpl.h
+  bridge/Derived.h
+)
+
+#ADD_EXECUTABLE( bridge ${bridge_SRCS} )
+#TARGET_LINK_LIBRARIES( bridge zypp )
+
+#ADD_EXECUTABLE( deltarpm deltarpm.cc )
+#TARGET_LINK_LIBRARIES( deltarpm zypp )
+
+#ADD_EXECUTABLE( pathinfo pathinfo.cc )
+#TARGET_LINK_LIBRARIES( pathinfo zypp )
+
+ADD_EXECUTABLE( poolquery poolquery.cc )
+TARGET_LINK_LIBRARIES( poolquery zypp )
+
+ADD_EXECUTABLE( repos repos.cc )
+TARGET_LINK_LIBRARIES( repos zypp )
+
+#ADD_EXECUTABLE( yumparsertest YUMParser_test.cc )
+#TARGET_LINK_LIBRARIES( yumparsertest zypp )
+
+ADD_EXECUTABLE( play play.cc )
+TARGET_LINK_LIBRARIES( play zypp boost_signals )
diff --git a/devel/devel.jkupec/YUMParser_test.cc b/devel/devel.jkupec/YUMParser_test.cc
new file mode 100644 (file)
index 0000000..73df2dc
--- /dev/null
@@ -0,0 +1,64 @@
+#include "zypp/ZYpp.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/Measure.h"
+#include "zypp/cache/CacheStore.h"
+#include "zypp/parser/yum/RepoParser.h"
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "yumparsertest"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::parser::yum;
+using zypp::debug::Measure;
+
+bool progress_function(ProgressData::value_type p)
+{
+  cout << "Parsing YUM source [" << p << "%]" << endl;
+//  cout << "\rParsing YUM source [" << p << "%]" << flush;
+  return true;
+}
+
+int main(int argc, char **argv)
+{
+  base::LogControl::instance().logfile("yumparsertest.log");
+  
+  if (argc < 2)
+  {
+    cout << "usage: yumparsertest path/to/yumsourcedir" << endl << endl;
+    return 1;
+  }
+
+  try
+  {
+    ZYpp::Ptr z = getZYpp();
+
+    Measure open_repository_timer("CacheStore: lookupOrAppendRepository");
+
+    cache::CacheStore store(getenv("PWD"));
+    data::RecordId repository_id = store.lookupOrAppendRepository("somealias");
+
+    open_repository_timer.stop();
+
+    MIL << "creating PrimaryFileParser" << endl;
+    Measure parse_primary_timer("primary.xml.gz parsing");
+
+    parser::yum::RepoParser parser( repository_id, store, &progress_function);
+    parser.parse(argv[1]);
+
+    store.commit();
+    parse_primary_timer.stop();
+
+    cout << endl;
+  }
+  catch ( const Exception &e )
+  {
+    cout << "Oops! " << e.msg() << std::endl;
+  }
+
+  return 0;
+}
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/devel/devel.jkupec/bridge/Base.h b/devel/devel.jkupec/bridge/Base.h
new file mode 100644 (file)
index 0000000..8f04e5f
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef JK_BASE_H_
+#define JK_BASE_H_
+
+#include "zypp/base/NonCopyable.h"
+
+namespace jk
+{
+
+
+  class Base : private zypp::base::NonCopyable
+  {
+  protected:
+    class BaseImpl;
+  };
+
+
+} // ns jk
+
+
+#endif /*JK_BASE_H_*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
+
diff --git a/devel/devel.jkupec/bridge/BaseImpl.cc b/devel/devel.jkupec/bridge/BaseImpl.cc
new file mode 100644 (file)
index 0000000..2bbf7a9
--- /dev/null
@@ -0,0 +1,16 @@
+#include "BaseImpl.h"
+
+using namespace std;
+
+namespace jk
+{
+
+
+  Base::BaseImpl::BaseImpl()
+  {}
+
+
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
+
diff --git a/devel/devel.jkupec/bridge/BaseImpl.h b/devel/devel.jkupec/bridge/BaseImpl.h
new file mode 100644 (file)
index 0000000..4f6252b
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef JK_BASEIMPL_H_
+#define JK_BASEIMPL_H_
+
+#include "zypp/base/NonCopyable.h"
+#include "Base.h"
+
+namespace jk
+{
+
+
+  class Base::BaseImpl : private zypp::base::NonCopyable
+  {
+  public:
+    BaseImpl();
+  };
+
+
+} // ns jk
+
+#endif /*JK_BASEIMPL_H_*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
+
diff --git a/devel/devel.jkupec/bridge/Derived.cc b/devel/devel.jkupec/bridge/Derived.cc
new file mode 100644 (file)
index 0000000..cc35d30
--- /dev/null
@@ -0,0 +1,28 @@
+#include "BaseImpl.h"
+#include "Derived.h"
+
+namespace jk
+{
+
+
+  class Derived::Impl : public BaseImpl
+  {
+  public:
+    Impl();
+  };
+
+  Derived::Impl::Impl()
+  {}
+
+
+  Derived::Derived() : _pimpl(new Impl())
+  {}
+
+  Derived::~Derived()
+  {}
+
+
+} // ns jk
+
+// vim: set ts=2 sts=2 sw=2 et ai:
+
diff --git a/devel/devel.jkupec/bridge/Derived.h b/devel/devel.jkupec/bridge/Derived.h
new file mode 100644 (file)
index 0000000..5efd9a4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef JK_DERIVED_H_
+#define JK_DERIVED_H_
+
+#include "zypp/base/PtrTypes.h"
+#include "Base.h"
+
+namespace jk
+{
+
+
+  class Derived : public Base
+  {
+  public:
+    Derived();
+    ~Derived();
+
+  private:
+    class Impl;
+    zypp::RW_pointer<Impl,zypp::rw_pointer::Scoped<Impl> > _pimpl;
+  };
+
+
+
+} // ns jk
+
+#endif /*JK_DERIVED_H_*/
diff --git a/devel/devel.jkupec/bridge/bridge.cc b/devel/devel.jkupec/bridge/bridge.cc
new file mode 100644 (file)
index 0000000..6ed712e
--- /dev/null
@@ -0,0 +1,26 @@
+#include<iostream>
+
+#include "zypp/base/Exception.h"
+
+#include "Derived.h"
+
+
+using namespace std;
+using namespace jk;
+using namespace zypp;
+
+int main(int argc, char **argv)
+{
+  try
+  {
+    Derived d;
+  }
+  catch ( const Exception &e )
+  {
+    cout << "Oops! " << e.msg() << std::endl;
+  }
+
+  return 0;
+}
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/deltainfo.xml b/devel/devel.jkupec/data/deltarpm/updates/repodata/deltainfo.xml
new file mode 100644 (file)
index 0000000..2909d1b
--- /dev/null
@@ -0,0 +1,17 @@
+ <deltainfo>
+  <newpackage name="libzypp" epoch="0" version="4.21.3" release="2" arch="i386">
+    <delta oldepoch="0" oldversion="4.21.3" oldrelease="1">
+      <filename>DRPMS/libzypp-4.21.3-1_4.21.3-2.i386.drpm</filename>
+      <sequence>libzypp-4.21.3-1-d3571f98b048b1a870e40241bb46c67ab4</sequence>
+      <size>22452</size>
+      <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
+    </delta>
+    <delta oldepoch="0" oldversion="4.21.2" oldrelease="3">
+      <filename>DRPMS/libzypp-4.21.2-3_4.21.3-2.i386.drpm</filename>
+       <sequence>libzypp-4.21.2-3-e82691677eee1e83b4812572c5c9ce8eb</sequence>
+       <size>110362</size>
+       <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
+     </delta>
+   </newpackage>
+ </deltainfo>
+
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/primary.xml.gz b/devel/devel.jkupec/data/deltarpm/updates/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..b41606a
Binary files /dev/null and b/devel/devel.jkupec/data/deltarpm/updates/repodata/primary.xml.gz differ
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml b/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..48a0a5c
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">d41033826a12ce44a1b33eff2e7905785e0999da</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">319b2951aad2417c8961442ba692d4700962043b</open-checksum>
+  </data>
+  <data type="updateinfo">
+    <location href="repodata/updateinfo.xml.gz"/>
+    <checksum type="sha">4f8f83525b232db93a761ed1be79515956b574cf</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">70eb95f379e0db1c9815f0a1cb2269d93e408015</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">28a6aae0cd873e1df286d4a07fc7e54263fec79d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">5ad445e403218ef4a6585dbfc37ccf31d5a10096</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">553f609c610b0cf51b54efc4c5c618537707ac8d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">8c840e0b03ad8c2ed0d4ddf57f9a6b5cea3ac412</open-checksum>
+  </data>
+  <data type="deltainfo">
+    <location href="repodata/deltainfo.xml"/>
+    <checksum type="sha">cb4c1c8b35d3c486dde918de72a4e630434e5b40</checksum>
+    <timestamp>1210522397</timestamp>
+    <open-checksum type="sha">cb4c1c8b35d3c486dde918de72a4e630434e5b40</open-checksum>
+  </data>
+</repomd>
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.asc b/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..209ad5c
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBILp6nqE7a6JyACsoRApMoAKCSmiVFYUOGXhWikMAKRrh0Lp0jaQCf
+YN82OkmNZJRvtpBFUs/R5iCx7CU=
+=BuV4
+-----END PGP SIGNATURE-----
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.key b/devel/devel.jkupec/data/deltarpm/updates/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..91c316f
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/devel/devel.jkupec/data/deltarpm/updates/repodata/updateinfo.xml.gz b/devel/devel.jkupec/data/deltarpm/updates/repodata/updateinfo.xml.gz
new file mode 100644 (file)
index 0000000..28061e0
Binary files /dev/null and b/devel/devel.jkupec/data/deltarpm/updates/repodata/updateinfo.xml.gz differ
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/deltainfo.xml b/devel/devel.jkupec/data/deltarpm/updates2/repodata/deltainfo.xml
new file mode 100644 (file)
index 0000000..2909d1b
--- /dev/null
@@ -0,0 +1,17 @@
+ <deltainfo>
+  <newpackage name="libzypp" epoch="0" version="4.21.3" release="2" arch="i386">
+    <delta oldepoch="0" oldversion="4.21.3" oldrelease="1">
+      <filename>DRPMS/libzypp-4.21.3-1_4.21.3-2.i386.drpm</filename>
+      <sequence>libzypp-4.21.3-1-d3571f98b048b1a870e40241bb46c67ab4</sequence>
+      <size>22452</size>
+      <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
+    </delta>
+    <delta oldepoch="0" oldversion="4.21.2" oldrelease="3">
+      <filename>DRPMS/libzypp-4.21.2-3_4.21.3-2.i386.drpm</filename>
+       <sequence>libzypp-4.21.2-3-e82691677eee1e83b4812572c5c9ce8eb</sequence>
+       <size>110362</size>
+       <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
+     </delta>
+   </newpackage>
+ </deltainfo>
+
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/primary.xml.gz b/devel/devel.jkupec/data/deltarpm/updates2/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..b41606a
Binary files /dev/null and b/devel/devel.jkupec/data/deltarpm/updates2/repodata/primary.xml.gz differ
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml b/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..48a0a5c
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">d41033826a12ce44a1b33eff2e7905785e0999da</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">319b2951aad2417c8961442ba692d4700962043b</open-checksum>
+  </data>
+  <data type="updateinfo">
+    <location href="repodata/updateinfo.xml.gz"/>
+    <checksum type="sha">4f8f83525b232db93a761ed1be79515956b574cf</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">70eb95f379e0db1c9815f0a1cb2269d93e408015</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">28a6aae0cd873e1df286d4a07fc7e54263fec79d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">5ad445e403218ef4a6585dbfc37ccf31d5a10096</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">553f609c610b0cf51b54efc4c5c618537707ac8d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">8c840e0b03ad8c2ed0d4ddf57f9a6b5cea3ac412</open-checksum>
+  </data>
+  <data type="deltainfo">
+    <location href="repodata/deltainfo.xml"/>
+    <checksum type="sha">cb4c1c8b35d3c486dde918de72a4e630434e5b40</checksum>
+    <timestamp>1210522397</timestamp>
+    <open-checksum type="sha">cb4c1c8b35d3c486dde918de72a4e630434e5b40</open-checksum>
+  </data>
+</repomd>
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.asc b/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..209ad5c
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBILp6nqE7a6JyACsoRApMoAKCSmiVFYUOGXhWikMAKRrh0Lp0jaQCf
+YN82OkmNZJRvtpBFUs/R5iCx7CU=
+=BuV4
+-----END PGP SIGNATURE-----
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.key b/devel/devel.jkupec/data/deltarpm/updates2/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..91c316f
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/devel/devel.jkupec/data/deltarpm/updates2/repodata/updateinfo.xml.gz b/devel/devel.jkupec/data/deltarpm/updates2/repodata/updateinfo.xml.gz
new file mode 100644 (file)
index 0000000..28061e0
Binary files /dev/null and b/devel/devel.jkupec/data/deltarpm/updates2/repodata/updateinfo.xml.gz differ
diff --git a/devel/devel.jkupec/data/pathinfo/afile b/devel/devel.jkupec/data/pathinfo/afile
new file mode 100644 (file)
index 0000000..aeef426
--- /dev/null
@@ -0,0 +1 @@
+a file
\ No newline at end of file
diff --git a/devel/devel.jkupec/data/pathinfo/alink b/devel/devel.jkupec/data/pathinfo/alink
new file mode 120000 (symlink)
index 0000000..6a93f4f
--- /dev/null
@@ -0,0 +1 @@
+afile
\ No newline at end of file
diff --git a/devel/devel.jkupec/data/pathinfo/subdir/anotherfile b/devel/devel.jkupec/data/pathinfo/subdir/anotherfile
new file mode 100644 (file)
index 0000000..cdcb284
--- /dev/null
@@ -0,0 +1 @@
+another file
\ No newline at end of file
diff --git a/devel/devel.jkupec/data/pathinfo/subdirlink b/devel/devel.jkupec/data/pathinfo/subdirlink
new file mode 120000 (symlink)
index 0000000..f57a792
--- /dev/null
@@ -0,0 +1 @@
+subdir/anotherfile
\ No newline at end of file
diff --git a/devel/devel.jkupec/dbqueries.sql b/devel/devel.jkupec/dbqueries.sql
new file mode 100644 (file)
index 0000000..9c84132
--- /dev/null
@@ -0,0 +1,54 @@
+-- what kinds of resolvables have been read?
+select * from types where class = 'kind';
+
+-- what languages have been encountered?
+select * from types where class = 'lang';
+
+-- what architectures have been read?
+select * from types where class = 'arch';
+
+-- what types of dependencies have been encountered?
+select * from types where class = 'deptype';
+
+-- ----------------------------------------------------------------------------
+
+-- how many packages have been read?
+select count(*) from resolvables r, types t where t.class = 'kind' and t.name = 'package' and t.id = r.kind;
+
+-- print resolvable kind -> count table
+select t.name, count(*) from resolvables r, types t where t.class = 'kind' and t.id = r.kind group by t.name;
+
+-- what patches have been read? print id, name, and version
+select r.id, r.name, r.version from resolvables r, types t where t.class = 'kind' and t.name = 'patch' and t.id = r.kind;
+
+-- ----------------------------------------------------------------------------
+
+-- print all text and numeric attributes of resolvable with id = 2
+select a.weak_resolvable_id "res-id", t.class "attr-class", t.name "attr-name", a.text "value"
+  from text_attributes a, types t
+  where t.id = a.attr_id and a.weak_resolvable_id = 2
+union
+select a.weak_resolvable_id "res-id", t.class "attr-class", t.name "attr-name", a.value "value"
+  from numeric_attributes a, types t
+  where t.id = a.attr_id and a.weak_resolvable_id = 2
+order by t.class
+
+-- print all named dependencies of resolvable with id = 2
+select dt.name "dtype", kt.name "kind", n.name "name", rt.name "rel", c.version "version"
+  from named_capabilities c, types dt, types kt, types rt, names n
+  where c.dependency_type = dt.id
+    and c.refers_kind= kt.id
+    and c.relation = rt.id
+    and c.name_id = n.id
+    and c.resolvable_id = 2;
+
+-- print all file dependencies of resolvable with id = 2
+select dt.name "dtype", kt.name "kind", (dname.name || '/' || fname.name) "file"
+  from file_capabilities c, types dt, types kt, files f
+    left outer join file_names fname on fname.id = f.file_name_id
+    left outer join dir_names dname on dname.id = f.dir_name_id
+  where c.dependency_type = dt.id
+    and c.refers_kind= kt.id
+    and c.file_id = f.id
+    and c.resolvable_id = 2;
+
diff --git a/devel/devel.jkupec/deltarpm.cc b/devel/devel.jkupec/deltarpm.cc
new file mode 100644 (file)
index 0000000..fcaaea4
--- /dev/null
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <iostream>
+#include <iterator>
+#include <list>
+
+extern "C"
+{
+#include <satsolver/repo.h>
+}
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/RepoManager.h"
+#include "zypp/repo/DeltaCandidates.h"
+#include "zypp/PoolQuery.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+
+
+struct PrintAndCount
+{
+  PrintAndCount() : _count(0) {}
+
+  bool operator()( const sat::Solvable & solvable )
+  {
+    zypp::PoolItem pi( zypp::ResPool::instance().find( solvable ) );
+    cout << pi.resolvable() << endl;
+    // name: yast2-sound 2.16.2-9 i586
+    ++_count;
+    return true;
+  }
+
+  unsigned _count;
+};
+
+
+
+int main (int argc, const char ** argv)
+{
+  Pathname rootdir(SRC_DIR "/data/deltarpm");
+  RepoManagerOptions opts(rootdir);
+  opts.repoRawCachePath = rootdir;
+  opts.repoSolvCachePath = rootdir;
+  RepoManager rm(opts);
+
+  RepoInfo updates;
+  updates.setAlias("updates");
+  updates.addBaseUrl(Url(string("dir:") + rootdir.absolutename().asString() + "/updates"));
+
+  RepoInfo updates2;
+  updates2.setAlias("updates2");
+  updates2.addBaseUrl(Url(string("dir:") + rootdir.absolutename().asString() + "/updates2"));
+
+  try
+  {
+    rm.buildCache(updates);
+    rm.buildCache(updates2);
+    rm.loadFromCache(updates);
+    rm.loadFromCache(updates2);
+  }
+  catch (const Exception & e)
+  {
+    cout << "Problem getting the data: " << e.msg() << endl;
+  }
+
+  sat::Pool pool(sat::Pool::instance());
+  for_(repoit, pool.reposBegin(), pool.reposEnd())
+  {
+    Repository repo(*repoit);
+    for (int i = 0; i < repo.get()->nextra; ++i)
+    {
+      cout << endl << "extra " << i << ":" << endl;
+      ::Dataiterator di;
+      ::dataiterator_init(&di, repo.get(), -1 - i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
+      while (::dataiterator_step(&di))
+      {
+        const char * keyname;
+        keyname = ::id2str(repo.get()->pool, di.key->name);
+        
+        cout << keyname << ": ";
+
+        switch (di.key->name)
+        {
+        case DELTA_PACKAGE_NAME:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_PACKAGE_EVR:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_PACKAGE_ARCH:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_LOCATION_DIR:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_LOCATION_NAME:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_LOCATION_EVR:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_LOCATION_SUFFIX:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_DOWNLOADSIZE:
+        {
+          cout << di.kv.num;
+          break;
+        }
+        case DELTA_CHECKSUM:
+        {
+          cout << di.kv.str;
+          break;
+        }
+        case DELTA_BASE_EVR:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_SEQ_NAME:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_SEQ_EVR:
+        {
+          cout << IdString(di.kv.id);
+          break;
+        }
+        case DELTA_SEQ_NUM:
+        {
+          cout << di.kv.str;
+          break;
+        }
+        default:
+          cout << "ingoring " << IdString(di.key->name) << endl;
+        }
+        cout << endl;
+      }
+    }
+  }
+
+  PoolQuery q;
+  q.addKind(ResKind::package);
+  q.addAttribute(sat::SolvAttr::name, "libzypp");
+  q.setMatchExact();
+  
+  std::for_each(q.begin(), q.end(), PrintAndCount());
+
+  PoolItem pi(*q.poolItemBegin());
+  if (pi)
+  {
+    Package::constPtr p = asKind<Package>(pi.resolvable());
+
+    std::list<Repository> repos( pool.reposBegin(), pool.reposEnd() );
+    repo::DeltaCandidates deltas(repos, p->name());
+    deltas.deltaRpms(p);
+  }
+  else
+    cout << "no such package" << endl;
+}
diff --git a/devel/devel.jkupec/pathinfo.cc b/devel/devel.jkupec/pathinfo.cc
new file mode 100644 (file)
index 0000000..40b97b5
--- /dev/null
@@ -0,0 +1,29 @@
+#include <iostream>
+
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+
+int main (int argc, const char ** argv)
+{
+  Pathname datadir(SRC_DIR "/data/pathinfo");
+  Pathname alink = datadir / "alink";
+  PathInfo alinkinfo(alink);
+
+  cout << "alink exists: " << alinkinfo.isExist() << endl;
+  Pathname alinkExp = filesystem::expandlink(alink);
+  cout << "alink expands to: " << alinkExp << endl;
+  cout << "alinkExp exists: " << PathInfo(alinkExp).isExist() << endl;
+
+  Pathname subdirlink = datadir / "subdirlink";
+  PathInfo subdirlinkinfo(subdirlink);
+
+  cout << "subdirlink exists: " << subdirlinkinfo.isExist() << endl;
+  Pathname subdirlinkExp = filesystem::expandlink(subdirlink);
+  cout << "subdirlink expands to: " << subdirlinkExp << endl;
+  cout << "subdirlinkExp exists: " << PathInfo(subdirlinkExp).isExist() << endl;
+}
diff --git a/devel/devel.jkupec/play.cc b/devel/devel.jkupec/play.cc
new file mode 100644 (file)
index 0000000..5133d74
--- /dev/null
@@ -0,0 +1,56 @@
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/ZYppCallbacks.h"
+
+#include "zypp/parser/HistoryLogReader.h"
+#include "zypp/parser/ParseException.h"
+
+using std::endl;
+using std::cout;
+using namespace zypp;
+
+bool progress_function(const ProgressData & p)
+{
+  cout << ".";
+  return true;
+}
+
+struct HistoryItemCollector
+{
+  std::vector<HistoryItem::Ptr> items;
+
+  bool operator()( const HistoryItem::Ptr & item_ptr )
+  {
+    items.push_back(item_ptr);
+    //cout << *item_ptr << endl;
+    return true;
+  }
+};
+
+// ---------------------------------------------------------------------------
+
+int main( int argc, const char * argv[] )
+{
+  --argc; ++argv; // skip arg 0
+
+  HistoryItemCollector ic;
+  parser::HistoryLogReader reader(*argv, boost::ref(ic));
+  reader.setIgnoreInvalidItems(true);
+  ProgressReportReceiver progress;
+  progress.connect();
+  try
+  {
+    //reader.readAll(&progress_function);
+    reader.readFromTo(Date("2009-01-01", "%Y-%m-%d"), Date("2009-01-02", "%Y-%m-%d"));
+  }
+  catch (const parser::ParseException & e)
+  {
+    cout << "error in " << *argv << ":" << endl;
+    cout << e.asUserHistory() << endl;
+  }
+  progress.disconnect();
+
+  cout << "got " << ic.items.size() << endl;
+  return 0;
+}
diff --git a/devel/devel.jkupec/poolquery.cc b/devel/devel.jkupec/poolquery.cc
new file mode 100644 (file)
index 0000000..e471807
--- /dev/null
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <iostream>
+#include <iterator>
+#include <list>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/PoolQueryUtil.tcc"
+#include "zypp/RepoInfo.h"
+#include "zypp/Arch.h"
+#include "zypp/Pathname.h"
+#include "zypp/base/Regex.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+
+
+bool result_cb( const sat::Solvable & solvable )
+{
+  zypp::PoolItem pi( zypp::ResPool::instance().find( solvable ) );
+  cout << pi.resolvable() << endl;
+  // name: yast2-sound 2.16.2-9 i586
+  return true;
+}
+
+
+static void init_pool()
+{
+  Pathname dir(TESTS_SRC_DIR);
+  dir += "/zypp/data/PoolQuery";
+
+  ZYpp::Ptr z = getZYpp();
+  ZConfig::instance().setSystemArchitecture(Arch("i586"));
+
+  RepoInfo i1; i1.setAlias("factory");
+  sat::Pool::instance().addRepoSolv(dir / "factory.solv", i1);
+  RepoInfo i2; i2.setAlias("factory-nonoss");
+  sat::Pool::instance().addRepoSolv(dir / "factory-nonoss.solv", i2);
+  RepoInfo i3; i3.setAlias("zypp_svn");
+  sat::Pool::instance().addRepoSolv(dir / "zypp_svn.solv", i3);
+  RepoInfo i5; i5.setAlias("pyton");
+  sat::Pool::instance().addRepoSolv(dir / "python.solv", i5);
+  RepoInfo i4; i4.setAlias("@System");
+  sat::Pool::instance().addRepoSolv(dir / "@System.solv", i4);
+}
+
+
+int main (int argc, const char ** argv)
+{
+  // ./poolquery regex string
+  if (argc == 3)
+  {
+    str::regex regex(argv[1], REG_EXTENDED | REG_NOSUB | REG_NEWLINE | REG_ICASE);
+    cout << (str::regex_match(argv[2], regex) ? "" : "no") << "match" << endl;
+  }
+
+  init_pool();
+
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "cjson");
+
+  /*
+  PoolQuery q;
+  q.addString("weather");
+  q.addAttribute(sat::SolvAttr::name, "thunder");
+  q.addAttribute(sat::SolvAttr::description, "storm");
+  q.addKind(ResKind::package);
+  q.addRepo("factory");
+*/
+  std::for_each(q.begin(), q.end(), &result_cb);
+//  cout << q.size() << endl;
+//  cout << q << endl;
+  cout << "=====" << endl;
+  for_(it, q.selectableBegin(), q.selectableEnd())
+    cout << *it << endl;
+}
diff --git a/devel/devel.jkupec/repos.cc b/devel/devel.jkupec/repos.cc
new file mode 100755 (executable)
index 0000000..125f8cd
--- /dev/null
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <iostream>
+#include <iterator>
+#include <list>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/Arch.h"
+#include "zypp/Pathname.h"
+#include "zypp/RepoManager.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+
+
+bool result_cb( const sat::Solvable & solvable )
+{
+  zypp::PoolItem pi( zypp::ResPool::instance().find( solvable ) );
+  cout << pi.resolvable() << endl;
+  // name: yast2-sound 2.16.2-9 i586
+  return true;
+}
+
+
+static void init_pool()
+{
+  Pathname dir(TESTS_SRC_DIR);
+  dir += "/zypp/data/PoolQuery";
+
+  ZYpp::Ptr z = getZYpp();
+  ZConfig::instance().setSystemArchitecture(Arch("i586"));
+
+  RepoInfo i1; i1.setAlias("factory");
+  sat::Pool::instance().addRepoSolv(dir / "factory.solv", i1);
+  RepoInfo i2; i2.setAlias("factory-nonoss");
+  sat::Pool::instance().addRepoSolv(dir / "factory-nonoss.solv", i2);
+  RepoInfo i3; i3.setAlias("zypp_svn");
+  sat::Pool::instance().addRepoSolv(dir / "zypp_svn.solv", i3);
+  RepoInfo i4; i4.setAlias("@System");
+  sat::Pool::instance().addRepoSolv(dir / "@System.solv", i4);
+}
+
+
+int main (int argc, const char ** argv)
+{
+  string _target_root = "/local/jkupec/rr";
+
+  RepoManagerOptions repo_options(_target_root);
+  // repo_options.knownReposPath = Pathname(_target_root) + repo_options.knownReposPath;
+
+  RepoManager rm(repo_options);
+
+  for ( RepoManager::RepoConstIterator it = rm.repoBegin();
+        it != rm.repoEnd(); ++it )
+  {
+    cout << it->packagesPath() << endl;
+  }
+}
diff --git a/devel/devel.jkupec/yum/patterns-example.xml b/devel/devel.jkupec/yum/patterns-example.xml
new file mode 100644 (file)
index 0000000..b2364e3
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- valid against patterns.rnc r5590 -->
+<patterns
+    xmlns="http://novell.com/package/metadata/suse/pattern"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    count="2">
+  <pattern>
+    <name>example pattern</name>
+    <summary lang="en">Just an example pattern</summary>
+    <summary lang="sk">Toto je len príklad patternu</summary>
+    <description lang="en">Description of the example pattern</description>
+    <description lang="sk">Detailnejší popis patternu</description>
+    <!-- <default/> -->
+    <uservisible/>
+    <category lang="en">Services</category>
+    <category lang="sk">Služby</category>
+    <icon>repodata/example-pattern.png</icon>
+    <rpm:requires>
+      <rpm:entry kind="package" name="pikaball" epoch="0" ver="3.0" rel="3" flags="GE"/>
+    </rpm:requires>
+  </pattern>
+  <pattern>
+    <name>funky apps</name>
+    <summary lang="en">Just an example of funky pattern</summary>
+    <summary lang="sk">Príklad funky patternu</summary>
+    <description lang="en">The best apps</description>
+    <description lang="sk">Najsamlepjšie programčoke</description>
+    <default/>
+    <uservisible/>
+    <category lang="en">Desktop Apps</category>
+    <category lang="sk">Klientské aplikácie</category>
+    <icon>repodata/funky-apps-pattern.png</icon>
+    <rpm:requires>
+      <rpm:entry kind="product" name="konsole" epoch="0" ver="2.1.5" rel="1" flags="GE"/>
+    </rpm:requires>
+  </pattern>
+</patterns>
diff --git a/devel/devel.jkupec/yum/products-example.xml b/devel/devel.jkupec/yum/products-example.xml
new file mode 100644 (file)
index 0000000..e139050
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- valid against products.rnc r5591 -->
+<products
+    xmlns="http://novell.com/package/metadata/suse/products"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm">
+  <product type="type">
+    <vendor>Novell, Inc.</vendor>
+    <name>SLES10-SP1</name>
+    <version epoch="0" ver="10.0.15" rel="1"/>
+    <displayname lang="en">SUSE Linux Enterprise Server 10 SP1</displayname>
+    <shortname lang="en">SLES 10 SP1</shortname>
+    <description lang="en">The best server OS so far</description>
+    <description lang="sk">Doposiaľ najlepší OS pre servery</description>
+    <rpm:requires>
+      <rpm:entry kind="ackage" name="pikaball" epoch="0" ver="3.0" rel="3" flags="GE" pre="1"/>
+      <rpm:entry kind="package" name="base" epoch="0" ver="3.0" rel="3" flags="GE"/>
+    </rpm:requires>
+    <rpm:provides>
+      <rpm:entry kind="product" name="SLES10-SP1"/>
+    </rpm:provides>
+  </product>
+</products>
diff --git a/devel/devel.ma/AOUT.cc b/devel/devel.ma/AOUT.cc
new file mode 100644 (file)
index 0000000..b56ad18
--- /dev/null
@@ -0,0 +1,36 @@
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/ProgressData.h"
+
+#include "zypp/base/Random.h"
+
+#include <boost/thread.hpp>
+
+using std::endl;
+using namespace zypp;
+
+
+void action( int i_r )
+{
+  unsigned sec = base::random( 3 );
+  sleep( sec );
+  MIL << "Action " << i_r << " (" << sec << ")" << endl;
+}
+
+
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+
+  for ( unsigned i = 0; i < 5; ++i )
+  {
+    new boost::thread( bind( action, i ) );
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return ( 0 );
+}
diff --git a/devel/devel.ma/Basic.cc b/devel/devel.ma/Basic.cc
new file mode 100644 (file)
index 0000000..35ef432
--- /dev/null
@@ -0,0 +1,88 @@
+#include "Tools.h"
+
+#include <iostream>
+
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/String.h>
+#include <zypp/base/SerialNumber.h>
+#include <zypp/ExternalProgram.h>
+#include <zypp/PathInfo.h>
+#include <zypp/TmpPath.h>
+#include <zypp/ResPoolProxy.h>
+#include <zypp/repo/PackageProvider.h>
+
+static const Pathname sysRoot( "/" );
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::ui;
+
+bool queryInstalledEditionHelper( const std::string & name_r,
+                                  const Edition &     ed_r,
+                                  const Arch &        arch_r )
+{
+  if ( ed_r == Edition::noedition )
+    return true;
+  if ( name_r == "kernel-default" && ed_r == Edition("2.6.22.5-10") )
+    return true;
+  if ( name_r == "update-test-affects-package-manager" && ed_r == Edition("1.1-6") )
+    return true;
+
+  return false;
+}
+
+ManagedFile repoProvidePackage( const PoolItem & pi )
+{
+  ResPool _pool( getZYpp()->pool() );
+  repo::RepoMediaAccess _access;
+
+  // Redirect PackageProvider queries for installed editions
+  // (in case of patch/delta rpm processing) to rpmDb.
+  repo::PackageProviderPolicy packageProviderPolicy;
+  packageProviderPolicy.queryInstalledCB( queryInstalledEditionHelper );
+
+  Package::constPtr p = asKind<Package>( pi.resolvable() );
+
+  // Build a repository list for repos
+  // contributing to the pool
+  repo::DeltaCandidates deltas;//( repo::makeDeltaCandidates( _pool.knownRepositoriesBegin(), _pool.knownRepositoriesEnd() ) );
+
+  repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
+
+  return pkgProvider.providePackage();
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  --argc;
+  ++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ::unsetenv( "ZYPP_CONF" );
+  ZConfig::instance();
+  TestSetup::LoadSystemAt( sysRoot );
+  ///////////////////////////////////////////////////////////////////
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+  dumpRange( USR, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  USR << "pool: " << pool << endl;
+
+  PoolItem pi( getPi<Package>( "amarok" ) );
+  SEC << pi << endl;
+  ManagedFile f( repoProvidePackage( pi ) );
+  SEC << f << endl;
+  //f.resetDispose();
+  ExternalProgram("find /tmp/var") >> DBG;
+  DBG << endl;
+
+  INT << "===[END]============================================" << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
diff --git a/devel/devel.ma/CMakeLists.txt b/devel/devel.ma/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f42a127
--- /dev/null
@@ -0,0 +1,42 @@
+## ############################################################
+
+#ADD_SUBDIRECTORY( doc )
+
+## ############################################################
+
+INCLUDE( FindQt3 )
+IF ( NOT QT_FOUND )
+  MESSAGE( WARNING " qt3 not found" )
+ELSE ( NOT QT_FOUND )
+  ADD_DEFINITIONS( ${QT_DEFINITIONS} )
+  INCLUDE_DIRECTORIES( ${QT_INCLUDE_DIR} )
+  #SET( QT_LIBRARIES
+  #"/usr/lib${LIB_SUFFIX}/qt3/lib${LIB_SUFFIX}/libqassistantclient.a;/usr/lib${LIB_SUFFIX}/qt3/lib${LIB_SUFFIX}/libqt-mt.so;-lSM;-lICE;/usr/X11R6/lib${LIB_SUFFIX}/libX11.so;/usr/X11R6/lib${LIB_SUFFIX}/libXext.so;dl;-lpthread"
+  #)
+ENDIF( NOT QT_FOUND )
+
+SET(THREAD_LIBRARY boost_thread-mt)
+
+## ############################################################
+
+ADD_CUSTOM_TARGET( ma_test ALL
+   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ma_test
+   COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/ma_test ${CMAKE_CURRENT_BINARY_DIR}/ma_test
+)
+
+## ############################################################
+
+FILE( GLOB ALLCC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" )
+STRING( REPLACE ".cc" ";" APLLPROG ${ALLCC} )
+FOREACH( loop_var ${APLLPROG} )
+   ADD_EXECUTABLE( ${loop_var}
+      ${loop_var}.cc
+   )
+   TARGET_LINK_LIBRARIES( ${loop_var}
+    boost_signals
+    zypp
+    #{THREAD_LIBRARY}
+   )
+ENDFOREACH( loop_var )
+
+## ############################################################
diff --git a/devel/devel.ma/CleandepsOnRemove.cc b/devel/devel.ma/CleandepsOnRemove.cc
new file mode 100644 (file)
index 0000000..003de04
--- /dev/null
@@ -0,0 +1,141 @@
+#include "Tools.h"
+
+#include <zypp/PoolQuery.h>
+#include <zypp/target/rpm/librpmDb.h>
+#include <zypp/parser/ProductFileReader.h>
+#include "zypp/pool/GetResolvablesToInsDel.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/ExternalProgram.h"
+
+///////////////////////////////////////////////////////////////////
+
+//static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+//static const Pathname sysRoot( "/tmp/ToolScanRepos" );
+static const Pathname sysRoot( "/" );
+
+///////////////////////////////////////////////////////////////////
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    //getZYpp()->resolver()->setOnlyRequires( true );
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    Measure x( "Upgrade" );
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    Measure x( "Upgrade Error" );
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  return true;
+}
+
+namespace zypp
+{
+  namespace target
+  {
+    void writeUpgradeTestcase();
+  }
+}
+
+std::ostream & operator<<( std::ostream & str, const sat::Solvable::SplitIdent & obj )
+{
+  str << "{" << obj.ident() << "}{" << obj.kind() << "}{" << obj.name () << "}" << endl;
+  return str;
+}
+
+namespace zypp {
+std::ostream & dumpOn( std::ostream & str, const Url & obj )
+{
+  str << "{" << obj.getHost() << "}{" << obj.getPort() << "}";
+  return str;
+}
+}
+
+int main( int argc, char * argv[] )
+try {
+  --argc,++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ///////////////////////////////////////////////////////////////////
+  if ( sysRoot == "/" )
+    ::unsetenv( "ZYPP_CONF" );
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+  dumpRange( WAR << "satpool.multiversion " , satpool.multiversionBegin(), satpool.multiversionEnd() ) << endl;
+  TestSetup::LoadSystemAt( sysRoot, Arch_i586 );
+  ///////////////////////////////////////////////////////////////////
+
+  char * fix[] = {
+      "test"
+  };
+  argv = fix;
+  argc = arraySize(fix);
+  for ( ; argc; --argc,++argv )
+  {
+    ui::Selectable::Ptr p( getSel<Package>( *argv ) );
+    if ( p )
+      USR << p->setToDelete() << endl;
+    else
+      ERR << p << endl;
+  }
+
+  std::set<PoolItem> todel;
+  {
+    getZYpp()->resolver()->setCleandepsOnRemove( false );
+    SEC << "=== Solve noclean:" << endl;
+    solve();
+    std::copy( make_filter_begin<resfilter::ByTransact>(pool),
+              make_filter_end<resfilter::ByTransact>(pool),
+              std::inserter( todel, todel.begin() ) );
+    WAR << todel << endl;
+  }
+  {
+    getZYpp()->resolver()->setCleandepsOnRemove( true );
+    SEC << "=== Solve clean:" << endl;
+    solve();
+    SEC << "========================================================" << endl;
+    for_( it, make_filter_begin<resfilter::ByTransact>(pool), make_filter_end<resfilter::ByTransact>(pool) )
+    {
+      ( todel.find( *it ) == todel.end() ? INT : USR ) << *it << endl;
+    }
+    SEC << "========================================================" << endl;
+  }
+
+
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+}
+catch (...)
+{}
+
diff --git a/devel/devel.ma/CommitCb.cc b/devel/devel.ma/CommitCb.cc
new file mode 100644 (file)
index 0000000..a0990ba
--- /dev/null
@@ -0,0 +1,164 @@
+#include "Tools.h"
+#include <zypp/ResObjects.h>
+
+#include <zypp/sat/LookupAttr.h>
+#include <zypp/PoolQuery.h>
+#include <zypp/ZYppCallbacks.h>
+
+struct IRR : public zypp::callback::ReceiveReport<zypp::target::rpm::InstallResolvableReport>
+{
+  IRR()
+  { connect(); }
+#if 0
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,     // retry
+    IGNORE     // ignore the failure
+  };
+
+  enum Error {
+    NO_ERROR,
+    NOT_FOUND,         // the requested Url was not found
+    IO,                // IO error
+    INVALID            // th resolvable is invalid
+  };
+
+        // the level of RPM pushing
+  /** \deprecated We fortunately no longer do 3 attempts. */
+  enum RpmLevel {
+    RPM,
+    RPM_NODEPS,
+    RPM_NODEPS_FORCE
+  };
+#endif
+
+  virtual void reportbegin()
+  { SEC << endl; }
+  virtual void reportend()
+  { SEC << endl; }
+
+  virtual void start(Resolvable::constPtr /*resolvable*/)
+  { INT << endl; }
+
+  virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+  {
+    static int i = 4;
+    if ( --i <= 0 )
+    {
+      INT << "return abort" << endl;
+      return false;
+    }
+    return true;
+  }
+
+  virtual Action problem(Resolvable::constPtr /*resolvable*/, Error /*error*/, const std::string &/*description*/, RpmLevel /*level*/)
+  {
+    INT << "return abort" << endl;
+    return ABORT;
+  }
+
+  virtual void finish(Resolvable::constPtr /*resolvable*/, Error /*error*/, const std::string &/*reason*/, RpmLevel /*level*/)
+  { INT << endl; }
+};
+
+struct RRR : public zypp::callback::ReceiveReport<zypp::target::rpm::RemoveResolvableReport>
+{
+  RRR()
+  { connect(); }
+#if 0
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,     // retry
+    IGNORE     // ignore the failure
+  };
+
+  enum Error {
+    NO_ERROR,
+    NOT_FOUND,         // the requested Url was not found
+    IO,                // IO error
+    INVALID            // th resolvable is invalid
+  };
+#endif
+
+  virtual void reportbegin()
+  { SEC << endl; }
+  virtual void reportend()
+  { SEC << endl; }
+
+  virtual void start( Resolvable::constPtr /*resolvable*/ )
+  { INT << endl; }
+
+  virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+  { INT << endl; return true; }
+
+  virtual Action problem( Resolvable::constPtr /*resolvable*/ , Error /*error*/ , const std::string &/*description*/ )
+  { INT << endl; return ABORT; }
+
+  virtual void finish( Resolvable::constPtr /*resolvable*/ , Error /*error*/ , const std::string &/*reason*/ )
+  { INT << endl; }
+};
+
+bool solve()
+{
+  static unsigned run = 0;
+  USR << "Solve " << run++ << endl;
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  return true;
+}
+
+bool install()
+{
+  ZYppCommitPolicy pol;
+//pol.dryRun(true);
+  pol.rpmInstFlags( pol.rpmInstFlags().setFlag( target::rpm::RPMINST_JUSTDB ) );
+  SEC << "START commit..." << endl;
+  SEC << getZYpp()->commit( pol ) << endl;
+  return true;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  IRR _irr;
+  RRR _rrr;
+  Pathname mroot( "/tmp/ToolScanRepos" );
+  TestSetup test( mroot, Arch_i586 );
+  test.loadTarget();
+  test.loadRepos();
+
+  ResPool pool( test.pool() );
+  ui::Selectable::Ptr sel;
+
+  getSel<Package>( "rpm" )->setToInstall();
+  vdumpPoolStats( USR << "Selected:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(pool),
+                  make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+  if ( solve() )
+  {
+    vdumpPoolStats( USR << "Solved:"<< endl,
+                    make_filter_begin<resfilter::ByTransact>(pool),
+                    make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+    install();
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
diff --git a/devel/devel.ma/DumpSolv.cc b/devel/devel.ma/DumpSolv.cc
new file mode 100644 (file)
index 0000000..86c3329
--- /dev/null
@@ -0,0 +1,289 @@
+#include "Tools.h"
+
+#include <zypp/ResObjects.h>
+#include <zypp/sat/WhatObsoletes.h>
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+static std::string appname( __FILE__ );
+static TestSetup test;
+
+///////////////////////////////////////////////////////////////////
+
+#define OUT   USR
+#define HEADL SEC << "===> "
+
+inline std::ostream & errmessage( const std::string & msg_r = std::string() )
+{
+  cerr << "*** ";
+  if ( ! msg_r.empty() )
+    cerr << msg_r << endl;
+  return cerr;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl;
+    errmessage( msg_r );
+    cerr << endl;
+  }
+  cerr << "Usage: " << appname << " TESTCASE" << endl;
+  cerr << "  Load and process testcase." << endl;
+  return exit_r;
+}
+
+///////////////////////////////////////////////////////////////////
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  return true;
+}
+
+bool solve()
+{
+  static unsigned run = 0;
+  USR << "Solve " << run++ << endl;
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+/**
+*/
+struct ArgList
+{
+  typedef std::vector<std::string>::const_iterator const_iterator;
+
+  ArgList()
+  {}
+
+  ArgList( const std::string & line_r )
+  { str::splitEscaped( line_r, std::back_inserter(_argv) ); }
+
+  const_iterator begin() const { const_iterator ret =_argv.begin(); for ( unsigned i = _carg; i; --i ) ++ret; return ret; }
+  const_iterator end()   const { return _argv.end(); }
+
+  void     clear()       { _argv.clear(); _carg = 0; }
+  bool     empty() const { return _argv.size() == _carg; }
+  unsigned size()  const { return _argv.size() - _carg; }
+
+  std::string &       operator[]( int idx )       { return _argv[_carg+idx]; }
+  const std::string & operator[]( int idx ) const { return _argv[_carg+idx]; }
+
+  std::string at( int idx ) const { return _carg+idx < _argv.size() ? _argv[_carg+idx] : std::string(); }
+
+  unsigned carg() const { return _carg; }
+  void poparg( int cnt = 1 ) { _carg = arange( _carg + cnt ); }
+
+  public:
+    std::vector<std::string> &       get()       { return _argv; }
+    const std::vector<std::string> & get() const { return _argv; }
+ private:
+   unsigned arange( int idx ) const { return idx < 0 ? 0 : std::min( unsigned(idx), _argv.size() ); }
+ private:
+    DefaultIntegral<unsigned,0> _carg;
+    std::vector<std::string> _argv;
+};
+
+std::ostream & operator<<( std::ostream & str, const ArgList & obj )
+{
+  for_( it, 0U, obj.get().size() )
+  {
+    str << ( it == obj.carg() ? " | " : " " ) << obj.get()[it];
+  }
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+#define DELGATE(N,F) if ( argv.at(0) == #N ) { argv.poparg(); F( argv ); return; }
+///////////////////////////////////////////////////////////////////
+
+void exitCmd( ArgList & argv )
+{
+  HEADL << argv << endl;
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::TmpLineWriter shutUp;
+  ::exit( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+
+void helpCmd( ArgList & argv )
+{
+  HEADL << argv << endl;
+  OUT << "list repos   - list repos in pool" << endl;
+  OUT << "list NAME... - list solvables named or providing NAME" << endl;
+  OUT << "help         - this" << endl;
+  OUT << "exit         - exit" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void listReposCmd( ArgList & argv )
+{
+  HEADL << "list repos" << endl;
+
+  sat::Pool satpool( test.satpool() );
+  for_( it, satpool.reposBegin(), satpool.reposEnd() )
+  {
+    OUT << *it << endl;
+  }
+}
+
+void listIdent( IdString ident_r )
+{
+  HEADL << "list " << ident_r << endl;
+
+  ui::Selectable::Ptr sel( ui::Selectable::get( ident_r ) );
+  if ( sel )
+  {
+    OUT << sel->ident()
+        << " I" << sel->installedSize()
+        << " A" << sel->availableSize()
+        << " " << sel->status()
+        << endl;
+    for_( it, sel->installedBegin(), sel->installedEnd() )
+    {
+      OUT << "i " << *it << endl;
+    }
+    PoolItem cand( sel->candidateObj() );
+    for_( it, sel->availableBegin(), sel->availableEnd() )
+    {
+      OUT << (*it == cand ? "* " : "  ") << *it << endl;
+    }
+  }
+
+  {
+    sat::WhatProvides q( (Capability( ident_r.id() )) );
+    bool head = true;
+    for_( it, q.begin(), q.end() )
+    {
+      if ( it->ident() != ident_r )
+      {
+        if ( head )
+        {
+          OUT << "provided by:" << endl;
+          head = false;
+        }
+        OUT << "  " << PoolItem( *it ) << endl;
+      }
+    }
+  }
+}
+
+
+void listCmd( ArgList & argv )
+{
+  DELGATE( repos, listReposCmd );
+
+  for_( it, argv.begin(), argv.end() )
+  {
+    listIdent( IdString(*it) );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+
+void gocmd( ArgList & argv )
+{
+  if ( argv.empty() )
+  {
+    helpCmd( argv );
+    return;
+  }
+
+  switch ( argv[0][0] )
+  {
+    case 'e':
+      DELGATE( exit, exitCmd );
+      break;
+
+    case 'h':
+      DELGATE( help, helpCmd );
+      break;
+
+    case 'l':
+      DELGATE( list, listCmd );
+      break;
+  }
+  // no command fall back to list
+  listCmd( argv );
+}
+
+void goprompt()
+{
+  std::cin.tie( &std::cout );
+
+  do {
+    ArgList argv;
+    std::cout << "Hallo : ";
+    str::splitEscaped( iostr::getline( std::cin ), std::back_inserter(argv.get()) );
+    gocmd( argv );
+  } while ( true );
+
+}
+
+///////////////////////////////////////////////////////////////////
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc;
+  ++argv;
+  ///////////////////////////////////////////////////////////////////
+
+  if ( !argc )
+    return usage();
+
+  Pathname mtest( *argv );
+  --argc;
+  ++argv;
+
+  if ( ! PathInfo( mtest / "solver-test.xml" ).isFile() )
+  {
+    return usage( "No testcase at " + mtest.asString() );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  test.loadTestcaseRepos( mtest ); // <<< repos
+#define GOCMD(c) { ArgList argv( #c ); gocmd( argv ); }
+  GOCMD( tgt );
+  GOCMD( iscsitarget );
+  goprompt();
+
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::TmpLineWriter shutUp;
+  return 0;
+}
diff --git a/devel/devel.ma/Ex.cc b/devel/devel.ma/Ex.cc
new file mode 100644 (file)
index 0000000..97a49b7
--- /dev/null
@@ -0,0 +1,166 @@
+#include "Tools.h"
+
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/Exception.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/ProvideNumericId.h>
+#include <zypp/AutoDispose.h>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/NVRAD.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/Package.h"
+#include "zypp/Pattern.h"
+#include "zypp/Language.h"
+#include "zypp/Digest.h"
+#include "zypp/PackageKeyword.h"
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+#include "zypp/parser/TagParser.h"
+#include "zypp/parser/susetags/PackagesFileReader.h"
+#include "zypp/parser/susetags/PackagesLangFileReader.h"
+#include "zypp/parser/susetags/PatternFileReader.h"
+#include "zypp/parser/susetags/ContentFileReader.h"
+#include "zypp/parser/susetags/RepoIndex.h"
+#include "zypp/parser/susetags/RepoParser.h"
+#include "zypp/cache/CacheStore.h"
+#include "zypp/RepoManager.h"
+#include "zypp/RepoInfo.h"
+
+#include "zypp/ui/PatchContents.h"
+#include "zypp/ResPoolProxy.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::ui;
+using zypp::parser::TagParser;
+
+///////////////////////////////////////////////////////////////////
+
+static const Pathname sysRoot( "/Local/GTEST" );
+
+///////////////////////////////////////////////////////////////////
+
+struct Xprint
+{
+  bool operator()( const PoolItem & obj_r )
+  {
+//     handle( asKind<Package>( obj_r ) );
+//     handle( asKind<Patch>( obj_r ) );
+//     handle( asKind<Pattern>( obj_r ) );
+//     handle( asKind<Product>( obj_r ) );
+    return true;
+  }
+
+  void handle( const Package_constPtr & p )
+  {
+    if ( !p )
+      return;
+  }
+
+  void handle( const Patch_constPtr & p )
+  {
+    if ( !p )
+      return;
+  }
+
+  void handle( const Pattern_constPtr & p )
+  {
+    if ( !p )
+      return;
+  }
+
+  void handle( const Product_constPtr & p )
+  {
+    if ( !p )
+      return;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  //zypp::base::LogControl::instance().logfile( "log.restrict" );
+  INT << "===[START]==========================================" << endl;
+
+  setenv( "ZYPP_CONF", (sysRoot/"zypp.conf").c_str(), 1 );
+
+  RepoManager repoManager( makeRepoManager( sysRoot ) );
+  RepoInfoList repos = repoManager.knownRepositories();
+  SEC << "knownRepositories " << repos << endl;
+
+  if ( repos.empty() )
+  {
+    RepoInfo nrepo;
+    nrepo
+       .setAlias( "factorytest" )
+       .setName( "Test Repo for factory." )
+       .setEnabled( true )
+       .setAutorefresh( false )
+       .addBaseUrl( Url("http://dist.suse.de/install/stable-x86/") );
+
+    repoManager.addRepository( nrepo );
+    repos = repoManager.knownRepositories();
+  }
+
+  for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+  {
+    RepoInfo & nrepo( *it );
+    if ( ! nrepo.enabled() )
+      continue;
+
+    SEC << "refreshMetadata" << endl;
+    repoManager.refreshMetadata( nrepo );
+
+    if ( ! repoManager.isCached( nrepo ) || 0 )
+    {
+      if ( repoManager.isCached( nrepo ) )
+      {
+       SEC << "cleanCache" << endl;
+       repoManager.cleanCache( nrepo );
+      }
+      SEC << "refreshMetadata" << endl;
+      repoManager.refreshMetadata( nrepo, RepoManager::RefreshForced );
+      SEC << "buildCache" << endl;
+      repoManager.buildCache( nrepo );
+    }
+
+    SEC << nrepo << endl;
+    Repository nrep( repoManager.createFromCache( nrepo ) );
+    const zypp::ResStore & store( nrep.resolvables() );
+    dumpPoolStats( SEC << "Store: " << endl,
+                  store.begin(), store.end() ) << endl;
+    getZYpp()->addResolvables( store );
+  }
+
+  ResPool pool( getZYpp()->pool() );
+  vdumpPoolStats( USR << "Initial pool:" << endl,
+                 pool.begin(),
+                 pool.end() ) << endl;
+
+  if ( 0 )
+  {
+    {
+      //zypp::base::LogControl::TmpLineWriter shutUp;
+      getZYpp()->initTarget( sysRoot );
+    }
+    MIL << "Added target: " << pool << endl;
+  }
+
+  std::for_each( pool.begin(), pool.end(), Xprint() );
+
+ ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
diff --git a/devel/devel.ma/ExPure.cc b/devel/devel.ma/ExPure.cc
new file mode 100644 (file)
index 0000000..6b353a8
--- /dev/null
@@ -0,0 +1,189 @@
+#include <libxml/xmlreader.h>
+
+#include <iostream>
+
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Function.h>
+#include <zypp/base/GzStream.h>
+#include <zypp/parser/yum/YUMParser.h>
+#include <zypp/Pathname.h>
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::parser::yum;
+
+#include "zypp/parser/yum/YUMParser.h"
+
+///////////////////////////////////////////////////////////////////
+
+template<class _Cl>
+  void ti( const _Cl & c )
+  {
+    SEC << __PRETTY_FUNCTION__ << endl;
+  }
+///////////////////////////////////////////////////////////////////
+
+template<class _Parser>
+  bool consume( const typename _Parser::value_type & node_r )
+  {
+    //DBG << node_r << endl;
+    return true;
+  }
+
+template<class _Parser>
+  void parseXmlFile( const Pathname & file_r,
+                     function<bool(const typename _Parser::value_type &)> consume_r
+                     = consume<_Parser> )
+  {
+    Measure x( "    zparse "+file_r.asString() );
+    ifgzstream istr( file_r.asString().c_str() );
+    if ( ! istr )
+      {
+        ZYPP_THROW( Exception( "Bad stream" ) );
+      }
+
+    for( _Parser parser( istr, "" ); ! parser.atEnd(); ++parser )
+      {
+        if ( consume_r && ! consume_r( *parser ) )
+          {
+            DBG << "abort parseXmlFile " << file_r << endl;
+            return;
+          }
+      }
+  }
+
+bool consumeRepomd( const YUMRepomdParser::value_type & node_r )
+{
+  DBG << node_r << endl;
+  return true;
+}
+
+void zparse( const Pathname & repodata_r )
+{
+  Measure x( "ZPARSE" );
+  parseXmlFile<YUMRepomdParser>  ( repodata_r / "repomd.xml", consumeRepomd );
+  parseXmlFile<YUMPrimaryParser> ( repodata_r / "primary.xml" );
+  parseXmlFile<YUMOtherParser>   ( repodata_r / "other.xml" );
+  parseXmlFile<YUMFileListParser>( repodata_r / "filelists.xml" );
+  //parseXmlFile<YUMPatchesParser> ( repodata_r / "patches.xml" );
+}
+
+///////////////////////////////////////////////////////////////////
+
+/**
+ * processNode:
+ * @reader: the xmlReader
+ *
+ * Dump information about the current node
+ */
+template<class _ParserValueType>
+static void
+processNode(xmlTextReaderPtr reader, const _ParserValueType & stp ) {
+    const xmlChar *name, *value;
+
+    name = xmlTextReaderConstName(reader);
+    if (name == NULL)
+       name = BAD_CAST "--";
+
+    value = xmlTextReaderConstValue(reader);
+    string t;
+    if ( value )
+      {
+        t = (const char *)value;
+      }
+    return;
+    printf("%d %d %s %d %d",
+           xmlTextReaderDepth(reader),
+           xmlTextReaderNodeType(reader),
+           name,
+           xmlTextReaderIsEmptyElement(reader),
+           xmlTextReaderHasValue(reader));
+    if (value == NULL)
+       printf("\n");
+    else {
+        if (xmlStrlen(value) > 40)
+            printf(" %.40s...\n", value);
+        else
+           printf(" %s\n", value);
+    }
+}
+
+
+/**
+ * streamFile:
+ * @filename: the file name to parse
+ *
+ * Parse and print information about an XML file.
+ */
+template<class _Parser>
+static void
+streamFile(const char *filename) {
+    Measure x( string("    lparse ")+filename );
+    xmlTextReaderPtr reader;
+    int ret;
+
+    typename _Parser::value_type stp;
+
+    reader = xmlReaderForFile(filename, NULL, 0);
+    if (reader != NULL) {
+        ret = xmlTextReaderRead(reader);
+        while (ret == 1) {
+            stp = new typename _Parser::value_type::element_type;
+            processNode(reader, stp);
+            ret = xmlTextReaderRead(reader);
+        }
+        xmlFreeTextReader(reader);
+        if (ret != 0) {
+          ZYPP_THROW( Exception( string("Failed to parse ") + filename ) );
+        }
+    } else {
+      ZYPP_THROW( Exception( string("Unable to open ") + filename ) );
+    }
+}
+
+void lparse( const Pathname & repodata_r )
+{
+  Measure x( "LPARSE" );
+    /*
+     * this initialize the library and check potential ABI mismatches
+     * between the version it was compiled for and the actual shared
+     * library used.
+     */
+    LIBXML_TEST_VERSION
+
+    streamFile<YUMRepomdParser>  ( (repodata_r / "repomd.xml").asString().c_str() );
+    streamFile<YUMPrimaryParser> ( (repodata_r / "primary.xml").asString().c_str() );
+    streamFile<YUMOtherParser>   ( (repodata_r / "other.xml").asString().c_str() );
+    streamFile<YUMFileListParser>( (repodata_r / "filelists.xml").asString().c_str() );
+    //streamFile<YUMPatchesParser> ( (repodata_r / "patches.xml").asString().c_str() );
+
+    /*
+     * Cleanup function for the XML library.
+     */
+    xmlCleanupParser();
+
+    /*
+     * this is to debug memory for regression tests
+     */
+    xmlMemoryDump();
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+
+  Pathname repodata( "/Local/PATCHES/repodata" );
+  repodata = "/Local/FACTORY/repodata";
+  lparse( repodata );
+  zparse( repodata );
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
diff --git a/devel/devel.ma/ExplicitMap.h b/devel/devel.ma/ExplicitMap.h
new file mode 100644 (file)
index 0000000..4265f36
--- /dev/null
@@ -0,0 +1,252 @@
+/*---------------------------------------------------------------------\
+ |                          ____ _   __ __ ___                          |
+ |                         |__  / \ / / . \ . \                         |
+ |                           / / \ V /|  _/  _/                         |
+ |                          / /__ | | | | | |                           |
+ |                         /_____||_| |_| |_|                           |
+ |                                                                      |
+ \---------------------------------------------------------------------*/
+/** \file zypp/ExplicitMap.h
+ *
+*/
+#ifndef ZYPP_EXPLICITMAP_H
+#define ZYPP_EXPLICITMAP_H
+
+#include <iosfwd>
+#include <map>
+
+#include <boost/call_traits.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : ExplicitMap<_Key, _Tp>
+  //
+  /** A simple lookup map using default value for not existing entries.
+   *
+   * A std::map providing <tt>operator[] const</tt> only. Nor existing
+   * entries are mapped to a default value. Entries are maipulated vis
+   * methods \ref set and \ref unset. Helper classes \ref TmpSet,
+   * \ref TmpUnset and \ref TmpSetDefault are provided to temporarily
+   * change and automaticlly restore values.
+   */
+  template<class _Key, class _Tp>
+    class ExplicitMap
+    {
+    public:
+      typedef typename boost::call_traits<_Tp>::value_type       value_type;
+      typedef typename boost::call_traits<_Tp>::reference        reference;
+      typedef typename boost::call_traits<_Tp>::const_reference  const_reference;
+      typedef typename boost::call_traits<_Tp>::param_type       param_type;
+
+    private:
+      typedef typename std::map<_Key,value_type> map_type;
+      typedef typename map_type::iterator        iterator;
+
+    public:
+      typedef typename map_type::key_type       key_type;
+      typedef typename map_type::size_type      size_type;
+      typedef typename map_type::const_iterator const_iterator;
+
+    public:
+      ExplicitMap()
+      {}
+
+      explicit
+      ExplicitMap( param_type mapDefault_r )
+      : _mapDefault( mapDefault_r )
+      {}
+
+      template <class _InputIterator>
+        ExplicitMap( _InputIterator first_r, _InputIterator last_r )
+        : _map( first_r, last_r )
+        {}
+
+      template <class _InputIterator>
+        ExplicitMap( _InputIterator first_r, _InputIterator last_r,
+                     param_type mapDefault_r )
+        : _map( first_r, last_r )
+        , _mapDefault( mapDefault_r )
+        {}
+
+    public:
+      size_type size() const
+      { return _map.size(); }
+
+      bool empty() const
+      { return _map.empty(); }
+
+      const_iterator begin() const
+      { return _map.begin(); }
+
+      const_iterator end() const
+      { return _map.end(); }
+
+      const_iterator find( const key_type & key_r ) const
+      { return _map.find( key_r ); }
+
+      bool has( const key_type & key_r ) const
+      { return _map.find( key_r ) != end(); }
+
+      const_reference get( const key_type & key_r ) const
+      {
+        const_iterator it = _map.find( key_r );
+        return( it == _map.end() ? _mapDefault : it->second );
+      }
+
+      const_reference getDefault() const
+      { return _mapDefault; }
+
+      const_reference operator[]( const key_type & key_r ) const
+      { return get( key_r ); }
+
+    public:
+      void clear()
+      { _map.clear(); }
+
+      void set( const key_type & key_r, param_type value_r )
+      { _map[key_r] = value_r; }
+
+      template <typename _InputIterator>
+        void set( _InputIterator first_r, _InputIterator last_r )
+        { _map.insert( first_r, last_r ); }
+
+      void unset( const key_type & key_r )
+      { _map.erase( key_r ); }
+
+      void setDefault( param_type value_r )
+      { _mapDefault = value_r; }
+
+    public:
+      class TmpSet;
+      class TmpUnset;
+      class TmpSetDefault;
+
+      //private:
+      value_type _mapDefault;
+      map_type   _map;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : ExplicitMap<_Key, _Tp>::TmpSet
+  //
+  /** Temporarily set a value. */
+  template<class _Key, class _Tp>
+    class ExplicitMap<_Key, _Tp>::TmpSet
+    {
+    public:
+      TmpSet( ExplicitMap & map_r, const key_type & key_r, param_type value_r )
+      : _map( map_r )
+      , _key( key_r )
+      {
+        const_iterator it = _map.find( _key );
+        if ( it == _map.end() )
+          {
+            _wasDefault = true;
+          }
+        else
+          {
+            _wasDefault = false;
+            _value = it->second;
+          }
+        _map.set( _key, value_r );
+      }
+
+      ~TmpSet()
+      {
+        if ( _wasDefault )
+          {
+            _map.unset( _key );
+          }
+        else
+          {
+            _map.set( _key, _value );
+          }
+      }
+
+    private:
+      ExplicitMap & _map;
+      key_type      _key;
+      param_type    _value;
+      bool          _wasDefault;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : ExplicitMap<_Key, _Tp>::TmpUnset
+  //
+  /** Temporarily unset a value. */
+  template<class _Key, class _Tp>
+    class ExplicitMap<_Key, _Tp>::TmpUnset
+    {
+    public:
+      TmpUnset( ExplicitMap & map_r, const key_type & key_r )
+      : _map( map_r )
+      , _key( key_r )
+      {
+        const_iterator it = _map.find( _key );
+        if ( it == _map.end() )
+          {
+            _wasDefault = true;
+          }
+        else
+          {
+            _wasDefault = false;
+            _value = it->second;
+            _map.unset( _key );
+          }
+      }
+
+      ~TmpUnset()
+      {
+        if ( ! _wasDefault )
+          {
+            _map.set( _key, _value );
+          }
+      }
+
+    private:
+      ExplicitMap & _map;
+      key_type      _key;
+      param_type    _value;
+      bool          _wasDefault;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : ExplicitMap<_Key, _Tp>::TmpSetDefault
+  //
+  /** Temporarily change the default value. */
+  template<class _Key, class _Tp>
+    class ExplicitMap<_Key, _Tp>::TmpSetDefault
+    {
+    public:
+      TmpSetDefault( ExplicitMap & map_r, param_type value_r )
+      : _map( map_r )
+      , _value( _map.getDefault() )
+      {
+        _map.setDefault( value_r );
+      }
+
+      ~TmpSetDefault()
+      {
+        _map.setDefault( _value );
+      }
+
+    private:
+      ExplicitMap & _map;
+      param_type    _value;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_EXPLICITMAP_H
diff --git a/devel/devel.ma/FakePool.cc b/devel/devel.ma/FakePool.cc
new file mode 100644 (file)
index 0000000..3b0c10b
--- /dev/null
@@ -0,0 +1,84 @@
+#include "Tools.h"
+#include "Tools.h"
+
+#include <iostream>
+
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/String.h>
+#include <zypp/base/SerialNumber.h>
+#include <zypp/PathInfo.h>
+#include <zypp/TmpPath.h>
+#include "zypp/ResPoolProxy.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::ui;
+
+bool chst( Selectable::Ptr & sel, Status status )
+{
+  DBG << "+++ " << sel << endl;
+  Status ostatus( sel->status() );
+  bool res = sel->set_status( status );
+  (res?MIL:WAR) << ostatus << " -> " << status << " ==>(" << res << ") " << sel->status() << endl;
+  DBG << "--- " << sel << endl;
+  return res;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+
+  const char *const lines[] = {
+    "@ package",
+    "@ installed",
+    "- foo 1 1 i686",
+    "@ available",
+    "- foo 2 1 i686",
+    "@ fin"
+  };
+
+  debug::addPool( lines, lines+(sizeof(lines)/sizeof(const char *const)) );
+
+  ResPool      pool( getZYpp()->pool() );
+  ResPoolProxy uipool( getZYpp()->poolProxy() );
+
+  USR << pool << endl;
+  USR << uipool << endl;
+
+  //for_each( pool.begin(), pool.end(), Print() );
+
+  Selectable::Ptr sel( *uipool.byKindBegin<Package>() );
+
+/*    enum Status
+    {
+      S_Protected,           // Keep this unmodified ( have installedObj && S_Protected )
+      S_Taboo,               // Keep this unmodified ( have no installedObj && S_Taboo)
+      // requested by user:
+      S_Del,                 // delete  installedObj ( clears S_Protected if set )
+      S_Update,              // install candidateObj ( have installedObj, clears S_Protected if set )
+      S_Install,             // install candidateObj ( have no installedObj, clears S_Taboo if set )
+      // not requested by user:
+      S_AutoDel,             // delete  installedObj
+      S_AutoUpdate,          // install candidateObj ( have installedObj )
+      S_AutoInstall,         // install candidateObj ( have no installedObj )
+      // no modification:
+      S_KeepInstalled,       // no modification      ( have installedObj && !S_Protected, clears S_Protected if set )
+      S_NoInst,              // no modification      ( have no installedObj && !S_Taboo, clears S_Taboo if set )
+    };
+*/
+  MIL << sel << endl;
+  chst( sel, ui::S_Update );
+  chst( sel, ui::S_Install );
+  chst( sel, ui::S_Protected );
+  chst( sel, ui::S_KeepInstalled );
+
+  INT << "===[END]============================================" << endl
+      << endl;
+  return 0;
+}
diff --git a/devel/devel.ma/FakePool.h b/devel/devel.ma/FakePool.h
new file mode 100644 (file)
index 0000000..30c2c57
--- /dev/null
@@ -0,0 +1,257 @@
+#if 0
+#define FakePool_h
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Functional.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResPoolProxy.h"
+#include "zypp/CapFactory.h"
+
+#include "zypp/Atom.h"
+#include "zypp/Package.h"
+#include "zypp/SrcPackage.h"
+#include "zypp/Selection.h"
+#include "zypp/Pattern.h"
+#include "zypp/Product.h"
+#include "zypp/Patch.h"
+#include "zypp/Script.h"
+#include "zypp/Message.h"
+#include "zypp/Language.h"
+#include "zypp/VendorAttr.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace debug
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+     * \code
+     * const char * data[] = {
+     * "@ product"
+     * ,"@ installed"
+     * ,"- prodold 1 1 x86_64"
+     * ,"@ available"
+     * ,"- prodnew 1 1 x86_64"
+     * ,"@ obsoletes"
+     * ,"prodold"
+     * ,"@ fin"
+     * };
+     * DataCollect dataCollect;
+     * for_each( data, data + ( sizeof(data) / sizeof(const char *) ),
+     * function<void(const string &)>( ref( dataCollect ) ) );
+     * \endcode
+    */
+    class DataCollect
+    {
+    public:
+      DataCollect( bool verbose_r = true )
+      : _definst( false )
+      , _defkind( ResKind::package )
+      , _defdep( Dep::PROVIDES )
+      , _defdepref( _defkind )
+      , _verbose( verbose_r )
+      {
+       VendorAttr::disableAutoProtect();
+      }
+
+      bool operator()( const std::string & line_r )
+      {
+       parseLine( str::trim( line_r ) );
+       return true;
+      }
+
+      const ResStore & installed() const
+      { return _installed; }
+
+      const ResStore & available() const
+      { return _available; }
+
+      template<class _Iterator>
+        void collect( _Iterator begin_r, _Iterator end_r )
+        {
+          for_each( begin_r, end_r,
+                    function<void(const std::string &)>( ref(*this) ) );
+        }
+
+    private:
+      struct Data
+      {
+        Data( bool inst_r, Resolvable::Kind kind_r, const std::vector<std::string> & words_r )
+        : _inst( inst_r )
+        , _kind( kind_r )
+        , _data( words_r[1], Edition( words_r[2], words_r[3] ), Arch( words_r[4] ) )
+        {}
+
+        bool             _inst;
+        Resolvable::Kind _kind;
+        NVRAD            _data;
+      };
+
+    private:
+      void parseLine( const std::string & line_r )
+      {
+       if ( line_r.empty() || line_r[0] == '#' )
+         return;
+
+       std::vector<std::string> words;
+        str::split( line_r, std::back_inserter( words ) );
+        if ( words.empty() )
+          return;
+
+        if ( words[0] == "@" )
+          {
+            if ( words.size() < 2 )
+              throw line_r;
+            if ( words[1] == "installed" )
+              _definst = true;
+            else if ( words[1] == "available" )
+              _definst = false;
+            else if ( words[1] == "fin" )
+              finalize();
+            else
+              {
+                try
+                  {
+                    _defdep = Dep( words[1] );
+                    if ( words.size() > 2 )
+                      _defdepref = Resolvable::Kind( words[2] );
+                  }
+                catch ( ... )
+                  {
+                    _defkind = _defdepref = Resolvable::Kind( words[1] );
+                  }
+                return;
+              }
+          }
+        else if ( words[0] == "-" )
+          {
+            if ( words.size() == 5 )
+              {
+                finalize();
+                _d.reset( new Data( _definst, _defkind, words ) );
+              }
+            else
+              {
+                throw words;
+              }
+          }
+        else
+          {
+            _d->_data[_defdep].insert( CapFactory().parse( _defdepref, line_r ) );
+          }
+      }
+
+      void finalize()
+      {
+        if ( _d )
+          {
+            ResObject::Ptr p;
+            if ( _d->_kind == ResKind::package )
+              p = make<Package>();
+            else if ( _d->_kind == ResKind::srcpackage )
+              p = make<SrcPackage>();
+            else if ( _d->_kind == ResTraits<Selection>::kind )
+              p = make<Selection>();
+            else if ( _d->_kind == ResKind::pattern )
+              p = make<Pattern>();
+            else if ( _d->_kind == ResKind::product )
+              p = make<Product>();
+            else if ( _d->_kind == ResKind::patch )
+              p = make<Patch>();
+            else if ( _d->_kind == ResTraits<Script>::kind )
+              p = make<Script>();
+            else if ( _d->_kind == ResTraits<Message>::kind )
+              p = make<Message>();
+            else if ( _d->_kind == ResTraits<Language>::kind )
+              p = make<Language>();
+            else if ( _d->_kind == ResTraits<Atom>::kind )
+              p = make<Atom>();
+            else
+              throw _d->_kind;
+
+            if ( _verbose )
+              {
+                _MIL("FakePool") << (_d->_inst?"i":"a") << " " << p << std::endl;
+                _DBG("FakePool") << p->deps() << std::endl;
+              }
+
+            (_d->_inst?_installed:_available).insert( p );
+            _d.reset();
+          }
+      }
+
+      template<class _Res>
+        ResObject::Ptr make()
+        {
+          typename detail::ResImplTraits<typename _Res::Impl>::Ptr impl;
+          return zypp::detail::makeResolvableAndImpl( _d->_data, impl );
+        }
+
+    private:
+      bool             _definst;
+      Resolvable::Kind _defkind;
+      Dep              _defdep;
+      Resolvable::Kind _defdepref;
+
+      bool             _verbose;
+
+      shared_ptr<Data> _d;
+
+      ResStore         _installed;
+      ResStore         _available;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates DataCollect Stream output. */
+    inline std::ostream & operator<<( std::ostream & str, const DataCollect & obj )
+    {
+      dumpRange( str << "Installed" << endl,
+                 obj.installed().begin(),
+                 obj.installed().end() ) << endl;
+      dumpRange( str << "Available:" << endl,
+                 obj.available().begin(),
+                 obj.available().end() ) << endl;
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    template<class _Iterator>
+       inline void addPool( _Iterator begin_r, _Iterator end_r )
+    {
+      DataCollect dataCollect;
+      dataCollect.collect( begin_r, end_r );
+      getZYpp()->addResolvables( dataCollect.installed(), true );
+      getZYpp()->addResolvables( dataCollect.available() );
+    }
+
+    inline void addPool( const Pathname & file_r )
+    {
+      std::ifstream in( file_r.c_str() );
+      DataCollect dataCollect;
+      function<bool(const std::string &)> fnc( ref(dataCollect) );
+      iostr::forEachLine( in, fnc );
+      getZYpp()->addResolvables( dataCollect.installed(), true );
+      getZYpp()->addResolvables( dataCollect.available() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace debug
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // FakePool_h
diff --git a/devel/devel.ma/FilelistTransform.cc b/devel/devel.ma/FilelistTransform.cc
new file mode 100644 (file)
index 0000000..3a9ec5c
--- /dev/null
@@ -0,0 +1,780 @@
+#include "Tools.h"
+#include <boost/call_traits.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+
+#include "zypp/parser/xml/Reader.h"
+
+using namespace std;
+using namespace zypp;
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/DefaultIntegral.h"
+#include <zypp/base/Function.h>
+#include <zypp/base/Iterator.h>
+#include <zypp/Pathname.h>
+#include <zypp/ExplicitMap.h>
+#include <zypp/Depository.h>
+#include <zypp/Edition.h>
+#include <zypp/CheckSum.h>
+#include <zypp/Date.h>
+
+///////////////////////////////////////////////////////////////////
+
+template<class _Cl>
+  void ti( const _Cl & c )
+  {
+    SEC << __PRETTY_FUNCTION__ << endl;
+  }
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  namespace parser
+  {
+    namespace yum
+    {
+      ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+    }
+  }
+}
+///////////////////////////////////////////////////////////////////
+
+bool nopNode( xml::Reader & reader_r )
+{
+  return true;
+}
+
+bool accNode( xml::Reader & reader_r )
+{
+  int i;
+  xml::XmlString s;
+#define X(m) reader_r->m()
+      i=X(readState);
+      i=X(lineNumber);
+      i=X(columnNumber);
+      i=X(depth);
+      i=X(nodeType);
+      s=X(name);
+      s=X(prefix);
+      s=X(localName);
+      i=X(hasAttributes);
+      i=X(attributeCount);
+      i=X(hasValue);
+      s=X(value);
+#undef X
+      return true;
+}
+
+bool dumpNode( xml::Reader & reader_r )
+{
+  switch ( reader_r->nodeType() )
+    {
+    case XML_READER_TYPE_ATTRIBUTE:
+       DBG << *reader_r << endl;
+       break;
+    case XML_READER_TYPE_ELEMENT:
+       MIL << *reader_r << endl;
+       break;
+    default:
+       WAR << *reader_r << endl;
+       break;
+    }
+  return true;
+}
+
+bool dumpNode2( xml::Reader & reader_r )
+{
+  dumpNode( reader_r );
+  return reader_r.foreachNodeAttribute( dumpNode );
+}
+
+bool dumpEd( xml::Reader & reader_r )
+{
+  static int num = 5;
+  if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT
+       && reader_r->name() == "version" )
+    {
+      MIL << *reader_r << endl;
+      DBG << reader_r->getAttribute( "rel" ) << endl;
+      ERR << *reader_r << endl;
+      DBG << reader_r->getAttribute( "ver" ) << endl;
+      ERR << *reader_r << endl;
+      DBG << reader_r->getAttribute( "epoch" ) << endl;
+      ERR << *reader_r << endl;
+      WAR << Edition( reader_r->getAttribute( "ver" ).asString(),
+                      reader_r->getAttribute( "rel" ).asString(),
+                      reader_r->getAttribute( "epoch" ).asString() ) << endl;
+      --num;
+    }
+  return num;
+}
+
+
+///////////////////////////////////////////////////////////////////
+
+namespace parser
+{
+  namespace consume
+  {
+    struct Repomd
+    {
+      struct Data
+      {
+        Depository<std::string> _type;
+        Depository<CheckSum>    _checksum;
+        Depository<Date>        _timestamp;
+        Depository<CheckSum>    _openChecksum;
+      };
+
+      Depository<Data> _data;
+    };
+  }
+
+
+
+}
+
+namespace data
+{
+  struct Repomd
+  {
+    struct Data
+    {
+      std::string _type;
+      CheckSum    _checksum;
+      Date        _timestamp;
+      CheckSum    _openChecksum;
+    };
+
+    std::map<std::string, Data> _data;
+  };
+}
+
+///////////////////////////////////////////////////////////////////
+
+
+
+struct Element;
+std::ostream & operator<<( std::ostream & str, const Element & obj );
+
+struct Element : private base::NonCopyable
+{
+  Element( xml::Reader & reader_r )
+  : _reader( reader_r )
+  , _name( _reader->name().asString() )
+  , _depth( _reader->depth() )
+  {
+    MIL << *this << endl;
+    //return;
+    while( nextElement() )
+      {
+        Element el( _reader );
+      }
+  }
+
+  ~Element()
+  {
+    while( nextElement() )
+      { ; }
+    DBG << *this << endl;
+  }
+
+  bool atBegin() const
+  {
+    return ( _reader->depth() == _depth
+             && _reader->nodeType() == XML_READER_TYPE_ELEMENT
+             && _reader->name().c_str() == _name );
+  }
+
+  bool atEnd() const
+  {
+    return ( _reader->depth() == _depth
+             && ( _reader->nodeType() == XML_READER_TYPE_END_ELEMENT
+                  || ( _reader->nodeType() == XML_READER_TYPE_ELEMENT
+                       && _reader->isEmptyElement() ) )
+             && _reader->name().c_str() == _name );
+  }
+
+  bool nextElement()
+  {
+    while ( ! atEnd() )
+      {
+        if ( ! _reader.nextNode() )
+          return false;
+        if ( _reader->nodeType() == XML_READER_TYPE_ELEMENT )
+          return true;
+        WAR << *_reader << endl;
+      }
+    return false;
+  }
+
+
+  xml::Reader & _reader;
+  std::string   _name;
+  int           _depth;
+};
+
+std::ostream & operator<<( std::ostream & str, const Element & obj )
+{
+  str << ( obj.atBegin() ? 'B' : '_' )
+      << ( obj.atEnd() ? 'E' : '_' )
+      << obj._depth << ":" <<  std::string( obj._depth, ' ') << obj._name
+      << " {" << *obj._reader << '}';
+  return str;
+}
+
+bool dumpEl( xml::Reader & reader_r )
+{
+  Element el( reader_r );
+  return true;
+}
+
+void parse2( const InputStream & file_r )
+{
+  Measure x( file_r.name() );
+  try
+    {
+      MIL << file_r << endl;
+      xml::Reader r( file_r );
+      MIL << *r << endl;
+      Element el( r );
+      MIL << *r << endl;
+    }
+  catch ( const Exception & )
+    {
+    }
+}
+///////////////////////////////////////////////////////////////////
+class BasicParser
+{
+  public:
+    typedef function<void( xml::Reader & )>   Consumer;
+    typedef ExplicitMap<std::string,Consumer> ConsumerMap;
+
+    BasicParser( const InputStream & file_r )
+    : _reader( file_r )
+    , _cmap( nop )
+    {}
+
+  public:
+    xml::Reader & reader()
+    { return _reader; }
+
+    const xml::Reader & reader() const
+    { return _reader; }
+
+    ConsumerMap & cmap()
+    { return _cmap; }
+
+    const ConsumerMap & cmap() const
+    { return _cmap; }
+
+  public:
+
+    bool parse( xml::Reader & reader_r )
+    {
+      switch ( reader_r->nodeType() )
+        {
+        case XML_READER_TYPE_ELEMENT:
+        case XML_READER_TYPE_TEXT:
+        case XML_READER_TYPE_CDATA:
+        case XML_READER_TYPE_END_ELEMENT:
+          consume( reader_r );
+        default:
+          ;
+        }
+      return true;
+    }
+
+  public:
+    void consume( xml::Reader & reader_r, const std::string & key_r )
+    { _cmap[key_r]( reader_r ); }
+
+    void consume( xml::Reader & reader_r )
+    { consume( reader_r, reader_r->name().asString() ); }
+
+    void consume()
+    { consume( _reader ); }
+
+  public:
+    static void nop( xml::Reader & reader_r )
+    { ; }
+
+    static void log( xml::Reader & reader_r )
+    { DBG << "NOP " << *reader_r << endl; }
+
+
+  protected:
+    xml::Reader _reader;
+    ConsumerMap _cmap;
+};
+
+///////////////////////////////////////////////////////////////////
+
+struct RepomdParser : private BasicParser
+{
+  RepomdParser( const InputStream & file_r )
+  : BasicParser( file_r )
+  {
+    reader().foreachNode( ref(*this) );
+  }
+
+  bool operator()( xml::Reader & reader_r )
+  {
+    return parse( reader_r );
+  }
+
+  // READER goes here!
+};
+
+///////////////////////////////////////////////////////////////////
+struct Consume
+{
+  struct Entry
+  {
+    Pathname _location;
+    CheckSum _checksum;
+    //unused: Date     _timestamp;
+    //unused: CheckSum _openChecksum;
+  };
+
+  typedef void (Consume::*Consumer)( xml::Reader & reader_r );
+
+  Consume( const InputStream & file_r )
+  : _reader( file_r )
+  , _consume( &Consume::nop )
+  , _centry( NULL )
+  {
+    _consume.set( "data", &Consume::data );
+    _reader.foreachNode( ref(*this) );
+  }
+
+  bool operator()( xml::Reader & reader_r )
+  {
+    switch ( reader_r->nodeType() )
+      {
+      case XML_READER_TYPE_ELEMENT:
+        (this->*_consume[reader_r->name().asString()])( reader_r );
+        //data( reader_r );
+        break;
+      default:
+        WAR << *_reader << endl;
+        break;
+      }
+    return true;
+  }
+
+  void nop( xml::Reader & reader_r )
+  { ; }
+
+  void log( xml::Reader & reader_r )
+  { DBG << "NOP " << *_reader << endl; }
+
+  void data( xml::Reader & reader_r )
+  {
+    MIL << *_reader << endl;
+    _result[reader_r->name().asString()] = Entry();
+  }
+
+
+
+  xml::Reader _reader;
+  ExplicitMap<std::string,Consumer> _consume;
+  std::map<std::string,Entry> _result;
+  Entry * _centry;
+};
+
+std::ostream & operator<<( std::ostream & str, const Consume & obj )
+{
+  return str;
+}
+
+std::ostream & operator<<( std::ostream & str, const Consume::Entry & obj )
+{
+  return str << "Entry";
+}
+
+void parse( const InputStream & file_r )
+{
+  Measure x( file_r.name() );
+  try
+    {
+      MIL << file_r << endl;
+      RepomdParser a( file_r );
+      //WAR << a._result << endl;
+    }
+  catch ( const Exception & )
+    {
+    }
+}
+
+struct Test
+{
+  struct Mix
+  {
+    Mix()
+    : a( 0 )
+    {}
+
+    void seta( int v )
+    { a = v; }
+
+    void setb( const string & v )
+    { b = v; }
+
+    int    a;
+    string b;
+   };
+
+  Test()
+  : a( 0 )
+  {}
+
+  int    a;
+  string b;
+  Mix    c;
+};
+
+std::ostream & operator<<( std::ostream & str, const Test & obj )
+{
+  return str << "Test(" << obj.a << '|' << obj.b
+             << '|' << obj.c.a << '|' << obj.c.b << ')';
+}
+
+struct Transform
+{
+  Transform()
+  : outfile( "susedu.xml", std::ios_base::out )
+  {}
+
+  static const bool indented = !false;
+  static const bool shorttags = !true;
+  std::fstream outfile;
+
+  bool operator()( xml::Reader & reader_r )
+  {
+    switch ( reader_r->nodeType() )
+      {
+      case XML_READER_TYPE_ELEMENT:
+        process( reader_r, true );
+        break;
+      case XML_READER_TYPE_END_ELEMENT:
+        process( reader_r, false );
+        break;
+      default:
+        //WAR << *reader_r << endl;
+        break;
+      }
+    return true;
+  }
+
+  struct File
+  {
+    std::string name;
+    std::string type;
+
+    bool operator<( const File & rhs ) const
+    { return( name < rhs.name ); }
+  };
+
+  struct Package
+  {
+    std::string    pkgid;
+    std::string    name;
+    std::string    epoch;
+    std::string    ver;
+    std::string    rel;
+    std::string    arch;
+    std::set<File> files;
+  };
+
+  shared_ptr<Package> pkg;
+
+  void process( xml::Reader & reader_r, bool open_r )
+  {
+    if ( reader_r->name() == "file" )
+      {
+        if ( open_r )
+          addFile( reader_r );
+      }
+    else if ( reader_r->name() == "version" )
+      {
+        if ( open_r )
+          addVersion( reader_r );
+      }
+    else if ( reader_r->name() == "package" )
+      {
+        if ( open_r )
+          startPackage( reader_r );
+        else
+          endPackage();
+      }
+    else if ( reader_r->name() == "filelists" )
+      {
+        DBG << *reader_r << endl;
+        if ( open_r )
+          {
+            DBG << outfile << endl;
+            outfile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+            outfile << "<susedu>" << endl;
+          }
+        else
+          {
+            outfile << "</susedu>" << endl;
+            outfile.close();
+          }
+      }
+    else
+      {
+        throw;
+      }
+  }
+
+  void startPackage( xml::Reader & reader_r )
+  {
+    endPackage();
+    pkg.reset( new Package );
+    pkg->pkgid = reader_r->getAttribute( "pkgid" ).asString();
+    pkg->name = reader_r->getAttribute( "name" ).asString();
+    pkg->arch = reader_r->getAttribute( "arch" ).asString();
+  }
+
+  void addVersion( xml::Reader & reader_r )
+  {
+    pkg->epoch = reader_r->getAttribute( "epoch" ).asString();
+    pkg->ver = reader_r->getAttribute( "ver" ).asString();
+    pkg->rel = reader_r->getAttribute( "rel" ).asString();
+  }
+
+  void addFile( xml::Reader & reader_r )
+  {
+    File f;
+    f.type = reader_r->getAttribute( "type" ).asString();
+    for( reader_r.nextNode();
+         reader_r->nodeType() != XML_READER_TYPE_END_ELEMENT;
+         reader_r.nextNode() )
+      {
+        if ( reader_r->nodeType() == XML_READER_TYPE_TEXT )
+          {
+            f.name = reader_r->value().asString();
+          }
+      }
+    pkg->files.insert( f );
+  }
+
+  void endPackage()
+  {
+    if ( pkg )
+      {
+        writePackage( outfile );
+        pkg.reset();
+      }
+  }
+
+  static std::ostream & putAttr( std::ostream & stream_r,
+                                 const std::string & tag_r,
+                                 const std::string & value_r )
+  {
+    if ( value_r.empty() || tag_r.empty() )
+      return stream_r;
+    return stream_r
+           << str::form( " %s=\"%s\"", tag_r.c_str(), value_r.c_str() );
+  }
+
+  void writePackage( std::ostream & stream_r )
+  {
+    stream_r << " <package";
+    putAttr( stream_r, "pkgid", pkg->pkgid );
+    putAttr( stream_r, "name", pkg->name );
+    putAttr( stream_r, "arch", pkg->arch );
+    stream_r << ">\n";
+
+    stream_r << "  <version";
+    putAttr( stream_r, "epoch", pkg->epoch );
+    putAttr( stream_r, "ver", pkg->ver );
+    putAttr( stream_r, "rel", pkg->rel );
+    stream_r << "/>\n";
+
+    writePackageFiles2( stream_r );
+
+    stream_r << " </package>\n";
+  }
+
+  void writePackageFiles( std::ostream & stream_r )
+  {
+    for ( std::set<File>::const_iterator it = pkg->files.begin();
+          it != pkg->files.end(); ++it )
+      {
+        stream_r << "   <file";
+        putAttr( stream_r, "type", it->type );
+        stream_r << ">" << it->name << "</file>\n";
+      }
+  }
+
+  struct Fnode
+  {
+    Fnode( const std::string & name_r )
+    : name( name_r )
+    , entry( NULL )
+    {}
+
+    std::string             name;
+    mutable const File *    entry;
+    mutable std::set<Fnode> children;
+
+    const Fnode * add( const std::string & sub_r ) const
+    {
+      std::set<Fnode>::iterator i = children.find( sub_r );
+      if ( i != children.end() )
+        return &(*i);
+      return &(*(children.insert( Fnode( sub_r ) ).first));
+    }
+
+    void dump( std::ostream & stream_r, const std::string & pname, unsigned level ) const
+    {
+      std::string tname;
+      if ( pname.empty() )
+        {
+          tname = name;
+        }
+      else if ( pname == "/" )
+        {
+          tname = pname+name;
+        }
+      else
+        {
+          tname = pname+"/"+name;
+        }
+
+      if ( children.size() == 1 )
+        {
+          children.begin()->dump( stream_r, tname, (indented?level:0) );
+          return;
+        }
+
+      std::string tag;
+      stream_r << std::string( level, ' ' );
+
+      if ( entry )
+        {
+          tag = (shorttags ? "f" : "file");
+          stream_r << "<" << tag;
+          putAttr( stream_r, (shorttags ? "t" : "type"), entry->type );
+          putAttr( stream_r, (shorttags ? "n" : "name"), tname );
+        }
+      else
+        {
+          tag = (shorttags ? "b" : "base");
+          stream_r << "<" << tag;
+          putAttr( stream_r, (shorttags ? "n" : "name"), tname );
+        }
+
+      if ( children.empty() )
+        {
+          stream_r << "/>" << (indented?"\n":"");
+        }
+      else
+        {
+          stream_r << ">" << (indented?"\n":"");
+          for ( std::set<Fnode>::const_iterator it = children.begin();
+                it != children.end(); ++it )
+            {
+              it->dump( stream_r, "", (indented?level+1:0) );
+            }
+          stream_r << std::string( level, ' ' ) << "</" << tag << ">" << (indented?"\n":"");
+        }
+    }
+
+    bool operator<( const Fnode & rhs ) const
+    { return( name < rhs.name ); }
+  };
+
+  void writePackageFiles2( std::ostream & stream_r )
+  {
+    Fnode root( "" );
+    for ( std::set<File>::const_iterator it = pkg->files.begin();
+          it != pkg->files.end(); ++it )
+      {
+        std::list<std::string> words;
+        str::split( it->name, std::back_inserter(words), "/" );
+
+        const Fnode * c = &root;
+        for ( std::list<std::string>::const_iterator w = words.begin();
+              w != words.end(); ++w )
+          {
+            c = c->add( *w );
+          }
+        c->entry = &(*it);
+      }
+    root.dump( stream_r, "/", (indented?3:0) );
+  }
+
+};
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  {
+    Measure x;
+    Pathname repodata( "/Local/PATCHES/repodata" );
+    //repodata = "/Local/FACTORY/repodata";
+    xml::Reader reader( repodata/"filelists.xml" );
+    Transform t;
+    reader.foreachNode( ref(t) );
+  }
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+  int s;
+
+  Pathname repodata( "/Local/PATCHES/repodata" );
+  //repodata = "/Local/FACTORY/repodata";
+  InputStream x ( "/Local/PATCHES/repodata" );
+  parse2( repodata/"repomd.xml" );
+  //parse2( repodata/"primary.xml" );
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+  {
+    Measure x;
+    for ( int i = 1; i; --i )
+      {
+        parse( repodata/"repomd.xml" );
+        parse( repodata/"primary.xml" );
+        parse( repodata/"filelists.xml" );
+        parse( repodata/"other.xml" );
+      }
+  }
+  ERR << "done..." << endl;
+  cin >> s;
+  return 0;
+  {
+    Measure x;
+    for ( int i = 20; i; --i )
+      {
+        parse( (repodata/"repomd.xml").asString() );
+        parse( repodata/"primary.xml" );
+        parse( repodata/"filelists.xml" );
+        parse( repodata/"other.xml" );
+      }
+  }
+  ERR << "done..." << endl;
+  cin >> s;
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
diff --git a/devel/devel.ma/Iorder.cc b/devel/devel.ma/Iorder.cc
new file mode 100644 (file)
index 0000000..f3eb14f
--- /dev/null
@@ -0,0 +1,475 @@
+#include "Tools.h"
+#include <zypp/ResObjects.h>
+#include <zypp/ProgressData.h>
+#include <zypp/sat/WhatObsoletes.h>
+
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+void Dbg( ui::Selectable::Ptr s )
+{
+  SEC << dump(s) << endl;
+  if ( s->installedObj() )
+  {
+    PoolItem pi( s->installedObj() );
+    DBG << pi.satSolvable().obsoletes() << endl;
+    sat::WhatObsoletes obs( pi );
+    INT << "WhatObsoletes " <<  pi << " " << obs << endl;
+  }
+  if ( s->candidateObj() )
+  {
+    PoolItem pi( s->candidateObj() );
+    DBG << pi.satSolvable().obsoletes() << endl;
+    sat::WhatObsoletes obs( pi );
+    INT << "WhatObsoletes " <<  pi << " " << obs << endl;
+  }
+
+}
+
+///////////////////////////////////////////////////////////////////
+
+static std::string appname( "ToolIorder" );
+
+void message( const std::string & msg_r )
+{
+  cerr << "*** " << msg_r << endl;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl;
+    message( msg_r );
+    cerr << endl;
+  }
+  cerr << "Usage: " << appname << "[OPTIONS] TESTCASE" << endl;
+  cerr << "  Load testcase and analyze install order." << endl;
+  return exit_r;
+}
+
+///////////////////////////////////////////////////////////////////
+
+
+bool progressReceiver( const ProgressData & v )
+{
+  DBG << "...->" << v << endl;
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+#define LCStack   "IOrder::Stack"
+#define LCCache   "IOrder::Cache"
+#define LCVerbose "IOrder::Verbose"
+
+struct RunnableCache
+{
+  typedef std::tr1::unordered_map<sat::Solvable,TriBool> CacheType;
+  typedef std::vector<sat::Solvable>                     AnalyzeStack;
+
+  RunnableCache()
+  : _ltag( "[0000]" )
+  {}
+
+  /**
+   * Test whether there is a runnable provider for each requirement.
+   */
+  bool isRunnable( const PoolItem & pi ) const
+  { return isRunnable( pi.satSolvable() ); }
+
+  bool isRunnable( sat::Solvable solv_r ) const
+  {
+    SEC << "Runnable?       " << solv_r << endl;
+    if ( _isRunnable( solv_r ) )
+    {
+      MIL << "Runnable:       " << solv_r << endl;
+      return true;
+    }
+    ERR << "NotRunnable:    " << solv_r << endl;
+    return false;
+  }
+
+  /**
+   * Test whether there is a runnable provider for each pre-requirement.
+   */
+  bool isInstallable( const PoolItem & pi ) const
+  { return isInstallable( pi.satSolvable() ); }
+
+  bool isInstallable( sat::Solvable solv_r ) const
+  {
+    SEC << "Installable?    " << solv_r << endl;
+    tribool & cent( get( solv_r ) ); // if (cached) runnable then also installable.
+    if ( cent || checkCaps( solv_r.prerequires() ) )
+    {
+      MIL << "Installable:    " << solv_r << endl;
+      return true;
+    }
+    ERR << "NotInstallable: " << solv_r << endl;
+    return false;
+  }
+
+  /** Clear the cache. */
+  void clear() const
+  {
+    _cache.clear();
+    _stack.clear();
+    _ltag = "[0000]";
+    _INT(LCCache) << "Cache cleared!" << endl;
+  }
+
+  private:
+    /** Internal version without loging for recursive calls. */
+    bool _isRunnable( const PoolItem & pi ) const
+    { return _isRunnable( pi.satSolvable() ); }
+
+    bool _isRunnable( sat::Solvable solv_r ) const
+    {
+      tribool & cent( get( solv_r ) );
+      if ( indeterminate( cent ) )
+        cent = analyze( solv_r );
+      return cent;
+    }
+
+    /**
+     * Determine whether this solvable is runnable.
+     */
+    bool analyze( sat::Solvable solv_r ) const
+    {
+      if ( ! push( solv_r ) )
+      {
+        if ( _stack.back() != solv_r )
+          _SEC(LCStack) << _ltag << "** CYCLE: " << solv_r << " " << _stack << endl;
+        // else it's a self requirement
+        return true; // assume runnable?
+      }
+      _INT(LCStack) << _ltag << "->" << solv_r << " " << _stack << endl;
+      bool ret = checkCaps( solv_r.requires() );
+      _INT(LCStack) << _ltag << "<-" << solv_r << " " << _stack << endl;
+      if ( ! pop( solv_r ) )
+      {
+        _SEC(LCStack) << "** Stack corrupted! Expect " << solv_r << " " << _stack << endl;
+      }
+      return ret;
+    }
+
+    /**
+     * For each capability find a runnable provider.
+     */
+    bool checkCaps( Capabilities caps_r ) const
+    {
+      for_( it, caps_r.begin(), caps_r.end() )
+      {
+        if ( ! findRunnableProvider( *it ) )
+          return false;
+      }
+      return true;
+    }
+
+    /**
+     * Find a runnable provider of a capability on system.
+     *
+     * A runnable package is already installed and all of
+     * its requirements are met by runnable packages.
+     */
+    bool findRunnableProvider( Capability cap_r ) const
+    {
+      _MIL(LCVerbose) << _ltag << "  " << cap_r << endl;
+      sat::WhatProvides prv( cap_r );
+      for_( pit, prv.begin(), prv.end() )
+      {
+        if ( ! *pit )
+        {
+          _DBG(LCVerbose) << _ltag << "     by system" << endl;
+          return true; // noSolvable provides: i.e. system provides
+        }
+
+        PoolItem pi( *pit );
+        if ( pi.status().onSystem() )
+        {
+          if ( _isRunnable( pi ) )
+          {
+            _DBG(LCVerbose) << _ltag << "    " << pi << endl;
+            return true;
+          }
+          else
+          {
+            _WAR(LCVerbose) << _ltag << "    " << pi << endl;
+          }
+        }
+      }
+      ERR << _ltag << "    NO runnable provider for " << cap_r << endl;
+      return false;
+    }
+
+  private:
+    /** Push a new solvable to the AnalyzeStack, or return false is already on stack. */
+    bool push( sat::Solvable solv_r ) const
+    {
+      if ( find( _stack.begin(), _stack.end(), solv_r ) == _stack.end() )
+      {
+        _stack.push_back( solv_r );
+        _ltag = str::form( "[%04lu]", _stack.size() );
+        return true;
+      }
+      // cycle?
+      return false;
+    }
+
+    /** Pop solvable from AnalyzeStack (expecting it to be \c solv_r). */
+    bool pop( sat::Solvable solv_r ) const
+    {
+      if ( _stack.back() == solv_r )
+      {
+        _stack.pop_back();
+        _ltag = str::form( "[%04lu]", _stack.size() );
+        return true;
+      }
+      // stack corrupted?
+      return false;
+    }
+
+    /** Return cache entry, initializing new entries with \ref indeterminate.*/
+    tribool & get( sat::Solvable solv_r ) const
+    {
+      CacheType::iterator it( _cache.find( solv_r ) );
+      if ( it == _cache.end() )
+        return (_cache[solv_r] = indeterminate);
+      return _cache[solv_r];
+    }
+
+    mutable CacheType    _cache;
+    mutable AnalyzeStack _stack;
+    mutable std::string  _ltag;
+};
+
+RunnableCache rcache;
+
+//==================================================
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  return true;
+}
+
+bool solve()
+{
+  static unsigned run = 0;
+  USR << "Solve " << run++ << endl;
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+
+  return true;
+}
+
+void display( const pool::GetResolvablesToInsDel & collect, std::set<IdString> interested )
+{
+  if ( ! interested.empty() )
+  {
+    USR << "======================================================================" << endl;
+    USR << "=== INTERESTED" << endl;
+    USR << "======================================================================" << endl;
+    for_( it, interested.begin(), interested.end() )
+    {
+      MIL << dump(ui::Selectable::get( *it )) << endl;
+    }
+  }
+
+  USR << "======================================================================" << endl;
+  USR << "=== DELETE" << endl;
+  USR << "======================================================================" << endl;
+  if ( 1 )
+  {
+    ProgressData tics( collect._toDelete.size() );
+    tics.name( "DELETE" );
+    tics.sendTo( &progressReceiver );
+    tics.toMin();
+
+    for_( it, collect._toDelete.begin(), collect._toDelete.end() )
+    {
+      tics.incr();
+
+      it->status().setTransact( true, ResStatus::USER );
+//       vdumpPoolStats( SEC << "Transacting:"<< endl,
+//                       make_filter_begin<resfilter::ByTransact>(pool),
+//                       make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+      if ( !interested.empty() && interested.find( it->satSolvable().ident() ) == interested.end() )
+      {
+        MIL << "..." << *it << endl;
+        continue;
+      }
+
+      rcache.clear();
+      if ( ! rcache.isInstallable( *it ) )
+      {
+        USR << "FAILED DEL " << *it << endl;
+      }
+    }
+  }
+
+  USR << "======================================================================" << endl;
+  USR << "=== INSTALL" << endl;
+  USR << "======================================================================" << endl;
+  if ( 1 )
+  {
+    ProgressData tics( collect._toInstall.size() );
+    tics.name( "INSTALL" );
+    tics.sendTo( progressReceiver );
+    tics.toMin();
+
+
+    for_( it, collect._toInstall.begin(), collect._toInstall.end() )
+    {
+      tics.incr();
+
+      ui::Selectable::Ptr p( ui::Selectable::get( *it ) );
+      p->setCandidate( *it );
+      p->setToInstall(); // also deletes the installed one
+//       vdumpPoolStats( SEC << "Transacting:"<< endl,
+//                       make_filter_begin<resfilter::ByTransact>(pool),
+//                       make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+
+      if ( !interested.empty() && interested.find( p->ident() ) == interested.end() )
+      {
+        MIL << "..." << *it << endl;
+        continue;
+      }
+
+      rcache.clear();
+
+      for_( it, p->installedBegin(), p->installedEnd() )
+      {
+        if ( ! rcache.isInstallable( *it ) )
+        {
+          USR << "FAILED OLD " << *it << endl;
+        }
+      }
+      sat::WhatObsoletes obs( *it );
+      for_( it, obs.begin(), obs.end() )
+      {
+        if ( ! rcache.isInstallable( *it ) )
+        {
+          USR << "FAILED OBS " << *it << endl;
+        }
+      }
+
+
+      if ( ! rcache.isInstallable( *it ) )
+      {
+        USR << "FAILED INS " << *it << endl;
+      }
+    }
+  }
+}
+
+void display( const pool::GetResolvablesToInsDel & collect, IdString ident_r )
+{
+  std::set<IdString> interested;
+  interested.insert( ident_r );
+  display( collect, interested );
+}
+
+
+void display( const pool::GetResolvablesToInsDel & collect )
+{
+  std::set<IdString> interested;
+  display( collect, interested );
+}
+
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc;
+  ++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  Pathname mtest( "/suse/ma/BUGS/439802/bug439802/YaST2/solverTestcase" );
+  Arch     march( Arch_ppc64 );
+
+  while ( argc )
+  {
+    --argc;
+    ++argv;
+  }
+
+  if ( mtest.empty() )
+  {
+    return usage( "Missing Testcase", 102 );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  TestSetup test( march );
+  ResPool   pool( test.pool() );
+  sat::Pool satpool( test.satpool() );
+
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    test.loadTarget();
+    test.loadTestcaseRepos( mtest ); // <<< repos
+  }
+  test.poolProxy().saveState();
+
+  { // <<< transaction
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    getPi<Product>( "SUSE_SLES", Edition("11-0"), Arch_ppc64 ).status().setTransact( true, ResStatus::USER );
+    vdumpPoolStats( USR << "Transacting:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(pool),
+                  make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+    upgrade();
+  }
+  vdumpPoolStats( USR << "Transacting:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(pool),
+                  make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+  pool::GetResolvablesToInsDel collect( pool, pool::GetResolvablesToInsDel::ORDER_BY_MEDIANR );
+
+
+
+  test.poolProxy().restoreState();
+  {
+    base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "iorder.log" ) );
+    std::set<IdString> interested;
+    //interested.insert( IdString("fillup") );
+    display( collect, interested );
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::TmpLineWriter shutUp;
+  return 0;
+}
diff --git a/devel/devel.ma/MaTest.cc b/devel/devel.ma/MaTest.cc
new file mode 100644 (file)
index 0000000..6791aac
--- /dev/null
@@ -0,0 +1,570 @@
+#include "Tools.h"
+
+#include "zypp/PoolQueryResult.h"
+
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/Exception.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Debug.h>
+#include <zypp/base/Functional.h>
+#include <zypp/base/IOStream.h>
+#include <zypp/base/InputStream.h>
+#include <zypp/base/ProvideNumericId.h>
+#include <zypp/base/Flags.h>
+#include <zypp/AutoDispose.h>
+
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/ResObjects.h"
+#include "zypp/Digest.h"
+#include "zypp/PackageKeyword.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+#include "zypp/RepoManager.h"
+#include "zypp/Repository.h"
+#include "zypp/RepoInfo.h"
+
+#include "zypp/repo/PackageProvider.h"
+
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/LocaleSupport.h"
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/SolvableSet.h"
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/ServiceInfo.h"
+
+#include <boost/mpl/int.hpp>
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::ui;
+
+///////////////////////////////////////////////////////////////////
+
+static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+bool queryInstalledEditionHelper( const std::string & name_r,
+                                  const Edition &     ed_r,
+                                  const Arch &        arch_r )
+{
+  if ( ed_r == Edition::noedition )
+    return true;
+  if ( name_r == "kernel-default" && ed_r == Edition("2.6.22.5-10") )
+    return true;
+  if ( name_r == "update-test-affects-package-manager" && ed_r == Edition("1.1-6") )
+    return true;
+
+  return false;
+}
+
+
+ManagedFile repoProvidePackage( const PoolItem & pi )
+{
+  ResPool _pool( getZYpp()->pool() );
+  repo::RepoMediaAccess _access;
+
+  // Redirect PackageProvider queries for installed editions
+  // (in case of patch/delta rpm processing) to rpmDb.
+  repo::PackageProviderPolicy packageProviderPolicy;
+  packageProviderPolicy.queryInstalledCB( queryInstalledEditionHelper );
+
+  Package::constPtr p = asKind<Package>(pi.resolvable());
+
+  // Build a repository list for repos
+  // contributing to the pool
+  repo::DeltaCandidates deltas( repo::makeDeltaCandidates( _pool.knownRepositoriesBegin(),
+                                                           _pool.knownRepositoriesEnd() ) );
+  repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
+  return pkgProvider.providePackage();
+}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+namespace zypp
+{
+  template <class _LIterator, class _RIterator, class _Function>
+      inline int invokeOnEach( _LIterator lbegin_r, _LIterator lend_r,
+                               _RIterator rbegin_r, _RIterator rend_r,
+                               _Function fnc_r )
+      {
+        int cnt = 0;
+        for ( _LIterator lit = lbegin_r; lit != lend_r; ++lit )
+        {
+          for ( _RIterator rit = rbegin_r; rit != rend_r; ++rit )
+          {
+            ++cnt;
+            if ( ! fnc_r( *lit, *rit ) )
+              return -cnt;
+          }
+        }
+        return cnt;
+      }
+}
+
+
+void dbgDu( Selectable::Ptr sel )
+{
+  if ( sel->installedObj() )
+  {
+    DBG << "i: " << sel->installedObj() << endl
+        << sel->installedObj()->diskusage() << endl;
+  }
+  if ( sel->candidateObj() )
+  {
+    DBG << "c: " << sel->candidateObj() << endl
+        << sel->candidateObj()->diskusage() << endl;
+  }
+  INT << sel << endl
+      << getZYpp()->diskUsage() << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+std::ostream & testDump( std::ostream & str, const PoolItem & pi )
+{
+  str << pi << endl;
+  Package::constPtr p( asKind<Package>(pi) );
+  if ( p )
+  {
+#define OUTS(V) str << str::form("%-25s: ",#V) << p->V() << endl
+    Locale l( "de" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    l = Locale( "fr" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    l = Locale( "dsdf" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    OUTS( summary );
+    OUTS( installSize );
+    OUTS( downloadSize );
+    OUTS( sourcePkgName );
+    OUTS( sourcePkgEdition );
+    OUTS( checksum );
+    OUTS( location );
+#undef OUTS
+
+
+  }
+  return str;
+}
+
+struct Xprint
+{
+  bool operator()( const PoolItem & obj_r )
+  {
+    //MIL << obj_r << endl;
+    //DBG << " -> " << obj_r->satSolvable() << endl;
+
+    return true;
+  }
+
+  bool operator()( const sat::Solvable & obj_r )
+  {
+    //dumpOn( MIL, obj_r ) << endl;
+    return true;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+struct SetTransactValue
+{
+  SetTransactValue( ResStatus::TransactValue newVal_r, ResStatus::TransactByValue causer_r )
+  : _newVal( newVal_r )
+  , _causer( causer_r )
+  {}
+
+  ResStatus::TransactValue   _newVal;
+  ResStatus::TransactByValue _causer;
+
+  bool operator()( const PoolItem & pi ) const
+  {
+    bool ret = pi.status().setTransactValue( _newVal, _causer );
+    if ( ! ret )
+      ERR << _newVal <<  _causer << " " << pi << endl;
+    return ret;
+  }
+};
+
+struct StatusReset : public SetTransactValue
+{
+  StatusReset()
+  : SetTransactValue( ResStatus::KEEP_STATE, ResStatus::USER )
+  {}
+};
+
+struct StatusInstall : public SetTransactValue
+{
+  StatusInstall()
+  : SetTransactValue( ResStatus::TRANSACT, ResStatus::USER )
+  {}
+};
+
+///////////////////////////////////////////////////////////////////
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool install()
+{
+  ZYppCommitPolicy pol;
+  pol.dryRun(true);
+  pol.rpmInstFlags( pol.rpmInstFlags().setFlag( target::rpm::RPMINST_JUSTDB ) );
+  SEC << getZYpp()->commit( pol ) << endl;
+  return true;
+}
+
+void testcase()
+{
+  getZYpp()->resolver()->createSolverTestcase( "./solverTestcase" );
+}
+
+///////////////////////////////////////////////////////////////////
+
+struct DigestReceive : public callback::ReceiveReport<DigestReport>
+{
+  DigestReceive()
+  {
+    connect();
+  }
+
+  virtual bool askUserToAcceptNoDigest( const zypp::Pathname &file )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAccepUnknownDigest( const Pathname &file, const std::string &name )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found )
+  {
+    USR << "fle " << PathInfo(file) << endl;
+    USR << "req " << requested << endl;
+    USR << "fnd " << found << endl;
+    return false;
+  }
+};
+
+struct KeyRingSignalsReceive : public callback::ReceiveReport<KeyRingSignals>
+{
+  KeyRingSignalsReceive()
+  {
+    connect();
+  }
+  virtual void trustedKeyAdded( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+  virtual void trustedKeyRemoved( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+struct MediaChangeReceive : public callback::ReceiveReport<media::MediaChangeReport>
+{
+  virtual Action requestMedia( Url & source
+                               , unsigned mediumNr
+                               , const std::string & label
+                               , Error error
+                               , const std::string & description
+                               , const std::vector<std::string> & devices
+                               , unsigned int & dev_current )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << source << endl
+    << "  " << mediumNr << endl
+    << "  " << label << endl
+    << "  " << error << endl
+    << "  " << description << endl
+    << "  " << devices << endl
+    << "  " << dev_current << endl;
+    return IGNORE;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+namespace container
+{
+  template<class _Tp>
+    bool isIn( const std::set<_Tp> & cont, const typename std::set<_Tp>::value_type & val )
+    { return cont.find( val ) != cont.end(); }
+}
+///////////////////////////////////////////////////////////////////
+
+void itCmp( const sat::Pool::SolvableIterator & l, const sat::Pool::SolvableIterator & r )
+{
+  SEC << *l << " - " << *r << endl;
+  INT << "== " << (l==r) << endl;
+  INT << "!= " << (l!=r) << endl;
+}
+
+bool isTrue()  { return true; }
+bool isFalse() { return false; }
+
+void dumpIdStr()
+{
+  for ( int i = -3; i < 30; ++i )
+  {
+    DBG << i << '\t' << IdString( i ) << endl;
+  }
+}
+
+void ttt( const char * lhs, const char * rhs )
+{
+  DBG << lhs << " <=> " << rhs << " --> " << ::strcmp( lhs, rhs ) << endl;
+}
+
+namespace zypp
+{
+namespace filter
+{
+  template <class _MemFun, class _Value>
+  class HasValue
+  {
+    public:
+      HasValue( _MemFun fun_r, _Value val_r )
+      : _fun( fun_r ), _val( val_r )
+      {}
+      template <class _Tp>
+      bool operator()( const _Tp & obj_r ) const
+      { return( _fun && (obj_r.*_fun)() == _val ); }
+    private:
+      _MemFun _fun;
+      _Value  _val;
+  };
+
+  template <class _MemFun, class _Value>
+  HasValue<_MemFun, _Value> byValue( _MemFun fun_r, _Value val_r )
+  { return HasValue<_MemFun, _Value>( fun_r, val_r ); }
+}
+
+}
+
+template <class L>
+struct _TestO { _TestO( const L & lhs ) : _lhs( lhs ) {} const L & _lhs; };
+
+template <class L>
+std::ostream & operator<<( std::ostream & str, const _TestO<L> & obj )
+{ const L & lhs( obj._lhs); return str << (lhs?'_':'*') << (lhs.empty()?'e':'_') << "'" << lhs << "'"; }
+
+template <class L>
+_TestO<L> testO( const L & lhs )
+{ return _TestO<L>( lhs ); }
+
+template <class L, class R>
+void testCMP( const L & lhs, const R & rhs )
+{
+  MIL << "LHS " << testO(lhs) << endl;
+  MIL << "RHS " << rhs << endl;
+
+#define OUTS(S) DBG << #S << ": " << (S) << endl
+  OUTS( lhs.compare(rhs) );
+  OUTS( lhs != rhs );
+  OUTS( lhs <  rhs );
+  OUTS( lhs <= rhs );
+  OUTS( lhs == rhs );
+  OUTS( lhs >= rhs );
+  OUTS( lhs >  rhs );
+#undef OUTS
+}
+
+inline bool useRepo( RepoInfo & repo )
+{
+  return repo.alias()  == "matest";
+  return repo.enabled();
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+try {
+  --argc;
+  ++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ZConfig::instance();
+
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+
+  if ( 0 )
+  {
+    Measure x( "INIT TARGET" );
+    {
+      {
+        zypp::base::LogControl::TmpLineWriter shutUp;
+        getZYpp()->initializeTarget( sysRoot );
+      }
+      getZYpp()->target()->load();
+      USR << getZYpp()->target()->targetDistribution() << endl;
+      USR << getZYpp()->target()->targetDistributionRelease() << endl;
+    }
+  }
+
+  if ( 1 )
+  {
+    RepoManager repoManager( makeRepoManager( sysRoot ) );
+    RepoInfoList repos = repoManager.knownRepositories();
+
+    // launch repos
+    for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+    {
+      RepoInfo & nrepo( *it );
+      SEC << nrepo << endl;
+
+      if ( ! useRepo( nrepo ) )
+        continue;
+
+      if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
+      {
+        if ( repoManager.isCached( nrepo ) )
+        {
+          SEC << "cleanCache" << endl;
+          repoManager.cleanCache( nrepo );
+        }
+        SEC << "refreshMetadata" << endl;
+        //repoManager.refreshMetadata( nrepo );
+        SEC << "buildCache" << endl;
+        repoManager.buildCache( nrepo );
+      }
+    }
+
+    // create from cache:
+    {
+      Measure x( "CREATE FROM CACHE" );
+      for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+      {
+        RepoInfo & nrepo( *it );
+        if ( ! useRepo( nrepo ) )
+          continue;
+
+        Measure x( "CREATE FROM CACHE "+nrepo.alias() );
+        try
+        {
+          repoManager.loadFromCache( nrepo );
+        }
+        catch ( const Exception & exp )
+        {
+          MIL << "Try to rebuild cache..." << endl;
+          SEC << "cleanCache" << endl;
+          repoManager.cleanCache( nrepo );
+          SEC << "buildCache" << endl;
+          repoManager.buildCache( nrepo );
+          SEC << "Create from cache" << endl;
+          repoManager.loadFromCache( nrepo );
+        }
+
+        USR << "pool: " << pool << endl;
+      }
+    }
+  }
+
+  dumpRange( USR, satpool.reposBegin(), satpool.reposEnd() );
+  USR << "pool: " << pool << endl;
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( 0 )
+  {
+    Measure x( "Upgrade" );
+    getZYpp()->resolver()->doUpgrade();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+//  UNTh_(2)product:openSUSE-11.1.x86_64(openSUSE-dvd-11.1)
+//  U_Th_(583)yast2-ntp-client-2.17.1-1.26.noarch(openSUSE-dvd-11.1)
+//  U_Th_(1652)kernel-default-2.6.27-7.2.x86_64(openSUSE-dvd-11.1)
+//  U_Th_(2490)ntp-4.2.4p5-1.6.x86_64(openSUSE-dvd-11.1)
+//  UNTh_(2545)openSUSE-release-11.1-1.4.x86_64(openSUSE-dvd-11.1)
+//  USTh_(3462)pattern:base-11.1-46.1.x86_64(openSUSE-dvd-11.1)
+//  USTh_(3672)pattern:x11-11.1-46.1.x86_64(openSUSE-dvd-11.1)
+//  USTu_(3680)pattern:xfce-11.1-46.1.x86_64(openSUSE-dvd-11.1)
+
+
+  getZYpp()->resolver()->addRequire( Capability("product:openSUSE") );
+  getZYpp()->resolver()->addRequire( Capability("yast2-ntp-client") );
+  getZYpp()->resolver()->addRequire( Capability("kernel-default") );
+  getZYpp()->resolver()->addRequire( Capability("ntp") );
+  getZYpp()->resolver()->addRequire( Capability("openSUSE-release") );
+  getZYpp()->resolver()->addRequire( Capability("pattern:base") );
+  getZYpp()->resolver()->addRequire( Capability("pattern:x11") );
+  getZYpp()->resolver()->addRequire( Capability("pattern:xfce") );
+
+  solve();
+
+  vdumpPoolStats( USR << "Transacting:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(pool),
+                  make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+
+  ByteCount sze;
+  ByteCount dusze;
+  DiskUsageCounter ducounter( DiskUsageCounter::justRootPartition() );
+  for_( it, make_filter_begin<resfilter::ByTransact>(pool), make_filter_end<resfilter::ByTransact>(pool) )
+  {
+    USR << *it << endl;
+    ByteCount csze( ducounter.disk_usage( *it ).begin()->commitDiff() );
+    sze += (*it)->installSize();
+    dusze += csze;
+    DBG <<(*it)->installSize() << " vs. " << csze << " | " << ByteCount( dusze-sze ) << endl;
+
+  }
+  SEC << sze << endl;
+  SEC << dusze << endl;
+
+  SEC << zypp::getZYpp()->diskUsage() << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+}
+catch (...)
+{}
+
+
diff --git a/devel/devel.ma/Main.cc b/devel/devel.ma/Main.cc
new file mode 100644 (file)
index 0000000..513cb09
--- /dev/null
@@ -0,0 +1,112 @@
+#include "Tools.h"
+
+#include <zypp/PoolQuery.h>
+#include <zypp/target/rpm/librpmDb.h>
+#include <zypp/parser/ProductFileReader.h>
+#include "zypp/pool/GetResolvablesToInsDel.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/ExternalProgram.h"
+
+///////////////////////////////////////////////////////////////////
+
+//static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+//static const Pathname sysRoot( "/tmp/ToolScanRepos" );
+static const Pathname sysRoot( "/" );
+
+///////////////////////////////////////////////////////////////////
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    //getZYpp()->resolver()->setOnlyRequires( true );
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    Measure x( "Upgrade" );
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    Measure x( "Upgrade Error" );
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  return true;
+}
+
+namespace zypp
+{
+  namespace target
+  {
+    void writeUpgradeTestcase();
+  }
+}
+
+std::ostream & operator<<( std::ostream & str, const sat::Solvable::SplitIdent & obj )
+{
+  str << "{" << obj.ident() << "}{" << obj.kind() << "}{" << obj.name () << "}" << endl;
+  return str;
+}
+
+namespace zypp {
+std::ostream & dumpOn( std::ostream & str, const Url & obj )
+{
+  str << "{" << obj.getHost() << "}{" << obj.getPort() << "}";
+  return str;
+}
+}
+
+int main( int argc, char * argv[] )
+try {
+  --argc;
+  ++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ///////////////////////////////////////////////////////////////////
+  if ( sysRoot == "/" )
+    ::unsetenv( "ZYPP_CONF" );
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+  dumpRange( WAR << "satpool.multiversion " , satpool.multiversionBegin(), satpool.multiversionEnd() ) << endl;
+  TestSetup::LoadSystemAt( sysRoot, Arch_i586 );
+  ///////////////////////////////////////////////////////////////////
+
+  ui::Selectable::Ptr p( getSel<Package>( "kruler" ) );
+  if ( p )
+  {
+    USR << p->setToDelete() << endl;
+    getZYpp()->resolver()->setCleandepsOnRemove( true );
+    solve();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+}
+catch (...)
+{}
+
diff --git a/devel/devel.ma/NewPool.cc b/devel/devel.ma/NewPool.cc
new file mode 100644 (file)
index 0000000..d96c453
--- /dev/null
@@ -0,0 +1,628 @@
+#include "Tools.h"
+
+#include "zypp/PoolQueryResult.h"
+
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/Exception.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Debug.h>
+#include <zypp/base/Functional.h>
+#include <zypp/base/IOStream.h>
+#include <zypp/base/InputStream.h>
+#include <zypp/base/ProvideNumericId.h>
+#include <zypp/base/Flags.h>
+#include <zypp/AutoDispose.h>
+
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/ResObjects.h"
+#include "zypp/Digest.h"
+#include "zypp/PackageKeyword.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+#include "zypp/RepoManager.h"
+#include "zypp/Repository.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/TriBool.h"
+#include "zypp/repo/PackageProvider.h"
+
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/LocaleSupport.h"
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/AttrMatcher.h"
+#include "zypp/sat/SolvableSet.h"
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/ServiceInfo.h"
+#include "zypp/media/MediaPriority.h"
+
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/target/rpm/librpmDb.h"
+
+#include <boost/mpl/int.hpp>
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::ui;
+
+///////////////////////////////////////////////////////////////////
+
+static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+bool queryInstalledEditionHelper( const std::string & name_r,
+                                  const Edition &     ed_r,
+                                  const Arch &        arch_r )
+{
+  if ( ed_r == Edition::noedition )
+    return true;
+  if ( name_r == "kernel-default" && ed_r == Edition("2.6.22.5-10") )
+    return true;
+  if ( name_r == "update-test-affects-package-manager" && ed_r == Edition("1.1-6") )
+    return true;
+
+  return false;
+}
+
+
+ManagedFile repoProvidePackage( const PoolItem & pi )
+{
+  ResPool _pool( getZYpp()->pool() );
+  repo::RepoMediaAccess _access;
+
+  // Redirect PackageProvider queries for installed editions
+  // (in case of patch/delta rpm processing) to rpmDb.
+  repo::PackageProviderPolicy packageProviderPolicy;
+  packageProviderPolicy.queryInstalledCB( queryInstalledEditionHelper );
+
+  Package::constPtr p = asKind<Package>(pi.resolvable());
+
+  // Build a repository list for repos
+  // contributing to the pool
+  repo::DeltaCandidates deltas( repo::makeDeltaCandidates( _pool.knownRepositoriesBegin(),
+                                                           _pool.knownRepositoriesEnd() ) );
+  repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
+  return pkgProvider.providePackage();
+}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+namespace zypp
+{
+  template <class _LIterator, class _RIterator, class _Function>
+      inline int invokeOnEach( _LIterator lbegin_r, _LIterator lend_r,
+                               _RIterator rbegin_r, _RIterator rend_r,
+                               _Function fnc_r )
+      {
+        int cnt = 0;
+        for ( _LIterator lit = lbegin_r; lit != lend_r; ++lit )
+        {
+          for ( _RIterator rit = rbegin_r; rit != rend_r; ++rit )
+          {
+            ++cnt;
+            if ( ! fnc_r( *lit, *rit ) )
+              return -cnt;
+          }
+        }
+        return cnt;
+      }
+}
+
+
+void dbgDu( Selectable::Ptr sel )
+{
+  if ( sel->installedObj() )
+  {
+    DBG << "i: " << sel->installedObj() << endl
+        << sel->installedObj()->diskusage() << endl;
+  }
+  if ( sel->candidateObj() )
+  {
+    DBG << "c: " << sel->candidateObj() << endl
+        << sel->candidateObj()->diskusage() << endl;
+  }
+  INT << sel << endl
+      << getZYpp()->diskUsage() << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+std::ostream & testDump( std::ostream & str, const PoolItem & pi )
+{
+  str << pi << endl;
+  Package::constPtr p( asKind<Package>(pi) );
+  if ( p )
+  {
+#define OUTS(V) str << str::form("%-25s: ",#V) << p->V() << endl
+    Locale l( "de" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    l = Locale( "fr" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    l = Locale( "dsdf" );
+    str << str::form("%-25s: ",l.code().c_str()) << p->summary(l) << endl;
+    OUTS( summary );
+    OUTS( installSize );
+    OUTS( downloadSize );
+    OUTS( sourcePkgName );
+    OUTS( sourcePkgEdition );
+    OUTS( checksum );
+    OUTS( location );
+#undef OUTS
+
+
+  }
+  return str;
+}
+
+struct Xprint
+{
+  bool operator()( const PoolItem & obj_r )
+  {
+    //MIL << obj_r << endl;
+    //DBG << " -> " << obj_r->satSolvable() << endl;
+
+    return true;
+  }
+
+  bool operator()( const sat::Solvable & obj_r )
+  {
+    //dumpOn( MIL, obj_r ) << endl;
+    return true;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+struct SetTransactValue
+{
+  SetTransactValue( ResStatus::TransactValue newVal_r, ResStatus::TransactByValue causer_r )
+  : _newVal( newVal_r )
+  , _causer( causer_r )
+  {}
+
+  ResStatus::TransactValue   _newVal;
+  ResStatus::TransactByValue _causer;
+
+  bool operator()( const PoolItem & pi ) const
+  {
+    bool ret = pi.status().setTransactValue( _newVal, _causer );
+    if ( ! ret )
+      ERR << _newVal <<  _causer << " " << pi << endl;
+    return ret;
+  }
+};
+
+struct StatusReset : public SetTransactValue
+{
+  StatusReset()
+  : SetTransactValue( ResStatus::KEEP_STATE, ResStatus::USER )
+  {}
+};
+
+struct StatusInstall : public SetTransactValue
+{
+  StatusInstall()
+  : SetTransactValue( ResStatus::TRANSACT, ResStatus::USER )
+  {}
+};
+
+///////////////////////////////////////////////////////////////////
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool install()
+{
+  ZYppCommitPolicy pol;
+  pol.dryRun(true);
+  pol.rpmInstFlags( pol.rpmInstFlags().setFlag( target::rpm::RPMINST_JUSTDB ) );
+  SEC << getZYpp()->commit( pol ) << endl;
+  return true;
+}
+
+void testcase()
+{
+  getZYpp()->resolver()->createSolverTestcase( "./solverTestcase" );
+}
+
+///////////////////////////////////////////////////////////////////
+
+struct DigestReceive : public callback::ReceiveReport<DigestReport>
+{
+  DigestReceive()
+  {
+    connect();
+  }
+
+  virtual bool askUserToAcceptNoDigest( const zypp::Pathname &file )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAccepUnknownDigest( const Pathname &file, const std::string &name )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found )
+  {
+    USR << "fle " << PathInfo(file) << endl;
+    USR << "req " << requested << endl;
+    USR << "fnd " << found << endl;
+    return false;
+  }
+};
+
+struct KeyRingSignalsReceive : public callback::ReceiveReport<KeyRingSignals>
+{
+  KeyRingSignalsReceive()
+  {
+    connect();
+  }
+  virtual void trustedKeyAdded( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+  virtual void trustedKeyRemoved( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+struct MediaChangeReceive : public callback::ReceiveReport<media::MediaChangeReport>
+{
+  virtual Action requestMedia( Url & source
+                               , unsigned mediumNr
+                               , const std::string & label
+                               , Error error
+                               , const std::string & description
+                               , const std::vector<std::string> & devices
+                               , unsigned int & dev_current )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << source << endl
+    << "  " << mediumNr << endl
+    << "  " << label << endl
+    << "  " << error << endl
+    << "  " << description << endl
+    << "  " << devices << endl
+    << "  " << dev_current << endl;
+    return IGNORE;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+namespace container
+{
+  template<class _Tp>
+    bool isIn( const std::set<_Tp> & cont, const typename std::set<_Tp>::value_type & val )
+    { return cont.find( val ) != cont.end(); }
+}
+///////////////////////////////////////////////////////////////////
+
+void itCmp( const sat::Pool::SolvableIterator & l, const sat::Pool::SolvableIterator & r )
+{
+  SEC << *l << " - " << *r << endl;
+  INT << "== " << (l==r) << endl;
+  INT << "!= " << (l!=r) << endl;
+}
+
+bool isTrue()  { return true; }
+bool isFalse() { return false; }
+
+void dumpIdStr()
+{
+  for ( int i = -3; i < 30; ++i )
+  {
+    DBG << i << '\t' << IdString( i ) << endl;
+  }
+}
+
+void ttt( const char * lhs, const char * rhs )
+{
+  DBG << lhs << " <=> " << rhs << " --> " << ::strcmp( lhs, rhs ) << endl;
+}
+
+namespace zypp
+{
+namespace filter
+{
+  template <class _MemFun, class _Value>
+  class HasValue
+  {
+    public:
+      HasValue( _MemFun fun_r, _Value val_r )
+      : _fun( fun_r ), _val( val_r )
+      {}
+      template <class _Tp>
+      bool operator()( const _Tp & obj_r ) const
+      { return( _fun && (obj_r.*_fun)() == _val ); }
+    private:
+      _MemFun _fun;
+      _Value  _val;
+  };
+
+  template <class _MemFun, class _Value>
+  HasValue<_MemFun, _Value> byValue( _MemFun fun_r, _Value val_r )
+  { return HasValue<_MemFun, _Value>( fun_r, val_r ); }
+}
+
+}
+
+template <class L>
+struct _TestO { _TestO( const L & lhs ) : _lhs( lhs ) {} const L & _lhs; };
+
+template <class L>
+std::ostream & operator<<( std::ostream & str, const _TestO<L> & obj )
+{ const L & lhs( obj._lhs); return str << (lhs?'_':'*') << (lhs.empty()?'e':'_') << "'" << lhs << "'"; }
+
+template <class L>
+_TestO<L> testO( const L & lhs )
+{ return _TestO<L>( lhs ); }
+
+template <class L, class R>
+void testCMP( const L & lhs, const R & rhs )
+{
+  MIL << "LHS " << testO(lhs) << endl;
+  MIL << "RHS " << rhs << endl;
+
+#define OUTS(S) DBG << #S << ": " << (S) << endl
+  OUTS( lhs.compare(rhs) );
+  OUTS( lhs != rhs );
+  OUTS( lhs <  rhs );
+  OUTS( lhs <= rhs );
+  OUTS( lhs == rhs );
+  OUTS( lhs >= rhs );
+  OUTS( lhs >  rhs );
+#undef OUTS
+}
+
+#include "zypp/Locks.h"
+#include "zypp/target/HardLocksFile.h"
+inline PoolQuery makeTrivialQuery( IdString ident_r )
+{
+  sat::Solvable::SplitIdent ident( ident_r );
+
+  PoolQuery q;
+  q.addAttribute( sat::SolvAttr::name, ident.name().asString() );
+  q.addKind( ident.kind() );
+  q.setMatchExact();
+  q.setCaseSensitive(true);
+  return q;
+}
+inline PoolQuery makeTrivialQuery( const char * ch )
+{ return makeTrivialQuery( IdString(ch) ); }
+void lktest()
+{
+  static unsigned i = 0;
+  ResPool pool( ResPool::instance() );
+  target::HardLocksFile::Data newdata;
+  pool.getHardLockQueries( newdata );
+  SEC << '[' << i++ << ']' << newdata << endl;
+}
+
+
+Capability guessPackageSpec( const std::string & str_r )
+{
+  return Capability::guessPackageSpec( str_r );
+}
+
+
+
+
+void cut( const Capability & cap )
+{
+  CapDetail detail( cap.detail() );
+  if ( detail.isSimple() )
+  {
+    MIL << detail.kind() << ": " << detail.name();
+    if ( detail.hasArch() )
+      MIL << " (" << detail.arch() << ")";
+    if ( detail.isVersioned() )
+      MIL << " " << detail.op() << " " << detail.ed();
+    MIL << endl;
+  }
+  else
+  {
+    MIL << "---???---" << endl;
+  }
+}
+
+namespace zypp { namespace target {
+  void XRunUpdateMessages( const Pathname & root_r,
+                           const Pathname & messagesPath_r,
+                           const std::vector<sat::Solvable> & checkPackages_r,
+                           ZYppCommitResult & result_r );
+
+}}
+using zypp::target::XRunUpdateMessages;
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+try {
+  --argc,++argv;
+  if (0) {
+    // download the repo index file
+    media::MediaManager mediamanager;
+    media::MediaAccessId mid = mediamanager.open( Url("http://download.opensuse.org") );
+    mediamanager.attach( mid );
+  }
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ZConfig::instance();
+
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+
+  if ( 0 )
+  {
+    Measure x( "INIT TARGET" );
+    {
+      {
+        //zypp::base::LogControl::TmpLineWriter shutUp;
+        getZYpp()->initializeTarget( sysRoot );
+      }
+      getZYpp()->target()->load();
+      USR << "baseproduct:               " << getZYpp()->target()->baseProduct() << endl;
+      USR << "targetDistribution:        " << getZYpp()->target()->targetDistribution() << endl;
+      USR << "targetDistributionRelease: " << getZYpp()->target()->targetDistributionRelease() << endl;
+      dumpRange( USR << "Product ", pool.byKindBegin<Product>(), pool.byKindEnd<Product>() ) << endl;
+    }
+  }
+
+  if ( 0 )
+  {
+    RepoManager repoManager( makeRepoManager( sysRoot ) );
+    ServiceInfoList services = repoManager.knownServices();
+
+    for ( ServiceInfoList::iterator it = services.begin(); it != services.end(); ++it )
+    {
+      ServiceInfo & nservice( *it );
+      SEC << nservice << endl;
+
+      if ( ! nservice.enabled() )
+        continue;
+
+      repoManager.refreshService( nservice );
+    }
+  }
+
+  if ( 1 )
+  {
+    RepoManager repoManager( makeRepoManager( sysRoot ) );
+    RepoInfoList repos = repoManager.knownRepositories();
+
+    // launch repos
+    for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+    {
+      RepoInfo & nrepo( *it );
+      SEC << nrepo << endl;
+
+      if ( ! nrepo.enabled() )
+        continue;
+
+      if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
+      {
+        if ( repoManager.isCached( nrepo ) )
+        {
+          SEC << "cleanCache" << endl;
+          repoManager.cleanCache( nrepo );
+        }
+        SEC << "refreshMetadata" << endl;
+        //repoManager.refreshMetadata( nrepo );
+        SEC << "buildCache" << endl;
+        repoManager.buildCache( nrepo );
+      }
+    }
+
+    // create from cache:
+    {
+      Measure x( "CREATE FROM CACHE" );
+      for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+      {
+        RepoInfo & nrepo( *it );
+        if ( ! nrepo.enabled() )
+          continue;
+
+        Measure x( "CREATE FROM CACHE "+nrepo.alias() );
+        try
+        {
+          repoManager.loadFromCache( nrepo );
+        }
+        catch ( const Exception & exp )
+        {
+          MIL << "Try to rebuild cache..." << endl;
+          SEC << "cleanCache" << endl;
+          repoManager.cleanCache( nrepo );
+          SEC << "buildCache" << endl;
+          repoManager.buildCache( nrepo );
+          SEC << "Create from cache" << endl;
+          repoManager.loadFromCache( nrepo );
+        }
+
+        //USR << "pool: " << pool << endl;
+      }
+    }
+  }
+
+  dumpRange( USR, satpool.reposBegin(), satpool.reposEnd() );
+  USR << "pool: " << pool << endl;
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( 0 )
+  {
+    Measure x( "Upgrade" );
+    getZYpp()->resolver()->doUpgrade();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+
+
+
+
+#if 1
+  getZYpp()->resolver()->addRequire( Capability("amarok") );
+  pool.byKindBegin<Package>()->status().setTransact( true, ResStatus::USER );
+  solve();
+  vdumpPoolStats( USR << "Transacting:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(pool),
+                  make_filter_end<resfilter::ByTransact>(pool) ) << endl;
+#endif
+
+  //////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+  throw;
+}
+catch (...)
+{
+  throw;
+}
+
+
diff --git a/devel/devel.ma/Parse.cc b/devel/devel.ma/Parse.cc
new file mode 100644 (file)
index 0000000..2847296
--- /dev/null
@@ -0,0 +1,483 @@
+#include "Tools.h"
+
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/Exception.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/ProvideNumericId.h>
+#include <zypp/AutoDispose.h>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPoolProxy.h"
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/NVRAD.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/Package.h"
+#include "zypp/Pattern.h"
+#include "zypp/Language.h"
+#include "zypp/Digest.h"
+#include "zypp/PackageKeyword.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/pool/GetResolvablesToInsDel.h"
+
+#include "zypp/parser/TagParser.h"
+#include "zypp/parser/susetags/PackagesFileReader.h"
+#include "zypp/parser/susetags/PackagesLangFileReader.h"
+#include "zypp/parser/susetags/PatternFileReader.h"
+#include "zypp/parser/susetags/ContentFileReader.h"
+#include "zypp/parser/susetags/RepoIndex.h"
+#include "zypp/parser/susetags/RepoParser.h"
+#include "zypp/cache/CacheStore.h"
+#include "zypp/RepoManager.h"
+#include "zypp/RepoInfo.h"
+
+#include "zypp/repo/DeltaCandidates.h"
+#include "zypp/repo/PackageProvider.h"
+#include "zypp/repo/SrcPackageProvider.h"
+
+#include "zypp/ui/PatchContents.h"
+#include "zypp/ResPoolProxy.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::ui;
+using zypp::parser::TagParser;
+
+///////////////////////////////////////////////////////////////////
+
+static const Pathname sysRoot( "/Local/ROOT" );
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+bool queryInstalledEditionHelper( const std::string & name_r,
+                                  const Edition &     ed_r,
+                                  const Arch &        arch_r )
+{
+  if ( ed_r == Edition::noedition )
+    return true;
+  if ( name_r == "kernel-default" && ed_r == Edition("2.6.22.5-10") )
+    return true;
+  if ( name_r == "update-test-affects-package-manager" && ed_r == Edition("1.1-6") )
+    return true;
+
+  return false;
+}
+
+
+ManagedFile repoProvidePackage( const PoolItem & pi )
+{
+  ResPool _pool( getZYpp()->pool() );
+  repo::RepoMediaAccess _access;
+
+  // Redirect PackageProvider queries for installed editions
+  // (in case of patch/delta rpm processing) to rpmDb.
+  repo::PackageProviderPolicy packageProviderPolicy;
+  packageProviderPolicy.queryInstalledCB( queryInstalledEditionHelper );
+
+  Package::constPtr p = asKind<Package>(pi.resolvable());
+
+  // Build a repository list for repos
+  // contributing to the pool
+  repo::DeltaCandidates deltas( repo::makeDeltaCandidates( _pool.knownRepositoriesBegin(),
+                                                           _pool.knownRepositoriesEnd() ) );
+  repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
+  return pkgProvider.providePackage();
+}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+void dbgDu( Selectable::Ptr sel )
+{
+  if ( sel->installedPoolItem() )
+  {
+    DBG << "i: " << sel->installedPoolItem() << endl
+        << sel->installedPoolItem()->diskusage() << endl;
+  }
+  if ( sel->candidatePoolItem() )
+  {
+    DBG << "c: " << sel->candidatePoolItem() << endl
+        << sel->candidatePoolItem()->diskusage() << endl;
+  }
+  INT << sel << endl
+      << getZYpp()->diskUsage() << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+struct Xprint
+{
+  bool operator()( const PoolItem & obj_r )
+  {
+    if ( obj_r.status().isLocked() )
+      SEC << obj_r << endl;
+
+//     handle( asKind<Package>( obj_r ) );
+//     handle( asKind<Patch>( obj_r ) );
+//     handle( asKind<Pattern>( obj_r ) );
+//     handle( asKind<Product>( obj_r ) );
+    return true;
+  }
+
+  void handle( const Package_constPtr & p )
+  {
+    if ( !p )
+      return;
+
+    WAR << p->size() << endl;
+    MIL << p->diskusage() << endl;
+  }
+
+  void handle( const Patch_constPtr & p )
+  {
+    if ( !p )
+      return;
+  }
+
+  void handle( const Pattern_constPtr & p )
+  {
+    if ( !p )
+      return;
+
+    if ( p->vendor().empty() )
+      ERR << p << endl;
+    else if ( p->vendor() == "SUSE (assumed)" )
+      SEC << p << endl;
+  }
+
+  void handle( const Product_constPtr & p )
+  {
+    if ( !p )
+      return;
+
+    USR << p << endl;
+    USR << p->vendor() << endl;
+    USR << p->type() << endl;
+  }
+
+  template<class _C>
+  bool operator()( const _C & obj_r )
+  {
+    return true;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+struct SetTransactValue
+{
+  SetTransactValue( ResStatus::TransactValue newVal_r, ResStatus::TransactByValue causer_r )
+  : _newVal( newVal_r )
+  , _causer( causer_r )
+  {}
+
+  ResStatus::TransactValue   _newVal;
+  ResStatus::TransactByValue _causer;
+
+  bool operator()( const PoolItem & pi ) const
+  {
+    bool ret = pi.status().setTransactValue( _newVal, _causer );
+    if ( ! ret )
+      ERR << _newVal <<  _causer << " " << pi << endl;
+    return ret;
+  }
+};
+
+struct StatusReset : public SetTransactValue
+{
+  StatusReset()
+  : SetTransactValue( ResStatus::KEEP_STATE, ResStatus::USER )
+  {}
+};
+
+struct StatusInstall : public SetTransactValue
+{
+  StatusInstall()
+  : SetTransactValue( ResStatus::TRANSACT, ResStatus::USER )
+  {}
+};
+
+///////////////////////////////////////////////////////////////////
+
+bool solve( bool establish = false )
+{
+  if ( establish )
+  {
+    bool eres = false;
+    {
+      zypp::base::LogControl::TmpLineWriter shutUp;
+      eres = getZYpp()->resolver()->establishPool();
+    }
+    if ( ! eres )
+    {
+      ERR << "establish " << eres << endl;
+      return false;
+    }
+    MIL << "establish " << eres << endl;
+  }
+
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool install()
+{
+  SEC << getZYpp()->commit( ZYppCommitPolicy() ) << endl;
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+struct ConvertDbReceive : public callback::ReceiveReport<target::ScriptResolvableReport>
+{
+  virtual void start( const Resolvable::constPtr & script_r,
+                      const Pathname & path_r,
+                      Task task_r )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << script_r << endl
+    << "  " << path_r   << endl
+    << "  " << task_r   << endl;
+  }
+
+  virtual bool progress( Notify notify_r, const std::string & text_r )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << notify_r << endl
+    << "  " << text_r   << endl;
+    return true;
+  }
+
+  virtual void problem( const std::string & description_r )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << description_r << endl;
+  }
+
+  virtual void finish()
+  {
+    SEC << __FUNCTION__ << endl;
+  }
+
+};
+///////////////////////////////////////////////////////////////////
+
+struct DigestReceive : public callback::ReceiveReport<DigestReport>
+{
+  DigestReceive()
+  {
+    connect();
+  }
+
+  virtual bool askUserToAcceptNoDigest( const zypp::Pathname &file )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAccepUnknownDigest( const Pathname &file, const std::string &name )
+  {
+    USR << endl;
+    return false;
+  }
+  virtual bool askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found )
+  {
+    USR << "fle " << PathInfo(file) << endl;
+    USR << "req " << requested << endl;
+    USR << "fnd " << found << endl;
+
+    waitForInput();
+
+    return false;
+  }
+};
+
+struct KeyRingSignalsReceive : public callback::ReceiveReport<KeyRingSignals>
+{
+  KeyRingSignalsReceive()
+  {
+    connect();
+  }
+  virtual void trustedKeyAdded( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+  virtual void trustedKeyRemoved( const PublicKey &/*key*/ )
+  {
+    USR << endl;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+struct MediaChangeReceive : public callback::ReceiveReport<media::MediaChangeReport>
+{
+  virtual Action requestMedia( Url & source
+                               , unsigned mediumNr
+                               , Error error
+                               , const std::string & description )
+  {
+    SEC << __FUNCTION__ << endl
+    << "  " << source << endl
+    << "  " << mediumNr << endl
+    << "  " << error << endl
+    << "  " << description << endl;
+    return IGNORE;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+namespace container
+{
+  template<class _Tp>
+    bool isIn( const std::set<_Tp> & cont, const typename std::set<_Tp>::value_type & val )
+    { return cont.find( val ) != cont.end(); }
+}
+
+///////////////////////////////////////////////////////////////////
+
+struct AddResolvables
+{
+  bool operator()( const Repository & src ) const
+  {
+    getZYpp()->addResolvables( src.resolvables() );
+    return true;
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+
+std::ostream & operator<<( std::ostream & str, const iostr::EachLine & obj )
+{
+  str << "(" << obj.valid() << ")[" << obj.lineNo() << "|" << obj.lineStart() << "]{" << *obj << "}";
+  return str;
+
+}
+
+///////////////////////////////////////////////////////////////////
+
+#define for_(IT,BEG,END) for ( typeof(BEG) IT = BEG; IT != END; ++IT )
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+
+  void Vtst( const std::string & lhs, const std::string & rhs )
+  {
+    (VendorAttr::instance().equivalent( lhs, rhs )?MIL:ERR) << lhs << " <==> "<< rhs << endl;
+
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+using namespace zypp;
+
+void tt( std::string dd )
+{
+  unsigned level = 3;
+  std::string::size_type pos = dd.find( "/" );
+  while ( --level && pos != std::string::npos )
+  {
+    pos = dd.find( "/", pos+1 );
+  }
+  if ( pos != std::string::npos )
+    dd.erase( pos+1 );
+  DBG << dd << "\t" << level << " " << pos << endl;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  //zypp::base::LogControl::instance().logfile( "log.restrict" );
+  INT << "===[START]==========================================" << endl;
+  setenv( "ZYPP_CONF", "/Local/ROOT/zypp.conf", 1 );
+
+  DigestReceive foo;
+  KeyRingSignalsReceive baa;
+
+  RepoManager repoManager( makeRepoManager( "/Local/ROOT" ) );
+
+  RepoInfoList repos = repoManager.knownRepositories();
+  SEC << "/Local/ROOT " << repos << endl;
+
+  for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+  {
+    RepoInfo & nrepo( *it );
+    if ( ! nrepo.enabled() )
+      continue;
+
+    if ( ! repoManager.isCached( nrepo ) || 0 )
+    {
+      if ( repoManager.isCached( nrepo ) )
+      {
+       SEC << "cleanCache" << endl;
+       repoManager.cleanCache( nrepo );
+      }
+      SEC << "refreshMetadata" << endl;
+      //repoManager.refreshMetadata( nrepo, RepoManager::RefreshForced );
+      repoManager.refreshMetadata( nrepo );
+      SEC << "buildCache" << endl;
+      repoManager.buildCache( nrepo );
+    }
+
+    SEC << nrepo << endl;
+    Repository nrep( repoManager.createFromCache( nrepo ) );
+    const zypp::ResStore & store( nrep.resolvables() );
+
+    dumpPoolStats( SEC << "Store: " << endl,
+                  store.begin(), store.end() ) << endl;
+    getZYpp()->addResolvables( store );
+  }
+
+  ResPool pool( getZYpp()->pool() );
+  USR << "pool: " << pool << endl;
+  SEC << pool.knownRepositoriesSize() << endl;
+
+  if ( 0 )
+  {
+    {
+      zypp::base::LogControl::TmpLineWriter shutUp;
+      //getZYpp()->initTarget( sysRoot );
+      getZYpp()->initTarget( "/" );
+    }
+    MIL << "Added target: " << pool << endl;
+  }
+
+
+  //std::for_each( pool.begin(), pool.end(), Xprint() );
+
+  PoolItem pi = getPi<Patch>( "fetchmsttfonts.sh" );
+  USR << pi << endl;
+
+  //pi.status().setTransact( true, ResStatus::USER );
+  //install();
+
+ ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+
diff --git a/devel/devel.ma/PluginTest.cc b/devel/devel.ma/PluginTest.cc
new file mode 100644 (file)
index 0000000..249c7be
--- /dev/null
@@ -0,0 +1,208 @@
+#include "Tools.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <iostream>
+#include <stdlib.h>
+#include <string.h>
+
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/Exception.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Debug.h>
+#include <zypp/base/Functional.h>
+#include <zypp/base/IOStream.h>
+#include <zypp/base/InputStream.h>
+#include <zypp/base/ProvideNumericId.h>
+#include <zypp/base/Flags.h>
+#include <zypp/AutoDispose.h>
+
+#include <zypp/PluginScript.h>
+#include <zypp/PathInfo.h>
+
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::ui;
+
+///////////////////////////////////////////////////////////////////
+
+static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+static PluginScript scr( "/bin/cat" );
+
+void repeat( const PluginFrame & f )
+{
+  if ( ! scr.isOpen() )
+    scr.open();
+  MIL << "--> " << f << endl;
+  scr.send( f );
+  PluginFrame r( scr.receive() );
+  MIL << "<-- " << r << endl;
+  if ( r != f )
+    ERR << "send/receive does not match." << endl;
+}
+
+void send( const PluginFrame & f )
+{
+  if ( ! scr.isOpen() )
+    scr.open();
+  MIL << "--> " << f << endl;
+  scr.send( f );
+}
+
+
+PluginFrame receive()
+{
+  if ( ! scr.isOpen() )
+    scr.open();
+  PluginFrame r( scr.receive() );
+  MIL << "<-- " << r << endl;
+  return r;
+}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#define DOLOG(C) USR << #C << ": " << endl; C;
+
+namespace zypp {
+  namespace target {
+    void testCommitPlugins( const Pathname & path_r );
+  }
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+try {
+  --argc,++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  //////////////////////////////////////////////////////////////////
+
+  zypp::target::testCommitPlugins( "/tmp/pltest" );
+
+  if ( 0 )
+  {
+  Pathname script( "PluginTest.py" );
+  PluginScript plugin( script );
+  USR << plugin << endl;
+
+  DOLOG( plugin.open() );
+
+  DOLOG( plugin.send( PluginFrame( "PLUGINBEGIN" ) ) );
+
+  PluginFrame ret;
+  DOLOG( ret = plugin.receive() );
+  MIL << ret << endl;
+
+  DOLOG( plugin.send( PluginFrame( "PLUGINEND" ) ) );
+  DOLOG( ret = plugin.receive() );
+  MIL << ret << endl;
+
+  DOLOG( plugin.close() );
+  }
+
+  if ( 0 ) {
+    Pathname script( ZConfig::instance().pluginsPath()/"system/spacewalkx" );
+    if ( PathInfo( script ).isX() )
+      try {
+       PluginScript spacewalk( script );
+       spacewalk.open();
+
+       PluginFrame notify( "PACKAGESETCHANGED" );
+       spacewalk.send( notify );
+
+       PluginFrame ret( spacewalk.receive() );
+       MIL << ret << endl;
+       if ( ret.command() == "ERROR" )
+         ret.writeTo( WAR ) << endl;
+      }
+      catch ( const Exception & excpt )
+      {
+       WAR << excpt.asUserHistory() << endl;
+      }
+  }
+
+  if ( 0 ) {
+    Measure x( "" );
+    PluginFrame f( "a" );
+    f.setBody( std::string( 1020, '0' ) );
+    if ( ! scr.isOpen() )
+      scr.open();
+    for ( unsigned i = 1; true; ++i )
+    {
+      try {
+       MIL << "Receiving " << i << endl;
+       PluginFrame ret( receive() );
+      }
+      catch ( const PluginScriptTimeout & excpt )
+      {
+       ERR << excpt << endl;
+       scr.send( f );
+      }
+      catch ( const PluginScriptDiedUnexpectedly & excpt )
+      {
+       ERR << excpt << endl;
+       ERR << scr << endl;
+       scr.close();
+       break;
+      }
+    }
+  }
+
+  if ( 0 ) {
+    Measure x( "" );
+    PluginFrame f( "a" );
+    f.setBody( std::string( 10200, '0' ) );
+    for ( unsigned i = 1; true; ++i )
+    {
+      try {
+       MIL << "Sending " << i << endl;
+       send( f );
+      }
+      catch ( const PluginScriptTimeout & excpt )
+      {
+       ERR << excpt << endl;
+       ::kill( scr.getPid(), SIGKILL);
+      }
+      catch ( const PluginScriptDiedUnexpectedly & excpt )
+      {
+       ERR << excpt << endl;
+       ERR << scr << endl;
+       scr.close();
+       break;
+      }
+    }
+  }
+
+  //////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+  throw;
+}
+catch (...)
+{
+  throw;
+}
+
+
diff --git a/devel/devel.ma/PluginTest.py b/devel/devel.ma/PluginTest.py
new file mode 100755 (executable)
index 0000000..e3a5873
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+#
+# ZYpp plugin
+#
+import os
+import sys
+import traceback
+import time
+
+from zypp_plugin import Plugin
+
+class MyPlugin(Plugin):
+
+  def PLUGINBEGIN(self, headers, body):
+    # commit is going to start.
+    #self.error( {}, 'oops' )
+    self.ack()
+
+  def PLUGINEND(self, headers, body):
+    # commit ended
+    self.ack()
+
+plugin = MyPlugin()
+plugin.main()
diff --git a/devel/devel.ma/Printing.h b/devel/devel.ma/Printing.h
new file mode 100644 (file)
index 0000000..e15e7f1
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef MA_PRINTING_H
+#define MA_PRINTING_H
+
+#include <iostream>
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/LogTools.h"
+#include <zypp/base/Logger.h>
+
+#include <zypp/base/String.h>
+#include <zypp/base/Iterator.h>
+#include <zypp/base/Algorithm.h>
+#include <zypp/base/Functional.h>
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+
+struct Print
+{
+  template<class _Tp>
+    bool operator()( const _Tp & val_r ) const
+    { USR << val_r << endl; return true; }
+};
+
+///////////////////////////////////////////////////////////////////
+
+template<class _Tp>
+  struct PrintOn : public std::unary_function<_Tp, bool>
+  {
+    bool operator()( const _Tp & obj ) const
+    {
+      if ( _leadNL )
+        _str << std::endl << _prfx << obj;
+      else
+        _str << _prfx << obj << std::endl;
+      return true;
+    }
+
+    PrintOn( std::ostream & str, const std::string & prfx = std::string(), bool leadNL = false )
+    : _str( str )
+    , _prfx( prfx )
+    , _leadNL( leadNL )
+    {}
+
+    std::ostream & _str;
+    std::string _prfx;
+    bool _leadNL;
+  };
+
+///////////////////////////////////////////////////////////////////
+#endif // MA_PRINTING_H
diff --git a/devel/devel.ma/Sat.cc b/devel/devel.ma/Sat.cc
new file mode 100644 (file)
index 0000000..1012ba0
--- /dev/null
@@ -0,0 +1,121 @@
+#include <iostream>
+
+#include <boost/thread.hpp>
+#include <boost/thread/thread_time.hpp>
+namespace boost
+{
+  namespace detail
+  {
+    inline std::ostream & operator<<( std::ostream & str, const thread_data_base & obj )
+    { return str << &obj; }
+  }
+}
+
+#include "zypp/ByteCount.h"
+#include "zypp/Pathname.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+
+using std::endl;
+using namespace zypp;
+
+#undef MIL
+#define MIL MilSync( _BASEFILE, __FUNCTION__, __LINE__ )._str
+
+#ifdef _REENTRANT
+#warning _REENTRANT
+#else
+#warning NOT_REENTRANT
+#endif
+
+template <class Derived>
+struct ClassLevelLockable
+{
+  typedef boost::recursive_mutex Lockable;
+
+  typedef boost::lock_guard<Lockable> Lock;
+  struct Lock
+  {
+    Lock( const Derived & obj )
+    {}
+    ~Lock
+  };
+
+  Lockable _mutex;
+};
+
+template <class Derived>
+struct ObjectLevelLockable
+{
+  typedef boost::recursive_mutex Lockable;
+  typedef boost::lock_guard<Lockable> Lock;
+
+};
+
+
+struct MilSync
+{
+  MilSync( const char * file_r, const char * func_r, const int line_r )
+    : _guard( _mutex )
+    , _str( zypp::base::logger::getStream( ZYPP_BASE_LOGGER_LOGGROUP, zypp::base::logger::E_MIL, file_r, func_r, line_r ) )
+  {}
+  typedef boost::recursive_mutex Lockable;
+  static Lockable             _mutex;
+  boost::lock_guard<Lockable> _guard;
+  std::ostream & _str;
+};
+MilSync::Lockable MilSync::_mutex;
+
+struct ThreadExcl
+{
+  ThreadExcl()
+  {
+    MIL << "+TE" << boost::this_thread::get_id() << endl;
+    boost::this_thread::sleep(  boost::posix_time::seconds(1) );
+  }
+
+  ~ThreadExcl()
+  {
+    MIL << "-TE" << boost::this_thread::get_id() << endl;
+  }
+};
+
+void t_exit()
+{
+  MIL << "---" << boost::this_thread::get_id() << endl;
+}
+
+void t_main()
+{
+  MIL << "+++" << boost::this_thread::get_id() << " " << boost::this_thread::interruption_enabled() << endl;
+  boost::this_thread::at_thread_exit( t_exit );
+  ThreadExcl a;
+  while( true )
+    boost::this_thread::sleep(  boost::posix_time::seconds(1) );
+}
+
+int main( int argc, char * argv[] )
+{
+  //zypp::base::LogControl::instance().logfile( "log.restrict" );
+  INT << "===[START]==========================================" << endl;
+
+  MIL << "M+++" << boost::this_thread::get_id() << endl;
+  boost::thread_group mthreads;
+
+  mthreads.create_thread( t_main );
+  mthreads.create_thread( t_main );
+  mthreads.create_thread( t_main );
+  mthreads.create_thread( t_main );
+  mthreads.create_thread( t_main );
+
+  MIL << "M???" << boost::this_thread::get_id() << endl;
+  //boost::this_thread::sleep(  boost::posix_time::seconds(10) );
+  mthreads.interrupt_all();
+  mthreads.join_all();
+  MIL << "M---" << boost::this_thread::get_id() << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
diff --git a/devel/devel.ma/SigTrackableFail.cc b/devel/devel.ma/SigTrackableFail.cc
new file mode 100644 (file)
index 0000000..1bb8b97
--- /dev/null
@@ -0,0 +1,133 @@
+#include <iostream>
+using std::endl;
+using std::cout;
+
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+using boost::signal;
+using boost::signals::connection;
+using boost::signals::scoped_connection;
+using boost::signals::trackable;
+
+#define DBG std::cerr
+#define MIL std::cerr
+namespace boost
+{
+  template<class Tp>
+  std::ostream & operator<<( std::ostream & str, const signal<Tp> & obj )
+  {
+    return str << "Connected slots: " << obj.num_slots();
+  }
+
+  namespace signals
+  {
+    std::ostream & operator<<( std::ostream & str, const connection & obj )
+    {
+      return str << "Connection: "
+          << ( obj.connected() ? '*' : '_' )
+          << ( obj.blocked()   ? 'B' : '_' )
+          ;
+    }
+  }
+}
+
+struct Sender
+{
+  void ping() const
+  {
+    static unsigned i = 0;
+    ++i;
+    MIL << "Sending " << i << " -> " << _sigA << endl;
+    _sigA( i );
+    _sigB();
+  }
+
+  typedef signal<void(unsigned)> SigA;
+  typedef signal<void(void)>     SigB;
+
+  SigA & siga() const { return _sigA; }
+  SigB & sigb() const { return _sigB; }
+
+  mutable SigA _sigA;
+  mutable SigB _sigB;
+};
+
+struct Receiver : public trackable
+{
+  Receiver() {_s=++s;}
+  Receiver( const Receiver & rhs ) : trackable( rhs ) {_s=++s;}
+  Receiver& operator=( const Receiver & rhs ) {
+    trackable::operator=( rhs );
+    return *this;
+  }
+  ~Receiver() {_s=-_s;}
+  static int s;
+  int _s;
+
+  void fpong()
+  {
+    dumpOn( DBG << "Receiver " << _s << " <- "  << 13 << " (" ) << ")" << endl;
+  }
+
+  void operator()( unsigned i )
+  { pong( i ); }
+
+  void pong( unsigned i )
+  {
+    dumpOn( DBG << "Receiver " << _s << " <- "  << i << " (" ) << ")" << endl;
+  }
+
+  std::ostream & dumpOn( std::ostream & str ) const
+  {
+    return str << "Receiver " << _s << " connected signals: " << _connected_signals().size();
+  }
+};
+
+std::ostream & operator<<( std::ostream & str, const Receiver & obj )
+{ return obj.dumpOn( str ); }
+
+int Receiver::s;
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, const char * argv[] )
+{
+  --argc; ++argv; // skip arg 0
+
+  Sender sender;
+  sender.ping();
+
+  Receiver rec;
+  sender.siga().connect( boost::bind( &Receiver::pong, &rec, _1 ) );
+  sender.ping();
+
+  {
+    Receiver recw;
+    sender.siga().connect( boost::ref(recw) );
+    sender.ping();
+
+    Receiver recx;
+    MIL << recx << endl;
+    sender.siga().connect( boost::bind( &Receiver::pong, &recx, _1 ) );
+    sender.sigb().connect( boost::bind( &Receiver::fpong, &recx ) );
+    MIL << recx << endl;
+    sender.ping();
+
+    Receiver recy;
+    connection cy( sender.siga().connect( boost::bind( &Receiver::pong, &recy, _1 ) ) );
+    sender.ping();
+
+    Receiver recz;
+    scoped_connection cz( sender.siga().connect( boost::bind( &Receiver::pong, &recz, _1 ) ) );
+    sender.ping();
+
+    cy.disconnect();
+  }
+
+  sender.ping();
+  return 0;
+}
+
diff --git a/devel/devel.ma/Signal.cc b/devel/devel.ma/Signal.cc
new file mode 100644 (file)
index 0000000..cbf418a
--- /dev/null
@@ -0,0 +1,122 @@
+#include <iostream>
+
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+
+#include <zypp/base/LogTools.h>
+#include <zypp/base/Easy.h>
+
+using std::endl;
+using std::cout;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace boost
+{
+  template<class Tp>
+      std::ostream & operator<<( std::ostream & str, const signal<Tp> & obj )
+  {
+    return str << "Connected slots: " << obj.num_slots();
+  }
+
+  namespace signals
+  {
+    std::ostream & operator<<( std::ostream & str, const connection & obj )
+    {
+      return str << "Connection: "
+         << ( obj.connected() ? '*' : '_' )
+         << ( obj.blocked()   ? 'B' : '_' )
+         ;
+    }
+  }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using namespace zypp;
+
+using boost::signal;
+using boost::signals::connection;
+using boost::signals::trackable;
+
+struct HelloWorld
+{
+  HelloWorld() {++i;}
+  HelloWorld(const HelloWorld &) {++i;}
+  ~HelloWorld() { --i;}
+
+  void operator()(unsigned) const
+  {
+    USR << "Hello, World! " << i << std::endl;
+  }
+
+  static int i;
+};
+
+int HelloWorld::i = 0;
+
+struct M
+{
+  void ping() const
+  {
+    static unsigned i = 0;
+    ++i;
+    MIL << i << " -> " << _sigA << endl;
+    _sigA( i );
+  }
+
+  typedef signal<void(unsigned)> SigA;
+
+  SigA & siga() const { return _sigA; }
+
+  mutable SigA _sigA;
+};
+
+struct X : public trackable
+{
+  X() {_s=++s;}
+  X( const X & ) {_s=++s;}
+  X& operator=( const X & ) { return *this; }
+  ~X() {_s=-_s;}
+  static int s;
+  int _s;
+
+  void pong( unsigned i ) const
+  {
+    DBG << _s << ' ' << i << endl;
+  }
+};
+
+int X::s;
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, const char * argv[] )
+{
+  --argc; ++argv; // skip arg 0
+
+  M m;
+  m.ping();
+  X xx;
+  m.siga().connect( boost::bind( &X::pong, &xx, _1 ) );
+  m.ping();
+
+  {
+    X x;
+    m.siga().connect( boost::bind( &X::pong, &x, _1 ) );
+    m.ping();
+
+    X y;
+    m.siga().connect( boost::bind( &X::pong, &y, _1 ) );
+    m.ping();
+
+  }
+
+  m.ping();
+
+  ///////////////////////////////////////////
+
+  INT << "---STOP" << endl;
+  return 0;
+}
+
diff --git a/devel/devel.ma/Test.cc b/devel/devel.ma/Test.cc
new file mode 100644 (file)
index 0000000..f39a634
--- /dev/null
@@ -0,0 +1,83 @@
+#include "Tools.h"
+#include <zypp/ResObjects.h>
+
+#include <zypp/sat/LookupAttr.h>
+#include <zypp/PoolQuery.h>
+#include <zypp/sat/AttrMatcher.h>
+
+static const Pathname sysRoot( "/tmp/ToolScanRepos" );
+
+void addInstall( const std::string & pkgspec_r )
+{
+  bool rewrote( false );
+  Capability pkgspec( Capability::guessPackageSpec( pkgspec_r, rewrote ) );
+  MIL << "Add '" << pkgspec << "' for '" << pkgspec_r << "'" << endl;
+  ResPool::instance().resolver().addRequire( pkgspec );
+}
+
+void addConflict( const std::string & pkgspec_r )
+{
+  bool rewrote( false );
+  Capability pkgspec( Capability::guessPackageSpec( pkgspec_r, rewrote ) );
+  MIL << "Con '" << pkgspec << "' for '" << pkgspec_r << "'" << endl;
+  ResPool::instance().resolver().addConflict( pkgspec );
+}
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    //ResPool::instance().resolver().setOnlyRequires( true );
+    rres = ResPool::instance().resolver().resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    ResPool::instance().resolver().problems();
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  vdumpPoolStats( USR << "Transacting:"<< endl,
+                 make_filter_begin<resfilter::ByTransact>(ResPool::instance()),
+                  make_filter_end<resfilter::ByTransact>(ResPool::instance()) ) << endl;
+
+  return true;
+}
+
+bool install()
+{
+  ZYppCommitPolicy pol;
+  pol.dryRun( true );
+  pol.rpmInstFlags( pol.rpmInstFlags().setFlag( target::rpm::RPMINST_JUSTDB ) );
+  SEC << getZYpp()->commit( pol ) << endl;
+  return true;
+}
+
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  ///////////////////////////////////////////////////////////////////
+  if ( sysRoot == "/" )
+    ::unsetenv( "ZYPP_CONF" );
+  TestSetup::LoadSystemAt( sysRoot, Arch_x86_64 );
+  ///////////////////////////////////////////////////////////////////
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+
+//   addConflict( "kernel-default" );
+//   addConflict( "kernel-default-base" );
+  addInstall( "test");
+  solve();
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
diff --git a/devel/devel.ma/ToolProvideSignedDir.cc b/devel/devel.ma/ToolProvideSignedDir.cc
new file mode 100644 (file)
index 0000000..ecd2ffa
--- /dev/null
@@ -0,0 +1,127 @@
+#include "Tools.h"
+
+#include <zypp/Fetcher.h>
+
+static std::string appname( "ToolProvideSignedDir" );
+
+void message( const std::string & msg_r )
+{
+  cerr << "*** " << msg_r << endl;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl;
+    message( msg_r );
+    cerr << endl;
+  }
+  cerr << "Usage: " << appname << "[OPTIONS] URL..." << endl;
+  cerr << "  Load the digested directory at URL to test system below /tmp/" << appname << "." << endl;
+  cerr << "  -r ROOT  Use /tmp/ROOT as location of test system (default: " << appname << ")." << endl;
+  cerr << "  -c       Clear an existing test system (default)." << endl;
+  cerr << "  -n       Do not clear an existing test system but reuse it." << endl;
+  return exit_r;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc;
+  ++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  Pathname mtmp( "/tmp" );
+  Pathname mroot( mtmp/appname );
+  bool     oClearRoot = true;
+
+  std::vector<std::string> urls;
+
+  while ( argc )
+  {
+    if ( argv[0] == std::string("-c") )
+    {
+      oClearRoot = true;
+    }
+    else if ( argv[0] == std::string("-n") )
+    {
+      oClearRoot = false;
+    }
+    else if ( argv[0] == std::string("-r") )
+    {
+      --argc;
+      ++argv;
+      if ( ! argc )
+        return usage( "Missing arg to -r ROOT", 101 );
+
+      if ( *(argv[0]) ) // empty
+        mroot = mtmp/argv[0];
+      else
+        mroot = mtmp/appname;
+    }
+   else
+    {
+      urls.push_back( argv[0] );
+    }
+    --argc;
+    ++argv;
+  }
+
+  if ( urls.empty() )
+  {
+    return usage( "Missing URLs", 102 );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( oClearRoot )
+  {
+    message( "Clear test system at " + mroot.asString() );
+    filesystem::recursive_rmdir( mroot );
+  }
+  else
+  {
+    message( "Use test system at " + mroot.asString() );
+  }
+  filesystem::assert_dir( mroot );
+
+  KeyRing::setDefaultAccept( KeyRing::ACCEPT_UNKNOWNKEY|KeyRing::TRUST_KEY_TEMPORARILY );
+
+  int ret = 0;
+  for_( it, urls.begin(), urls.end() )
+  {
+    message( "Setup " + *it );
+    try
+    {
+      Url url( *it );
+      Pathname tdir( mroot/url.getHost()/url.getPathName() );
+      filesystem::assert_dir( tdir );
+
+      Fetcher f;
+      f.setOptions( Fetcher::AutoAddIndexes );
+      f.enqueueDigestedDir( Pathname("."), /*recursive*/false );
+      MediaSetAccess ma( url );
+      f.start( tdir, ma );
+    }
+    catch ( const Exception & exp )
+    {
+      message( exp.asString() + "\n" + exp.historyAsString() );
+      ++ret;
+    }
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return ret;
+}
diff --git a/devel/devel.ma/Tools.h b/devel/devel.ma/Tools.h
new file mode 100644 (file)
index 0000000..f67a8a4
--- /dev/null
@@ -0,0 +1,217 @@
+#ifndef Tools_h
+#define Tools_h
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <list>
+#include <map>
+#include <set>
+
+#include "Printing.h"
+#include "FakePool.h"
+
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+#include <zypp/base/Easy.h>
+#include <zypp/base/Counter.h>
+#include <zypp/base/Measure.h>
+
+#include <zypp/PathInfo.h>
+#include <zypp/Date.h>
+#include <zypp/ResObject.h>
+#include <zypp/pool/PoolStats.h>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResPoolProxy.h"
+#include "zypp/ui/Selectable.h"
+#include <zypp/Repository.h>
+#include <zypp/RepoManager.h>
+
+
+using namespace zypp;
+using zypp::debug::Measure;
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+
+#define for_providers_(IT,CAP) for ( sat::WhatProvides::const_iterator IT = sat::WhatProvides( Capability CAP ).begin(), _for_end = sat::WhatProvides().end(); IT != _for_end; ++IT )
+
+///////////////////////////////////////////////////////////////////
+
+template<typename T>
+void whichType( T )
+{ INT << __PRETTY_FUNCTION__ << endl; }
+template<typename T>
+void whichType()
+{ INT << __PRETTY_FUNCTION__ << endl; }
+
+void waitForInput()
+{
+  std::string i;
+  USR << "WAITING FOR INPUT!" << endl;
+  std::cin >> i;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void mksrc( const std::string & url, const std::string & alias, RepoManager & repoManager )
+{
+  RepoInfo nrepo;
+  nrepo.setAlias( alias );
+  nrepo.setName( alias );
+  nrepo.setEnabled( true );
+  nrepo.setAutorefresh( false );
+  nrepo.addBaseUrl( Url(url) );
+
+  if ( ! repoManager.isCached( nrepo ) )
+  {
+    repoManager.buildCache( nrepo );
+  }
+
+  repoManager.loadFromCache( nrepo );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+template<class _Condition>
+  struct SetTrue
+  {
+    SetTrue( _Condition cond_r )
+    : _cond( cond_r )
+    {}
+
+    template<class _Tp>
+      bool operator()( _Tp t ) const
+      {
+        _cond( t );
+        return true;
+      }
+
+    _Condition _cond;
+  };
+
+template<class _Condition>
+  inline SetTrue<_Condition> setTrue_c( _Condition cond_r )
+  {
+    return SetTrue<_Condition>( cond_r );
+  }
+
+struct PrintPoolItem
+{
+  void operator()( const PoolItem & pi ) const
+  { USR << pi << endl; }
+};
+
+template <class _Iterator>
+  std::ostream & vdumpPoolStats( std::ostream & str,
+                                 _Iterator begin_r, _Iterator end_r )
+  {
+    pool::PoolStats stats;
+    std::for_each( begin_r, end_r,
+
+                   functor::chain( setTrue_c(PrintPoolItem()),
+                                   setTrue_c(functor::functorRef<void,ResObject::constPtr>(stats)) )
+
+                 );
+    return str << stats;
+  }
+
+///////////////////////////////////////////////////////////////////
+// rstats
+
+typedef zypp::pool::PoolStats Rstats;
+
+template<class _Iterator>
+  void rstats( _Iterator begin, _Iterator end )
+  {
+    DBG << __PRETTY_FUNCTION__ << endl;
+    Rstats stats;
+    for_each( begin, end, functor::functorRef<void,ResObject::constPtr>(stats) );
+    MIL << stats << endl;
+  }
+
+template<class _Container>
+  void rstats( const _Container & c )
+  {
+    rstats( c.begin(), c.end() );
+  }
+
+///////////////////////////////////////////////////////////////////
+
+inline RepoManager makeRepoManager( const Pathname & mgrdir_r )
+{
+  // set via zypp.conf
+  return RepoManager();
+}
+
+///////////////////////////////////////////////////////////////////
+
+template<class _Res>
+ui::Selectable::Ptr getSel( const std::string & name_r )
+{
+  ResPoolProxy uipool( getZYpp()->poolProxy() );
+  for_(it, uipool.byKindBegin<_Res>(), uipool.byKindEnd<_Res>() )
+  {
+    if ( (*it)->name() == name_r )
+      return (*it);
+  }
+  return 0;
+}
+
+
+
+template<class _Res>
+PoolItem getPi( const std::string & alias_r, const std::string & name_r, const Edition & ed_r, const Arch & arch_r )
+{
+  PoolItem ret;
+  ResPool pool( getZYpp()->pool() );
+  for_(it, pool.byIdentBegin<_Res>(name_r), pool.byIdentEnd<_Res>(name_r) )
+  {
+    if (    ( ed_r.empty()    || ed_r.match((*it)->edition()) == 0 )
+         && ( arch_r.empty()  || arch_r == (*it)->arch()  )
+         && ( alias_r.empty() || alias_r == (*it)->repository().alias() ) )
+    {
+      if ( !ret || ret->repository().alias() == sat::Pool::systemRepoAlias() )
+      {
+        ret = (*it);
+        MIL << "    ->" << *it << endl;
+      }
+      else
+      {
+        DBG << "    - " << *it << endl;
+      }
+    }
+    else
+    {
+      DBG << "     ?" << *it << endl;
+    }
+  }
+  return ret;
+}
+template<class _Res>
+PoolItem getPi( const std::string & name_r, const Edition & ed_r, const Arch & arch_r )
+{
+  return getPi<_Res>( "", name_r, ed_r, arch_r );
+}
+template<class _Res>
+PoolItem getPi( const std::string & name_r )
+{
+  return getPi<_Res>( name_r, Edition(), Arch_empty );
+}
+template<class _Res>
+PoolItem getPi( const std::string & name_r, const Edition & ed_r )
+{
+  return getPi<_Res>( name_r, ed_r, Arch_empty );
+}
+template<class _Res>
+PoolItem getPi( const std::string & name_r, const Arch & arch_r )
+{
+  return getPi<_Res>( name_r, Edition(), arch_r );
+}
+
+///////////////////////////////////////////////////////////////////
+#endif // Tools_h
diff --git a/devel/devel.ma/TransList.cc b/devel/devel.ma/TransList.cc
new file mode 100644 (file)
index 0000000..94a66d7
--- /dev/null
@@ -0,0 +1,278 @@
+#include "Tools.h"
+
+#include <zypp/PoolQuery.h>
+#include <zypp/target/rpm/librpmDb.h>
+#include <zypp/parser/ProductFileReader.h>
+#include "zypp/pool/GetResolvablesToInsDel.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/ExternalProgram.h"
+#include <zypp/ZYppCallbacks.h>
+
+#include "zypp/sat/Transaction.h"
+
+///////////////////////////////////////////////////////////////////
+
+//static const Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+//static const Pathname sysRoot( "/tmp/ToolScanRepos" );
+// static const Pathname sysRoot( "/tmp/updateTestcase" );
+static const Pathname sysRoot( "/tmp/ToolScanRepos" );
+
+///////////////////////////////////////////////////////////////////
+struct IRR : public zypp::callback::ReceiveReport<zypp::target::rpm::InstallResolvableReport>
+{
+  IRR()
+  { connect(); }
+#if 0
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,     // retry
+    IGNORE     // ignore the failure
+  };
+
+  enum Error {
+    NO_ERROR,
+    NOT_FOUND,         // the requested Url was not found
+    IO,                // IO error
+    INVALID            // th resolvable is invalid
+  };
+
+        // the level of RPM pushing
+  /** \deprecated We fortunately no longer do 3 attempts. */
+  enum RpmLevel {
+    RPM,
+    RPM_NODEPS,
+    RPM_NODEPS_FORCE
+  };
+#endif
+
+  virtual void reportbegin()
+  { /*SEC << endl;*/ }
+  virtual void reportend()
+  { /*SEC << endl;*/ }
+
+  virtual void start(Resolvable::constPtr /*resolvable*/)
+  { INT << endl; }
+
+  virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+  {
+    static int i = 4;
+    if ( --i <= 0 )
+    {
+      INT << "return abort" << endl;
+      return false;
+    }
+    return true;
+  }
+
+  virtual Action problem(Resolvable::constPtr /*resolvable*/, Error /*error*/, const std::string &/*description*/, RpmLevel /*level*/)
+  {
+    INT << "return abort" << endl;
+    return ABORT;
+  }
+
+  virtual void finish(Resolvable::constPtr /*resolvable*/, Error /*error*/, const std::string &/*reason*/, RpmLevel /*level*/)
+  { INT << endl; }
+};
+
+struct RRR : public zypp::callback::ReceiveReport<zypp::target::rpm::RemoveResolvableReport>
+{
+  RRR()
+  { connect(); }
+#if 0
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,     // retry
+    IGNORE     // ignore the failure
+  };
+
+  enum Error {
+    NO_ERROR,
+    NOT_FOUND,         // the requested Url was not found
+    IO,                // IO error
+    INVALID            // th resolvable is invalid
+  };
+#endif
+
+  virtual void reportbegin()
+  { /*SEC << endl;*/ }
+  virtual void reportend()
+  { /*SEC << endl;*/ }
+
+  virtual void start( Resolvable::constPtr /*resolvable*/ )
+  { INT << endl; }
+
+  virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+  { INT << endl; return true; }
+
+  virtual Action problem( Resolvable::constPtr /*resolvable*/ , Error /*error*/ , const std::string &/*description*/ )
+  { INT << endl; return ABORT; }
+
+  virtual void finish( Resolvable::constPtr /*resolvable*/ , Error /*error*/ , const std::string &/*reason*/ )
+  { INT << endl; }
+};
+
+
+bool solve()
+{
+  bool rres = false;
+  {
+    //zypp::base::LogControl::TmpLineWriter shutUp;
+    //getZYpp()->resolver()->setOnlyRequires( true );
+    rres = getZYpp()->resolver()->resolvePool();
+  }
+  if ( ! rres )
+  {
+    ERR << "resolve " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "resolve " << rres << endl;
+  return true;
+}
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    zypp::base::LogControl::TmpLineWriter shutUp;
+    Measure x( "Upgrade" );
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    Measure x( "Upgrade Error" );
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  return true;
+}
+
+bool install()
+{
+  ZYppCommitPolicy pol;
+  //pol.dryRun( true );
+  pol.downloadMode( DownloadAsNeeded );
+  pol.rpmInstFlags( pol.rpmInstFlags().setFlag( target::rpm::RPMINST_JUSTDB ) );
+  ZYppCommitResult res( getZYpp()->commit( pol ) );
+  SEC << res << endl;
+  MIL << res.transactionStepList() << endl;
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+namespace zypp
+{
+  void tradOrder()
+  {
+    scoped_ptr<base::LogControl::TmpLineWriter> shutUp( new base::LogControl::TmpLineWriter );
+    ResPool pool( ResPool::instance() );
+    pool::GetResolvablesToInsDel collect( pool, pool::GetResolvablesToInsDel::ORDER_BY_SOURCE );
+    shutUp.reset();
+
+    MIL << "GetResolvablesToInsDel -" << collect._toDelete.size()
+    << " +" << (collect._toInstall.size() + collect._toSrcinstall.size()) << " {" << endl;
+    for_( it, collect._toDelete.begin(), collect._toDelete.end() )
+    {
+      MIL << " - " << *it << endl;
+    }
+    for_( it, collect._toInstall.begin(), collect._toInstall.end() )
+    {
+      MIL << " + " << *it << endl;
+    }
+    for_( it, collect._toSrcinstall.begin(), collect._toSrcinstall.end() )
+    {
+      MIL << " + " << *it << endl;
+    }
+    MIL << "}" << endl;
+  }
+
+}
+///////////////////////////////////////////////////////////////////
+
+void checkTrans()
+{
+  ResPool pool( ResPool::instance() );
+  pool::GetResolvablesToInsDel collect( pool, pool::GetResolvablesToInsDel::ORDER_BY_SOURCE );
+  collect.debugDiffTransaction();
+}
+
+template <class _Iter>
+unsigned count( _Iter begin, _Iter end )
+{
+  unsigned cnt = 0;
+  for_( it, begin, end )
+    ++cnt;
+  return cnt;
+}
+
+///////////////////////////////////////////////////////////////////
+int main( int argc, char * argv[] )
+try {
+  --argc;
+  ++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  ///////////////////////////////////////////////////////////////////
+  IRR _irr;
+  RRR _rrr;
+  if ( sysRoot == "/" )
+    ::unsetenv( "ZYPP_CONF" );
+
+  sat::Transaction();
+  const sat::Transaction a;
+  sat::Transaction b;
+  sat::Transaction c( a );
+  b = a;
+
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+  dumpRange( WAR << "satpool.multiversion " , satpool.multiversionBegin(), satpool.multiversionEnd() ) << endl;
+  TestSetup::LoadSystemAt( sysRoot, Arch_i586 );
+  getZYpp()->initializeTarget( sysRoot );
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( 1 )
+  {
+    getPi<Product>( "openSUSE-CD-retail" ).status().setToBeInstalled( ResStatus::USER );
+    getPi<Pattern>( "devel_qt4" ).status().setToBeInstalled( ResStatus::USER );
+//     getPi<Pattern>( "devel_qt4" ).status().setToBeInstalled( ResStatus::USER );
+    solve();
+    sat::Transaction trans( pool.resolver().getTransaction() );
+    trans.order();
+
+    USR << count( trans.actionBegin(), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(sat::Transaction::STEP_TODO), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(sat::Transaction::STEP_DONE), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(sat::Transaction::STEP_ERROR), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(sat::Transaction::STEP_TODO|sat::Transaction::STEP_ERROR), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(~sat::Transaction::STEP_ERROR), trans.actionEnd() ) << endl;
+    USR << count( trans.actionBegin(~sat::Transaction::STEP_TODO), trans.actionEnd() ) << endl;
+
+    //install();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //  ResPoolProxy selpool( pool.proxy() );
+  if ( 0 )
+  {
+    upgrade();
+    install();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+}
+catch (...)
+{}
+
diff --git a/devel/devel.ma/Xml.cc b/devel/devel.ma/Xml.cc
new file mode 100644 (file)
index 0000000..07d06ef
--- /dev/null
@@ -0,0 +1,217 @@
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+
+#include <zypp/base/LogTools.h>
+#include <zypp/base/LogControl.h>
+#include <zypp/base/Exception.h>
+
+#include <zypp/parser/xml/Reader.h>
+//#include <zypp/parser/xml/ParseDef.h>
+//#include <zypp/parser/xml/ParseDefConsume.h>
+
+using namespace std;
+using namespace zypp;
+
+#include <zypp/base/Measure.h>
+using zypp::debug::Measure;
+
+///////////////////////////////////////////////////////////////////
+
+/** Helper to detect an objects type. */
+template<class _Cl> void ti( const _Cl & c )
+{
+  SEC << __PRETTY_FUNCTION__ << endl;
+}
+
+bool noop( xml::Reader & reader_r )
+{
+  return true;
+}
+struct Noop
+{
+  bool operator()( xml::Reader & reader_r ) const
+  { return true; }
+};
+
+///////////////////////////////////////////////////////////////////
+
+bool dumpNode( xml::Reader & reader_r )
+{
+  switch ( reader_r->nodeType() )
+  {
+    case XML_READER_TYPE_ELEMENT:
+      MIL << *reader_r << endl;
+      for ( int i = 0; i < reader_r->attributeCount(); ++i )
+      {
+       MIL << " attr no " << i << " '" << reader_r->getAttributeNo( i ) << "'" << endl;
+      }
+      break;
+
+    case XML_READER_TYPE_ATTRIBUTE:
+      WAR << *reader_r << endl;
+      break;
+
+    case XML_READER_TYPE_TEXT:
+    case XML_READER_TYPE_CDATA:
+      DBG << *reader_r << endl;
+      break;
+
+    default:
+      //ERR << *reader_r << endl;
+      break;
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+bool consume( xml::Reader & reader_r )
+{
+  switch ( reader_r->nodeType() )
+  {
+    case XML_READER_TYPE_ELEMENT:
+      MIL << *reader_r << endl;
+      for ( int i = 0; i < reader_r->attributeCount(); ++i )
+      {
+       MIL << " attr no " << i << " '" << reader_r->getAttributeNo( i ) << "'" << endl;
+      }
+      break;
+
+    case XML_READER_TYPE_ATTRIBUTE:
+      WAR << *reader_r << endl;
+      break;
+
+    case XML_READER_TYPE_TEXT:
+    case XML_READER_TYPE_CDATA:
+      DBG << *reader_r << endl;
+      break;
+
+    default:
+      //ERR << *reader_r << endl;
+      break;
+  }
+  return true;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  --argc, ++argv;
+  INT << "===[START]==========================================" << endl;
+
+  bool verbose( true );
+  Pathname input( "test.xml" );
+  xml::Reader::ProcessNode consumer( consume );
+
+  if ( argc && !strcmp( *argv, "-q" ) )
+  {
+    --argc, ++argv;
+    verbose = false;
+  }
+
+  {
+    Measure m( "Parse all" );
+    for ( ; argc; --argc, ++argv )
+    {
+      input = *argv;
+
+      try {
+       Measure m( input.basename() );
+//     zypp::base::LogControl::TmpLineWriter shutUp;
+       xml::Reader reader( input );
+       if ( verbose )
+         reader.foreachNodeOrAttribute( consumer );
+       else
+         reader.foreachNode( consumer );
+      }
+      catch ( const Exception & exp )
+      {
+       INT << exp << endl << exp.historyAsString();
+      }
+    }
+  }
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
+
+/*
+<?xml version="1.0" encoding="UTF-8"?>
+<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="23230">
+<package type="rpm">
+<name>fam-devel</name>
+<arch>ppc</arch>
+<version epoch="0" ver="2.6.10" rel="141"/>
+<checksum type="sha" pkgid="YES">59d6a65cdadd911fe8ceee87740a54305b2ab053</checksum>
+<summary>Include Files and Libraries Mandatory for Development</summary>
+<description>Fam is a file alteration monitoring service. This means that you can
+
+foreachNode( dumpNode )
+=======================
+START MEASURE(Parse)
+0:ELEMENT <metadata>  [attr 3]
+ attr no 0 'http://linux.duke.edu/metadata/common'
+ attr no 1 'http://linux.duke.edu/metadata/rpm'
+ attr no 2 '23230'
+1: ELEMENT <package>  [attr 1]
+ attr no 0 'rpm'
+2:  ELEMENT <name>  [noattr]
+3:   TEXT <#text>  [noattr] {fam-devel}
+2:  ELEMENT <arch>  [noattr]
+3:   TEXT <#text>  [noattr] {ppc}
+2:  ELEMENT <version>  [attr 3|empty]
+ attr no 0 '0'
+ attr no 1 '2.6.10'
+ attr no 2 '141'
+2:  ELEMENT <checksum>  [attr 2]
+ attr no 0 'sha'
+ attr no 1 'YES'
+3:   TEXT <#text>  [noattr] {59d6a65cdadd911fe8ceee87740a54305b2ab053}
+2:  ELEMENT <summary>  [noattr]
+3:   TEXT <#text>  [noattr] {Include Files and Libraries Mandatory for Development}
+2:  ELEMENT <description>  [noattr]
+3:   TEXT <#text>  [noattr] {Fam is a file alteration monitoring service. This means that you can
+
+foreachNodeOrAttribute( dumpNode )
+==================================
+START MEASURE(Parse)
+0:ELEMENT <metadata>  [attr 3]
+ attr no 0 'http://linux.duke.edu/metadata/common'
+ attr no 1 'http://linux.duke.edu/metadata/rpm'
+ attr no 2 '23230'
+1: ATTRIBUTE <xmlns>  [noattr] {http://linux.duke.edu/metadata/common}
+1: ATTRIBUTE <xmlns:rpm>  [noattr] {http://linux.duke.edu/metadata/rpm}
+1: ATTRIBUTE <packages>  [noattr] {23230}
+1: ELEMENT <package>  [attr 1]
+ attr no 0 'rpm'
+2:  ATTRIBUTE <type>  [noattr] {rpm}
+2:  ELEMENT <name>  [noattr]
+3:   TEXT <#text>  [noattr] {fam-devel}
+2:  ELEMENT <arch>  [noattr]
+3:   TEXT <#text>  [noattr] {ppc}
+2:  ELEMENT <version>  [attr 3|empty]
+ attr no 0 '0'
+ attr no 1 '2.6.10'
+ attr no 2 '141'
+3:   ATTRIBUTE <epoch>  [noattr] {0}
+3:   ATTRIBUTE <ver>  [noattr] {2.6.10}
+3:   ATTRIBUTE <rel>  [noattr] {141}
+2:  ELEMENT <checksum>  [attr 2]
+ attr no 0 'sha'
+ attr no 1 'YES'
+3:   ATTRIBUTE <type>  [noattr] {sha}
+3:   ATTRIBUTE <pkgid>  [noattr] {YES}
+3:   TEXT <#text>  [noattr] {59d6a65cdadd911fe8ceee87740a54305b2ab053}
+3:   ATTRIBUTE <type>  [noattr] {sha}
+3:   ATTRIBUTE <pkgid>  [noattr] {YES}
+2:  ELEMENT <summary>  [noattr]
+3:   TEXT <#text>  [noattr] {Include Files and Libraries Mandatory for Development}
+2:  ELEMENT <description>  [noattr]
+3:   TEXT <#text>  [noattr] {Fam is a file alteration monitoring service. This means that you can
+
+*/
diff --git a/devel/devel.ma/defstr.txt b/devel/devel.ma/defstr.txt
new file mode 100644 (file)
index 0000000..1e98f8b
--- /dev/null
@@ -0,0 +1,31 @@
+0      <NULL>
+1      
+2      solvable:name
+3      solvable:arch
+4      solvable:evr
+5      solvable:vendor
+6      solvable:provides
+7      solvable:obsoletes
+8      solvable:conflicts
+9      solvable:requires
+10     solvable:recommends
+11     solvable:suggests
+12     solvable:supplements
+13     solvable:enhances
+14     solvable:freshens
+15     rpm:dbid
+16     solvable:prereqmarker
+17     solvable:filemarker
+18     namespace:installed
+19     namespace:modalias
+20     system:system
+21     src
+22     nosrc
+23     noarch
+24     <NULL>
+25     <NULL>
+26     <NULL>
+27     <NULL>
+28     <NULL>
+29     <NULL>
+
diff --git a/devel/devel.ma/iorderbug.pool b/devel/devel.ma/iorderbug.pool
new file mode 100644 (file)
index 0000000..8ea0574
--- /dev/null
@@ -0,0 +1,28 @@
+@ package
+@ installed
+
+- readline 1 1 i686
+
+- bash 1 1 i686
+@ requires
+readline = 1
+
+- fontcfg 1 1 i686
+@ prerequires
+bash
+
+@ available
+
+- readline 2 1 i686
+
+- bash 2 1 i686
+@ requires
+readline = 2
+
+- fontcfg 2 1 i686
+#@ prerequires
+#bash
+filesystem()
+filesystem(sdfa)
+
+@ fin
diff --git a/devel/devel.ma/lines2array b/devel/devel.ma/lines2array
new file mode 100755 (executable)
index 0000000..18107ae
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/bash
+
+OFILE=${1:-array}
+
+cat $1 | awk -vOFILE=$OFILE '
+BEGIN {
+       sep = " "
+       printf "#ifndef ARRAY_%s_h\n", OFILE
+       printf "#define ARRAY_%s_h\n", OFILE
+       printf "\n"
+       printf "#include <list>\n"
+       printf "#include <string>\n"
+       printf "\n"
+       printf "std::list<std::string> %s()\n", OFILE
+       printf "{\n"
+       printf "  const char *const val[] = {\n"
+}
+{
+       printf "    %s\"%s\"\n", sep, $0
+       sep = ","
+}
+END {
+       printf "  };\n"
+       printf "  const char *const* e = val + sizeof( val ) / sizeof( const char *const );\n"
+       printf "  return std::list<std::string>( val, e );\n"
+       printf "}\n"
+       printf "\n"
+       printf "#endif // ARRAY_%s_h\n", OFILE
+}
+' > $OFILE.h
diff --git a/devel/devel.ma/ma_test b/devel/devel.ma/ma_test
new file mode 100755 (executable)
index 0000000..4baea41
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+#
+#export LD_LIBRARY_PATH=/usr/local/lib64
+
+PRG=Main
+SUDO=
+if [ "$1" = -su ]; then
+       SUDO="sudo -E"
+       shift
+fi
+
+if [ -n "$1" ]; then
+       PRG=$1
+       shift
+fi
+
+if [ ! -x ~ma/bin/sy2l.tcl ]; then
+       exec ./$PRG "$@"
+fi
+
+LPIPE=LOGFILE
+
+test -p "$LPIPE" || {
+        test -e "$LPIPE" && { echo "No pipe '$LPIPE' exists"; exit 1; }
+        mkfifo $LPIPE
+}
+if ! fuser $LPIPE >/dev/null; then
+        ~ma/bin/sy2l.tcl $LPIPE &
+fi
+
+if ps axl | grep -q '[/]usr/sbin/iceccd'; then
+       MAKE="make -j 15"
+else
+       MAKE="make -j 2"
+fi
+
+$MAKE $PRG || exit 1
+echo "====================================================="
+export ZYPP_LOGFILE=-
+export ZYPP_KEYRING_DEFAULT_ACCEPT_ALL=1
+export SYSROOT=/Local/ROOT
+export ZYPP_CONF=$SYSROOT/zypp.conf
+
+#export ZYPP_TESTSUITE_FAKE_ARCH=x86_64
+#$SUDO ./$PRG "$@" 2>&1 | tee log > $LPIPE
+exec 2> >(tee log > $LPIPE)
+$SUDO ./$PRG "$@"
+
diff --git a/devel/devel.ma/main.cc b/devel/devel.ma/main.cc
new file mode 100644 (file)
index 0000000..948d73f
--- /dev/null
@@ -0,0 +1,58 @@
+#include "Tools.h"
+
+#include <zypp/ResPool.h>
+#include <zypp/ResObjects.h>
+#include <zypp/PoolQuery.h>
+
+#include <zypp/misc/CheckAccessDeleted.h>
+
+///////////////////////////////////////////////////////////////////
+
+Pathname sysRoot( getenv("SYSROOT") ? getenv("SYSROOT") : "/Local/ROOT" );
+
+///////////////////////////////////////////////////////////////////
+
+int main( int argc, char * argv[] )
+try {
+  --argc;
+  ++argv;
+  zypp::base::LogControl::instance().logToStdErr();
+  INT << "===[START]==========================================" << endl;
+  if ( argc )
+  {
+    unsetenv("SYSROOT");
+    sysRoot = Pathname(*argv);
+    setenv( "ZYPP_CONF", (sysRoot/"etc/zypp.conf").c_str(), true );
+  }
+  ZConfig::instance();
+  //TestSetup::LoadSystemAt( sysRoot );
+  ///////////////////////////////////////////////////////////////////
+  ResPool   pool( ResPool::instance() );
+  sat::Pool satpool( sat::Pool::instance() );
+  ///////////////////////////////////////////////////////////////////
+  dumpRange( USR, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  USR << "pool: " << pool << endl;
+  ///////////////////////////////////////////////////////////////////
+
+  {
+    Measure x("x");
+    CheckAccessDeleted checker;
+    USR << checker << endl;
+  }
+  SEC << CheckAccessDeleted::findService( "syslog" ) << endl;
+  SEC << CheckAccessDeleted::findService( "syslogd" ) << endl;
+  SEC << CheckAccessDeleted::findService( "ssh" ) << endl;
+  SEC << CheckAccessDeleted::findService( "sshd" ) << endl;
+  SEC << CheckAccessDeleted::findService( 3844 ) << endl;
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  zypp::base::LogControl::instance().logNothing();
+  return 0;
+}
+catch ( const Exception & exp )
+{
+  INT << exp << endl << exp.historyAsString();
+}
+catch (...)
+{}
+
diff --git a/devel/devel.ma/main.h b/devel/devel.ma/main.h
new file mode 100644 (file)
index 0000000..cd7ed67
--- /dev/null
@@ -0,0 +1,170 @@
+#ifndef main_h
+#define main_h
+
+#include <iosfwd>
+#include <string>
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include <zypp/base/PtrTypes.h>
+
+using std::string;
+
+///////////////////////////////////////////////////////////////////
+
+class Resolvable : public zypp::base::ReferenceCounted, private zypp::base::NonCopyable
+{
+  friend std::ostream & operator<<( std::ostream & str, const Resolvable & res );
+  public:
+    string kind() const;
+    string name() const;
+  public:
+    typedef Resolvable Self;
+    typedef zypp::base::intrusive_ptr<Self>       Ptr;
+    typedef zypp::base::intrusive_ptr<const Self> constPtr;
+  protected:
+    Resolvable( const string kind_r /*NVRA*/ );
+    virtual ~Resolvable();
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+  private:
+    struct Impl;
+    zypp::base::ImplPtr<Impl> _impl;
+};
+
+inline void intrusive_ptr_add_ref( const Resolvable * ptr_r )
+{ zypp::base::ReferenceCounted::add_ref( ptr_r ); }
+inline void intrusive_ptr_release( const Resolvable * ptr_r )
+{ zypp::base::ReferenceCounted::release( ptr_r ); }
+
+inline std::ostream & operator<<( std::ostream & str, const Resolvable & res )
+{ return res.dumpOn( str ); }
+
+///////////////////////////////////////////////////////////////////
+
+// connect resolvables interface and implementation.
+template<class _Res>
+  struct ResImplConnect : public _Res
+  {
+  public:
+    typedef ResImplConnect               Self;
+    typedef typename _Res::Impl          Impl;
+    typedef zypp::base::shared_ptr<Impl> ImplPtr;
+    typedef zypp::base::intrusive_ptr<Self>       Ptr;
+    typedef zypp::base::intrusive_ptr<const Self> constPtr;
+  public:
+    ResImplConnect( /*NVRA*/ ImplPtr impl_r )
+    : _impl( impl_r ? impl_r : ImplPtr(new Impl /*NVRA*/) )
+    { _impl->_backRef = this; }
+    virtual ~ResImplConnect() {}
+  private:
+    ImplPtr _impl;
+    virtual Impl &       impl()       { return *_impl; }
+    virtual const Impl & impl() const { return *_impl; }
+  };
+
+
+template<class _Impl>
+  typename _Impl::ResType::Ptr
+  makeResolvable( /*NVRA*/ zypp::base::shared_ptr<_Impl> & impl_r )
+  {
+    impl_r.reset( new _Impl );
+    return new ResImplConnect<typename _Impl::ResType>( /*NVRA*/ impl_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+#include <list>
+using std::list;
+
+struct ObjectImpl
+{
+  const Resolvable * self() const { return _backRef; }
+  Resolvable *       self()       { return _backRef; }
+
+  virtual string       summary()         const { return string(); }
+  virtual list<string> description()     const { return list<string>(); }
+  //virtual FSize        size()            const { return 0; }
+  //virtual bool         providesSources() const { return false; }
+
+  ObjectImpl()
+  : _backRef( 0 )
+  {}
+  virtual ~ObjectImpl() {};
+
+  private:
+    template<class _Res>
+      friend class ResImpl;
+    Resolvable * _backRef;
+};
+
+class Object : public Resolvable
+{
+  public:
+    string       summary()         const;
+    list<string> description()     const;
+    //FSize        size()            const;
+    //bool         providesSources() const;
+  public:
+    typedef Object     Self;
+    typedef ObjectImpl Impl;
+    typedef zypp::base::intrusive_ptr<Self>       Ptr;
+    typedef zypp::base::intrusive_ptr<const Self> constPtr;
+  protected:
+    Object( const string kind_r /*NVRA*/ );
+    virtual ~Object();
+  private:
+    virtual Impl &       impl()       = 0;
+    virtual const Impl & impl() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////
+class Package;
+struct PackageImpl : public ObjectImpl
+{
+  typedef Package ResType;
+  virtual string packagedata() const { return string(); }
+};
+
+class Package : public Object
+{
+  public:
+    string packagedata() const;
+  public:
+    typedef Package     Self;
+    typedef PackageImpl Impl;
+    typedef zypp::base::intrusive_ptr<Self>       Ptr;
+    typedef zypp::base::intrusive_ptr<const Self> constPtr;
+  protected:
+    Package( /*NVRA*/ );
+    virtual ~Package();
+  private:
+    virtual Impl &       impl()       = 0;
+    virtual const Impl & impl() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////
+
+class Selection;
+struct SelectionImpl : public ObjectImpl
+{
+  typedef Selection ResType;
+  virtual string selectiondata() const { return string(); }
+};
+
+class Selection : public Object
+{
+  public:
+    string selectiondata() const;
+  public:
+    typedef Selection     Self;
+    typedef SelectionImpl Impl;
+    typedef zypp::base::intrusive_ptr<Self>       Ptr;
+    typedef zypp::base::intrusive_ptr<const Self> constPtr;
+  protected:
+    Selection( /*NVRA*/ );
+    virtual ~Selection();
+  private:
+    virtual Impl &       impl()       = 0;
+    virtual const Impl & impl() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////
+#endif // main_h
diff --git a/devel/genclass.in b/devel/genclass.in
new file mode 100644 (file)
index 0000000..faa493a
--- /dev/null
@@ -0,0 +1,240 @@
+#! /bin/bash
+
+function usage() {
+   echo $@ >&2
+   echo <<EOF >&2
+Usage: genclass [path/]stem
+EOF
+   exit 1
+}
+
+test -z "$1" && usage "Missing name!"
+
+TOPSRCDIR=${XTOPSRCDIR:-$(cd @CMAKE_SOURCE_DIR@ && pwd)}
+test -z "$TOPSRCDIR" && {
+   echo "Dir does not exist '$TOPSRCDIR'" >&2
+   exit 1
+}
+
+OUTDIR=$(dirname $1)
+STEM=$(basename $1)
+STEMDIR=$( cd $OUTDIR && pwd )
+test -z "$STEMDIR" && {
+   echo "Dir does not exist '$(dirname $1)'" >&2
+   exit 1
+}
+STEMDIR=${STEMDIR#$TOPSRCDIR/}
+
+CLASS=$STEM
+CLASS_H=$STEMDIR/$STEM.h
+CLASS_CC=$STEMDIR/$STEM.cc
+
+OUT_CLASS_H=$OUTDIR/$STEM.h
+OUT_CLASS_CC=$OUTDIR/$STEM.cc
+test -e $OUT_CLASS_H -o -e $OUT_CLASS_CC && {
+   test -e $OUT_CLASS_H && echo "File exists '$OUT_CLASS_H' using '$OUT_CLASS_H.new'" >&2
+   test -e $OUT_CLASS_CC && echo "File exists '$OUT_CLASS_CC' using '$OUT_CLASS_CC.new'" >&2
+   OUT_CLASS_H="$OUT_CLASS_H.new"
+   OUT_CLASS_CC="$OUT_CLASS_CC.new"
+}
+
+INCLUDE_H=$CLASS_H
+INCLUDE_DEF=$(echo $INCLUDE_H | sed 's/[./]/_/g' | awk '{print toupper($0)}')
+NSLIST=$(echo $(dirname $INCLUDE_H) | awk '{l=tolower($0);gsub("/"," ",l);print l}')
+SNLIST=
+INDENT=
+for N in $NSLIST; do
+   SNLIST="$N $SNLIST"
+   INDENT="$INDENT  "
+done
+
+######################################################################
+function intro() {
+######################################################################
+   local FILE=$1
+cat <<EOF
+/*---------------------------------------------------------------------\\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      ${FILE}
+ */
+EOF
+}
+
+######################################################################
+function nsopen() {
+######################################################################
+   local I=
+   for N in $NSLIST; do
+      echo "${I}///////////////////////////////////////////////////////////////////"
+      echo "${I}namespace $N"
+      echo "${I}{ /////////////////////////////////////////////////////////////////"
+      I="$I  "
+   done
+}
+
+######################################################################
+function nsclose() {
+######################################################################
+   local I=${INDENT}
+   for N in $SNLIST; do
+      echo "${I}/////////////////////////////////////////////////////////////////"
+      I=${I#  }
+      echo "${I}} // namespace $N"
+      echo "${I}///////////////////////////////////////////////////////////////////"
+   done
+}
+
+######################################################################
+function genH() {
+######################################################################
+cat <<EOF
+$(intro $CLASS_H)
+#ifndef $INCLUDE_DEF
+#define $INCLUDE_DEF
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/SafeBool.h"
+#include "zypp/base/NonCopyable.h"
+
+$(nsopen)
+
+${INDENT}/**
+${INDENT} */
+${INDENT}class ${CLASS} : protected base::SafeBool<${CLASS}>, private base::NonCopyable
+${INDENT}{
+${INDENT}  friend std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj );
+${INDENT}  friend std::ostream & dumpOn( std::ostream & str, const ${CLASS} & obj );
+${INDENT}  friend bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs );
+
+${INDENT}  public:
+${INDENT}    /** Default ctor */
+${INDENT}    ${CLASS}();
+
+${INDENT}    /** Dtor */
+${INDENT}    ~${CLASS}();
+
+${INDENT}  public:
+${INDENT}    /**  Validate object in a boolean context. */
+${INDENT}    using base::SafeBool<${CLASS}>::operator bool_type;
+
+${INDENT}  private:
+${INDENT}    friend base::SafeBool<${CLASS}>::operator bool_type() const;
+${INDENT}    /**  Validate object in a boolean context. */
+${INDENT}    bool boolTest() const
+${INDENT}    {
+${INDENT}      /* !!! Perform Boolean logic here AND check implememtation of operator==!!!
+${INDENT}       * NOTE: SafeBool requires operator== otherwise equality is reduced to
+${INDENT}       *       ( bool(${CLASS}) == bool(${CLASS}) ).
+${INDENT}       */
+${INDENT}    }
+${INDENT}  public:
+${INDENT}    /** Implementation  */
+${INDENT}    class Impl;
+${INDENT}  private:
+${INDENT}    /** Pointer to implementation */
+${INDENT}    RWCOW_pointer<Impl> _pimpl;
+${INDENT}};
+
+${INDENT}/** \relates ${CLASS} Stream output */
+${INDENT}std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj );
+
+${INDENT}/** \relates ${CLASS} Verbose stream output */
+${INDENT}std::ostream & dumOn( std::ostream & str, const ${CLASS} & obj );
+
+${INDENT}/** \relates ${CLASS} */
+${INDENT}bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs );
+
+${INDENT}/** \relates ${CLASS} */
+${INDENT}inline bool operator!=( const ${CLASS} & lhs, const ${CLASS} & rhs )
+${INDENT}{ return !( lhs == rhs ); }
+
+$(nsclose)
+#endif // $INCLUDE_DEF
+EOF
+}
+
+######################################################################
+function genCC() {
+######################################################################
+cat <<EOF
+$(intro $CLASS_CC)
+#include <iostream>
+//#include "zypp/base/LogTools.h"
+
+#include "${INCLUDE_H}"
+
+using std::endl;
+
+$(nsopen)
+
+${INDENT}/** ${CLASS} implementation.
+${INDENT} */
+${INDENT}struct ${CLASS}::Impl
+${INDENT}{
+${INDENT}  friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
+${INDENT}  friend std::ostream & dumpOn( std::ostream & str, const Impl & obj );
+
+${INDENT}  public:
+
+${INDENT}  public:
+${INDENT}    /** Offer default Impl. */
+${INDENT}    static shared_ptr<Impl> nullimpl()
+${INDENT}    {
+${INDENT}      static shared_ptr<Impl> _nullimpl( new Impl );
+${INDENT}      return _nullimpl;
+${INDENT}    }
+${INDENT}  private:
+${INDENT}    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+${INDENT}    /** clone for RWCOW_pointer */
+${INDENT}    Impl * clone() const
+${INDENT}    { return new Impl( *this ); }
+${INDENT}};
+
+${INDENT}/** \relates ${CLASS}::Impl Stream output */
+${INDENT}inline std::ostream & operator<<( std::ostream & str, const ${CLASS}::Impl & obj )
+${INDENT}{ return str << "${CLASS}::Impl"; }
+
+${INDENT}/** \relates ${CLASS}::Impl Verbose stream output */
+${INDENT}inline std::ostream & dumpOn( std::ostream & str, const ${CLASS}::Impl & obj )
+${INDENT}{ return str << obj; }
+
+${INDENT}///////////////////////////////////////////////////////////////////
+${INDENT}//
+${INDENT}//    CLASS NAME : ${CLASS}
+${INDENT}//
+${INDENT}///////////////////////////////////////////////////////////////////
+
+${INDENT}${CLASS}::${CLASS}()
+${INDENT}  : _pimpl( Impl::nullimpl() )
+${INDENT}{}
+
+${INDENT}${CLASS}::~${CLASS}()
+${INDENT}{}
+
+${INDENT}std::ostream & operator<<( std::ostream & str, const ${CLASS} & obj )
+${INDENT}{ return str << *obj._pimpl; }
+
+${INDENT}std::ostream & dumpOn( std::ostream & str, const ${CLASS} & obj )
+${INDENT}{ return dumpOn( str, *obj._pimpl ); }
+
+${INDENT}bool operator==( const ${CLASS} & lhs, const ${CLASS} & rhs )
+${INDENT}{ return lhs._pimpl == rhs._pimpl || lhs._pimpl && rhs._pimpl && *lhs._pimpl == *rhs._pimpl; }
+
+$(nsclose)
+EOF
+}
+
+######################################################################
+######################################################################
+######################################################################
+
+genH >$OUT_CLASS_H
+genCC >$OUT_CLASS_CC
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fe5f576
--- /dev/null
@@ -0,0 +1,7 @@
+ADD_SUBDIRECTORY( autodoc EXCLUDE_FROM_ALL)
+
+INSTALL(
+  FILES locks.5
+  DESTINATION ${MANDIR}/man5
+)
+
diff --git a/doc/autodoc/CMakeLists.txt b/doc/autodoc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..696df97
--- /dev/null
@@ -0,0 +1,70 @@
+### ##################################################
+
+FIND_PROGRAM( DOT dot )
+IF ( NOT DOT )
+   MESSAGE( "dot not found: you should install graphviz." )
+   SET( HAVE_DOT NO )
+ELSE ( NOT DOT )
+   SET( HAVE_DOT YES )
+ENDIF ( NOT DOT )
+
+SET( ZYPP_SOURCE_DIR     ${LIBZYPP_SOURCE_DIR}/zypp )
+SET( ZYPP_TESTUTILS_DIR     ${LIBZYPP_SOURCE_DIR}/test/lib )
+SET( ZYPP_DOCINCLUDE_DIR ${LIBZYPP_SOURCE_DIR}/doc/autoinclude )
+SET( ZYPP_EXAMPLE_DIR    ${LIBZYPP_SOURCE_DIR}/examples )
+
+SET( DOXYGEN_INPUT    ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile )
+SET( DOXYGEN_OUTPUT   ${CMAKE_CURRENT_BINARY_DIR}/html/index.html )
+FILE( MAKE_DIRECTORY  ${CMAKE_CURRENT_BINARY_DIR}/html )
+
+### ##################################################
+
+CONFIGURE_FILE(
+   ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake
+   ${DOXYGEN_INPUT}
+   @ONLY
+   )
+
+### ##################################################
+
+ADD_CUSTOM_COMMAND (
+   OUTPUT  ${DOXYGEN_OUTPUT}
+   COMMAND ${CMAKE_COMMAND} -E echo_append "Building Documentation..."
+   COMMAND ${DOXYGEN} ${DOXYGEN_INPUT}
+   COMMAND ${CMAKE_COMMAND} -E echo "Done."
+   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+   DEPENDS ${DOXYGEN_INPUT}
+)
+
+ADD_CUSTOM_TARGET( doc ALL
+   DEPENDS ${DOXYGEN_OUTPUT}
+)
+ADD_DEPENDENCIES(doc zypp)
+
+ADD_CUSTOM_TARGET( doc_forced
+   COMMAND ${CMAKE_COMMAND} -E echo_append "Building Documentation..."
+   COMMAND ${DOXYGEN} ${DOXYGEN_INPUT}
+   COMMAND ${CMAKE_COMMAND} -E echo "Done."
+   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+   DEPENDS ${DOXYGEN_INPUT}
+)
+
+### ##################################################
+
+INSTALL( FILES
+   ${CMAKE_CURRENT_BINARY_DIR}/libzypp.doxytag
+   DESTINATION ${DOC_INSTALL_DIR}/libzypp/libzypp.doxytag
+)
+
+# We could use the thing below but it wont work with cmake older than 2.4.4
+FILE(GLOB docfiles ${CMAKE_CURRENT_BINARY_DIR}/html/*)
+INSTALL( FILES
+   ${docfiles}
+   DESTINATION ${DOC_INSTALL_DIR}/libzypp/html
+)
+
+#INSTALL( DIRECTORY
+#   ${CMAKE_CURRENT_BINARY_DIR}/html
+#   DESTINATION ${DOC_INSTALL_DIR}
+#)
+
diff --git a/doc/autodoc/Doxyfile.cmake b/doc/autodoc/Doxyfile.cmake
new file mode 100644 (file)
index 0000000..27afed3
--- /dev/null
@@ -0,0 +1,219 @@
+# Doxyfile 1.4.4
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = @PACKAGE@
+PROJECT_NUMBER         = @VERSION@
+OUTPUT_DIRECTORY       =
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = @CMAKE_SOURCE_DIR@
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+DISTRIBUTE_GROUP_DOC   = NO
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = @ZYPP_DOCINCLUDE_DIR@ @ZYPP_SOURCE_DIR@ @ZYPP_TESTUTILS_DIR@
+FILE_PATTERNS          = *.h *.hh *.hxx *.hpp *.h++ *.c *.cc *.cxx *.cpp *.c++ *.tcc *.hcc *.doc
+RECURSIVE              = YES
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXAMPLE_PATH           = @ZYPP_DOCINCLUDE_DIR@ @ZYPP_EXAMPLE_DIR@
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = YES
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             =
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       = @PACKAGE@.doxytag
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = NO
+HAVE_DOT               = @HAVE_DOT@
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = NO
+GROUP_GRAPHS           = NO
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = NO
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/doc/autoinclude/CodePitfalls.doc b/doc/autoinclude/CodePitfalls.doc
new file mode 100644 (file)
index 0000000..71d0cca
--- /dev/null
@@ -0,0 +1,60 @@
+namespace zypp
+{
+/** \page CodePitfalls Code Pitfalls - Frequently made mistakes
+
+\section TriBoolCompare Comparing TriBool values
+
+  Comparing two \ref TriBool values is not as easy as it might look like,
+  because the \c TriBool::operator== and \c TriBool::operator!= return
+  a \ref TriBool.
+
+  For example  is <tt>(indeterminate==indeterminate)</tt> not \c true, but
+  \c indeterminate. That's why the following snippet does not do what the
+  author expected:
+
+  \code
+    // buggy option class
+    struct Option
+    {
+      public:
+        Option()
+          : _value( indeterminate )
+        {}
+
+        bool get() const
+        { return ( _value == indeterminate ) ? true : bool(_value); }
+
+        void set( bool new_r )
+        { _value = new_r; }
+
+      private:
+        tribool _value;
+    };
+  \endcode
+
+  You find that \c get() returns \c false as long as the option is unset,
+  and not \c true as the code suggests. That's because <tt>(_value==indeterminate)</tt>
+  returns \c indeterminate.
+
+  \note Always use \c indeterminate(_value) to test whether a TriBools value is \c indeterminate:
+
+  \code
+        bool get() const
+        { return indeterminate( _value ) ? true : bool(_value); }
+  \endcode
+
+  \code
+        tribool _value;
+        ...
+        if ( indeterminate( _value ) )
+        { ... }              // indeterminate
+        else if ( _value )
+        { ... }              // true
+        else
+        { ... }              // false
+  \endcode
+<HR>
+
+
+*/
+}
diff --git a/doc/autoinclude/CodeSnippets.doc b/doc/autoinclude/CodeSnippets.doc
new file mode 100644 (file)
index 0000000..daa33a2
--- /dev/null
@@ -0,0 +1,122 @@
+/** \page CodeSnippets Code Snippets
+
+\section for_
+  If you prefer using iterator in a \c for loop, but dislike to figure out
+  the exact type of the iterator, you may find the  \c for_ macro convenient:
+  \code
+    #include "zypp/base/Easy.h"
+
+    for_( it, pool.byIdentBegin( kind, name ),
+              pool.byIdentEnd( kind, name ) )
+    {
+      PoolItem copy = *it;
+    }
+  \endcode
+  instead of:
+  \code
+    for ( ResPool::byIdent_iterator it = pool.byIdentBegin( kind, name ),
+          end = pool.byIdentEnd( kind, name );
+          it != end, ++it )
+    {
+      PoolItem copy = *it;
+    }
+  \endcode
+
+\section erase erase elements from containers
+  \verbatim
+  // //////////////////////////////////////////////////////////////////////
+  // Avoid buggy code, that tries to erase elements, matching a
+  // certain property from containers. Example:
+  //
+  //  for (ResStore::iterator it = store.begin(); it != store.end(); ++it)
+  //  {
+  //    _pool.erase(*it);
+  //  }
+  //
+  // Problem: Removing an element from a container invalidates (at least)
+  //          all iterators pointing to it. Thus after erasing *it, it is
+  //          no longer valid. ++it has UNDEFINED BEHAVIOUR.
+
+  // //////////////////////////////////////////////////////////////////////
+  // Loop based algorithms (differs depending on the kind of container)
+  // =====================
+  // //////////////////////////////////////////////////////////////////////
+
+  // //////////////////////////////////////////////////////////////////////
+  // Sequential container (vector string deque list): erase returns
+  // a valid iterator to the next element.
+  // //////////////////////////////////////////////////////////////////////
+
+    SeqContainer c;
+    for ( SeqContainer::iterator it = c.begin(); it != c.end(); /**/ )
+      {
+        if ( toBeRemoved( *it ) )
+          {
+            it = c.erase( it ); // valid next-iterator returned
+          }
+        else
+          ++it;
+      }
+
+
+  // //////////////////////////////////////////////////////////////////////
+  // Associative container (maps sets): erase returns void, but we can use
+  // postfix increment, as ONLY iterators to the eased object get invalid:
+  // //////////////////////////////////////////////////////////////////////
+
+    AssocContainer c;
+    for ( AssocContainer::iterator it = c.begin(); it != c.end(); /**/ )
+      {
+        if ( toBeRemoved( *it ) )
+          {
+            c.erase( it++ ); // postfix! Incrementing before erase
+          }
+        else
+          ++it;
+      }
+
+
+  // //////////////////////////////////////////////////////////////////////
+  // stl algorithms
+  // ==============
+  //
+  // In case toBeRemoved above is actually a function/functor.
+  // //////////////////////////////////////////////////////////////////////
+
+
+  // //////////////////////////////////////////////////////////////////////
+  // Sequential container (vector string deque): stl::remove_if,
+  // does not erase elements, they are just moved to the containers
+  // end, and an iterator to the 1st item to be 'removed' is returned.
+  // //////////////////////////////////////////////////////////////////////
+
+    SeqContainer c;
+    c.erase( stl::remove_if( c.begin(), c.end(), toBeRemoved ),
+            c.end() );
+
+
+  // //////////////////////////////////////////////////////////////////////
+  // Sequential container (list): The above works too, but list has a
+  // builtin remove/remove_if which is more efficient.
+  // //////////////////////////////////////////////////////////////////////
+
+    list c;
+    c.remove_if( toBeRemoved );
+
+
+  // //////////////////////////////////////////////////////////////////////
+  // Associative container (maps sets): Actually the loop above is the most
+  // efficient solution. There is an algorithm based solution, but it requires
+  // copying all elements not to be removed ;(
+  // //////////////////////////////////////////////////////////////////////
+
+    AssocContainer c;
+
+    AssocContainer keepItems;
+    stl::remove_copy_if( c.begin(), c.end(),
+                        stl::inserter( keepItems, keepItems.end() ),
+                        toBeRemoved );
+    c.swap( keepItems );
+  \endverbatim
+
+*/
\ No newline at end of file
diff --git a/doc/autoinclude/EnvironmentVariables.doc b/doc/autoinclude/EnvironmentVariables.doc
new file mode 100644 (file)
index 0000000..739ff84
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+
+\page zypp-envars Environment Variables
+
+\author Michael Andres <ma@suse.de>
+
+\section zypp-envars Environment Variables
+
+Note that for Boolean variables we usually test whether the variable exists and not for the assigned value.
+
+\subsection zypp-envars-config Location of the zypp.conf file.
+
+\li \c ZYPP_CONF=<PATH> Location of the zypp.conf file.
+
+\subsection zypp-envars-logging Variables related to logging
+
+\li \c ZYPP_LOGFILE=<PATH> Location of the logfile to write or \c - for stderr.
+\li \c ZYPP_FULLLOG=1 Even more verbose logging (usually not needed).
+\li \c ZYPP_LIBSAT_FULLLOG=1 Verbose logging when resolving dependencies.
+\li \c ZYPP_MEDIA_CURL_DEBUG=<0|1> Log http headers, if \c 1 also log server responses.
+
+\subsection zypp-envars-mediabackend Selecting the mediabackend to use.
+
+\li \c ZYPP_MULTICURL=0 Turn off multicurl (metalink and zsync) and fall back to plain libcurl.
+\li \c ZYPP_ARIA2C=1 Enable aria2c backend (deprecated).
+
+\subsection zypp-envars-plugin Variables related to plugins
+
+\li \c PLUGIN_DEBUG=1 Verbose logging from plugin framework.
+\li \c PLUGIN_TIMEOUT=<sec> Send/receive timeout for plugin communication.
+\li \c PLUGIN_SEND_TIMEOUT=<sec> Send timeout for plugin communication.
+\li \c PLUGIN_RECEIVE_TIMEOUT=<sec> Receive timeout for plugin communication.
+
+\subsection zypp-envars-misc Variables \b not for common use (test and debug)
+
+\li \c ZYPP_MODALIAS_SYSFS=<PATH> Use this instead of \c /sys to evaluate modaliases.
+\li \c ZYPP_COMMIT_NO_PACKAGE_CACHE=1
+\li \c ZYPP_TESTSUITE_FAKE_ARCH Never use this!
+\li \c ZYPPTMPDIR=<PATH>
+\li \c ZYPP_LOCKFILE_ROOT=<PATH> Hack to circumvent the currently poor --root support.
+\li \c ZYPP_PROFILING=1
+
+*/
\ No newline at end of file
diff --git a/doc/autoinclude/FeatureTest.doc b/doc/autoinclude/FeatureTest.doc
new file mode 100644 (file)
index 0000000..6e57b93
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+
+\page feature-test Testing for provided features.
+
+\author Michael Andres <ma@suse.de>
+
+<HR><!-- ====================================================================== -->
+\section intro Introduction
+
+The libzypp rpm package will indicate the presence of certain features by using special \c Provides:
+
+\verbatim
+  # Provides:     libzypp(FEATURE) = FEATURE_VERSION
+  Provides:       libzypp(code10) = 0
+\endverbatim
+
+Packages requiring a feature may use the corresponding \c Requires: in their .spec file.
+
+
+<HR><!-- ====================================================================== -->
+\section features Features
+<DL>
+
+  <DT>plugin</DT>
+  <DD><DL>
+    <DT>version 0</DT>
+    <DD>General ability to provide and handle plugins.</DD>
+  </DL></DD>
+
+  <DT>plugin:commit</DT>
+  <DD><DL>
+    <DT>version 0</DT>
+    <DD>\see \ref plugin-commit </DD>
+  </DL></DD>
+
+  <DT>plugin:services</DT>
+  <DD><DL>
+    <DT>version 0</DT>
+    <DD>\see \ref plugin-services </DD>
+  </DL></DD>
+
+  <DT>plugin:system</DT>
+  <DD><DL>
+    <DT>version 0</DT>
+    <DD>Plugin executed when system content change is detected (by now SUSE Manager/spacewalk only).</DD>
+  </DL></DD>
+
+  <DT>plugin:urlresolver</DT>
+  <DD><DL>
+    <DT>version 0</DT>
+    <DD>\see \ref plugin-url-resolver </DD>
+  </DL></DD>
+
+</DL>
+
+*/
diff --git a/doc/autoinclude/Mainpage.doc b/doc/autoinclude/Mainpage.doc
new file mode 100644 (file)
index 0000000..85613c3
--- /dev/null
@@ -0,0 +1,67 @@
+/** \mainpage Welcome to libzypp
+
+\section intro Introduction
+
+Welcome to the libzypp documentation.
+
+libzypp is the package management library that powers applications like YaST, zypper and the openSUSE/SLE implementation of PackageKit.
+
+\section whatis What is libzypp?
+
+libzypp provides all the functionality for a package manager:
+
+- An API for package repository management, supporting most common repository metadata formats and signed repositories.
+- An API for solving packages, products, patterns and patches (installation, removal, update and distribution upgrade operations) dependencies, with additional features like locking.
+- An API for commiting the transaction to the system over a rpm target. Supporting deltarpm calculation, media changing and installation order calculation.
+- An API for browsing available and installed software, with some facilities for programs with an user interface.
+- A suite of maintained solving testcases representing common and uncommon operations on Linux software management
+
+\section impl Implementation
+
+libzypp is implemented as a C++ library with experimental bindings available for python and ruby.
+
+\section otherfunc Other functionality offered by libzypp
+
+Most of libzypp functionality is implemented over components provided also by libzypp:
+
+- Media abstraction library offering a common interface over local files, http,ftp,iso,nfs and other protocols.
+
+\section thirdpfunc 3rd party software libzypp is based on
+
+Additionally, some functionality libzypp uses is based on 3rd party libraries:
+
+- SAT solver library, for dependency solving and repository data storage and querying
+- curl, that powers some protocols of the media abstraction component
+- boost, for misc. utilities like shared pointers, functors and algorithms.
+
+\section join Join the community
+
+Visit our web site at : http://en.opensuse.org/Libzypp or #zypp on irc.freenode.net
+
+\section license License
+
+\code
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+Copyright (C) 2000-2002 Ximian, Inc.
+Copyright (C) 2005 SUSE Linux Products GmbH
+
+ZYpp is licensed under the GNU General Public License version 2
+or later. The text of the GNU General Public License can be viewed at
+http://www.gnu.org/licenses/gpl.html
+
+As a special exception, you have permission to link this program
+with the following libraries and distribute executables, as long as you
+follow the requirements of the GNU GPL in regard to all of the
+software in the executable aside from the following libraries:
+- OpenSSL (http://www.openssl.org)
+\endcode
+
+*/
\ No newline at end of file
diff --git a/doc/autoinclude/Notes.doc b/doc/autoinclude/Notes.doc
new file mode 100644 (file)
index 0000000..f05be9d
--- /dev/null
@@ -0,0 +1,29 @@
+namespace zypp
+{
+/** \page Notes Notes on revisions
+
+\section V_6_8_0  6.8.0 - Deprecated methods deleted
+\li \c AuthData::setUserName           renamed to              AuthData::setUsername
+\li \c ResObject::installsize          renamed to              ResObject::installSize
+\li \c ResObject::size                 renamed to              ResObject::installSize
+\li \c ZConfig::productsPath           unused/unneeded/deleted
+\li \c PoolQuery::setMatchFiles                unused/unneeded/deleted
+\li \c PoolQuery::matchFiles           unused/unneeded/deleted
+\li \c PoolQuery::matchType            instead use             PoolQuery::matchMode().mode() (returns an enum now, no longer int)
+\li \c Patch::reboot_needed            renamed to              Patch::rebootSuggested
+\li \c Patch::affects_pkg_manager      renamed to              Patch::restartSuggested
+\li \c Patch::id                       unused/unneeded/deleted
+\li \c MediaSetAccess::provideOptionalFile     unused/unneeded/deleted
+\li \c Target::reset                   unused/unneeded/deleted
+\li \c Target::release                 renamed to              Target::targetDistribution
+\li \c MediaManager::attachDesiredMedia        renamed to              MediaManager::attach
+<HR>
+
+\section V_6_10_0  6.10.0 - Removed zypp::UpgradeStatistics
+Since \c Code-11 the distribution upgrade is computed by the satsolver and no longer by
+libzypp itself. For some reason zypp::UpgradeStatistics were still present in the interfaces,
+but actually no longer used. Neither as input nor as result struct. Now they are removed.
+<HR>
+
+*/
+}
diff --git a/doc/autoinclude/Plugin-Commit.doc b/doc/autoinclude/Plugin-Commit.doc
new file mode 100644 (file)
index 0000000..4083633
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+
+\page plugin-commit Commit plugin
+
+\author Michael Andres <ma@suse.de>
+
+<HR><!-- ====================================================================== -->
+\section intro Introduction
+
+This is a statefull plugin executed during \ref zypp::ZYpp::commit. At the beginning of a commit all plugins found in \c /usr/lib/zypp/plugins/commit are launched. The plugins will receive messages as commit proceeds. Unless otherwise specified messages received need to be confirmed by sending an \c ACC message. Sending back an \c ERROR message execution of the plugin will be canceled.
+
+If you have e.g. \c zypp-plugin-python installed a basic commit plugin could look like this:
+
+\verbatim
+#!/usr/bin/env python
+#
+# zypp commit plugin
+#
+import os
+import sys
+from zypp_plugin import Plugin
+
+class MyPlugin(Plugin):
+
+  def PLUGINBEGIN(self, headers, body):
+    # commit is going to start.
+    self.ack()
+
+  def PLUGINEND(self, headers, body):
+    # commit ended
+    self.ack()
+
+plugin = MyPlugin()
+plugin.main()
+\endverbatim
+
+\see \ref plugin-writing
+
+<HR><!-- ====================================================================== -->
+\section pluginbegin PLUGINBEGIN
+\verbatim
+PLUGINBEGIN
+
+^@
+\endverbatim
+Sent as 1st message after the plugin was launched. Prepare your plugin and send an \c ACC message when you are done. Commit will start after all plugins are initialized.
+
+
+<HR><!-- ====================================================================== -->
+\section pluginend PLUGINEND
+\verbatim
+PLUGINEND
+
+^@
+\endverbatim
+This message is sent at the end of commit. You should receive this message even if commit was aborted by some unexpected exception.
+
+*/
diff --git a/doc/autoinclude/Plugins.doc b/doc/autoinclude/Plugins.doc
new file mode 100644 (file)
index 0000000..dcde6ae
--- /dev/null
@@ -0,0 +1,202 @@
+/**
+
+\page zypp-plugins Extending ZYpp: Plugins and Hooks
+
+\author Duncan Mac-Vicar P. <dmacvicar@suse.de>
+\author Michael Andres <ma@suse.de>
+
+<HR><!-- ====================================================================== -->
+\section plugins-intro Introduction
+
+Plugins allow to extend the ZYpp package manager without the need to change
+code. Plugins are designed as external programs so that they can be written in any language.
+
+\section plugin-protocols Plugin protocols
+
+Depending on the complexity and need for future extension, plugins talk
+to ZYpp using two methods.
+
+\subsection plugin-protocol-stateless Stateless
+
+This type of plugin receive input reading the standard input, and answer ZYpp writing to the standard output. This means the plugin is executed once per hook and they are stateless (unless the plugin keeps the state out of process).
+
+\subsection plugin-protocol-stateful Stateful
+
+This type of plugin is called by ZYpp and a conversation using a simple protocol. The protocol is based on STOMP http://stomp.codehaus.org/Protocol (Streaming Text Orientated Messaging Protocol). Messages (called "frames") look like the following:
+
+\verbatim
+COMMAND
+param1:val1
+param2:val2
+...
+Thus a COMMAND hollowed by key:value header lines
+and a multiline body separated from header
+by an empty line and terminated by NUL.
+^@
+\endverbatim
+
+\note ^@ is a null (control-@ in ASCII) byte.
+
+<HR><!-- ====================================================================== -->
+\section plugin-writing Writing plugins
+
+A python class is offered as a helper to handle communication between the plugin and
+libzypp. It is available by installing \c zypp-plugin-python.
+
+\verbatim
+zypper in -C zypp-plugin-python
+\endverbatim
+
+The plugin must define a method for each message it may receive from \ref libzypp. Message header list and body are passed to the method as arguments.
+
+\verbatim
+#!/usr/bin/env python
+#
+# zypp plugin
+#
+import os
+import sys
+from zypp_plugin import Plugin
+
+class MyPlugin(Plugin):
+
+  def SAMPLE( self, headers, body ):
+    # called upon revieving a SAMPLE message
+    ...
+    if ( ok ):
+      self.ack()
+    else:
+      self.error( { "aheader":"header value" }, "body\n(multiline text ok)" )
+
+plugin = MyPlugin()
+plugin.main()
+\endverbatim
+
+Two methods \c ack and \c error are available to send back a standard \c ACK or \c ERROR message. You may optionally pass header entries and a multiline body. For sending custom messages use \c answer, which takes the command name as 1st argument, followed by optional header entries and a multiline body.
+
+Plugin closes \c stdin and exits when receiving a \c _DISCONNECT message. Upon an \c ACK response to \c _DISCONNECT libzypp will not attempt to kill the script. An exit value different than \c 0 may be set via an \c 'exit' header in \c ACK.
+
+\verbatim
+  def _DISCONNECT( self, headers, body ):
+    sys.stding.close()
+    self.ack( {'exit':'99'}, 'Famous last words.' )
+\endverbatim
+
+<HR><!-- ====================================================================== -->
+\section plugin-toc Supported plugins
+
+\subpage plugin-commit Escort installation of packages
+
+\ref plugin-services
+
+\ref plugin-url-resolver
+
+
+<HR><!-- ====================================================================== -->
+\section plugin-services Service plugins
+
+ZYpp will find a subscribed service for each executable located in /usr/lib/zypp/plugins/services and will set the alias as the executable name. The type will be set to "plugin".
+
+Service plugins are used to provide a client a list of repositories from a central location where more complex logic is needed than a simple remote xml index accessible via http (in that case you can use \ref services-remote "Remote Services").
+
+\subsection plugin-services-example1 Example: Management console
+
+You have a custom mass management application that controls the repositories each client muss have. While you can use \ref services-remote "Remote Services" and subscribe each client to an url in the server providing a dynamic repoindex.xml based on the client, if you need to pass custom information in order for the server to calculate the repository list (e.g. number of CPUs) or the protocol that the client and the server and client speak is proprietary, you may implement the service locally, as an executable that will be installed in each client /usr/lib/zypp/plugins/services directory (it may be installed from a package).
+
+\subsection plugin-services-how How to write a Services plugin
+
+When listing services, ZYpp will find each plugin service as a subscribed service.
+
+Service plugins are Stateless. When services are refreshed, ZYpp will call each plugin and the repository list will be taken from the output of the script in INI format (same as how they are stored in /etc/zypp/repos.d).
+
+For our example:
+
+\verbatim
+# example plugin output
+# comments are ignored
+[repo1]
+name=Base repository
+summary=Standard repository
+baseurl=http://server.com/repo1
+type=rpm-md
+
+# multiple repositories can be present in the output
+
+[repo2]
+...
+
+\endverbatim
+
+The repositories will be added on service refresh with the alias present in the output, prefixed by the service alias (in this case, the executable name).
+
+
+<HR><!-- ====================================================================== -->
+\section plugin-url-resolver Url Resolver plugins
+
+Url resolver plugins convert urls of scheme "plugin" into the output of the plugin named $name using the protocol. Thanks to the protocol, each header returned is also added as HTTP headers. The current protocol sequence is:
+
+ZYpp sees a repository whose url has the format:
+
+\verbatim
+plugin:foo?param1=val1&param2=val2
+\endverbatim
+
+ZYpp tries to executa a plugin named foo (in /usr/lib/zypp/plugins/urlresolver) and calla it with the following protocol:
+
+\verbatim
+   RESOLVEURL
+   param1:val1
+   param2:val2
+   ...
+   ^@
+\endverbatim
+
+The plugin answers:
+
+\verbatim
+   RESOLVEDURL:
+   header1:val1
+   header2:val2
+   ...
+   http://realurl.com?opts=vals
+   ^@
+\endverbatim
+
+And this url is used instead.
+
+\subsection plugin-urlresolver-example Example
+
+You have a repository with url:
+
+   plugin:lan
+
+The script looks which distribution you have installed, and via SLP finds the right repositories in the lan and selects the update one and returns it url. But in addition, it adds a header with the update status that can be collected on the server side.
+
+This type of plugin can be combined with service plugins, because a local service could return a list of repos like this:
+
+\verbatim
+  [distro]
+  name=Distribution repository
+  baseurl=plugin:lan?repo=distro
+  [update]
+  name=Update repository
+  baseurl=plugin:lan?repo=update
+\endverbatim
+
+\note
+In this example, the service plugin could have inmediately resolved the urls and returned http://realurl, but the url resolver allows also to add HTTP headers to the request.
+
+<HR><!-- ====================================================================== -->
+\section plugins-impl Developers: Implementation
+
+Plugins are implemented in the following classes:
+
+- \ref zypp::PluginScript (Plugin as an external program)
+- \ref zypp::PluginScriptException
+- \ref zypp::PluginFrame (Message for the stateful protocol)
+- \ref zypp::PluginFrameException
+- \ref zypp::repo::PluginServices (Finds Service plugins)
+
+The plugins default location is obtained from \ref zypp::ZConfig::pluginsPath()
+
+*/
\ No newline at end of file
diff --git a/doc/autoinclude/README b/doc/autoinclude/README
new file mode 100644 (file)
index 0000000..cc16030
--- /dev/null
@@ -0,0 +1,54 @@
+All .doc files here will be parsed and included in the autodocs generated
+by doxygen (see http://www.doxygen.org/). All the other files here (mostly
+below notes) are available to be vervatim included in some documentation
+block using:
+
+    /** \include somefile
+    */
+  or
+    /** \example
+    */
+
+
+The .doc files here will mostly provide:
+
+- The content of the main index page (defined in Mainpage.doc).
+
+- Other related documentation pages:
+
+  /*! \page page1 A documentation page
+    Leading text.
+    \section sec An example section
+    This page contains the subsections \ref subsection1 and \ref subsection2.
+    For more info see page \ref page2.
+    \subsection subsection1 The first subsection
+    Text.
+    \subsection subsection2 The second subsection
+    More text.
+  */
+
+  /*! \page page2 Another page
+    Even more info.
+  */
+
+Those pages will per default appear in the documentations 'Related Pages'
+section, unless you refer to them from within soome other page by using
+'\subpage':
+
+  /** \mainpage A simple manual
+
+  Some general info.
+
+  This manual is divided in the following sections:
+  - \subpage intro
+  - \subpage advanced "Advanced usage"
+  */
+
+
+- Documentation for a group of classes defined by \ingroup:
+
+  /*! \defgroup g_Resolvable Resolvable Objects
+    Some explanation.
+  */
+
+- And whatever else we don't want to keep in the header files.
diff --git a/doc/autoinclude/Services.doc b/doc/autoinclude/Services.doc
new file mode 100644 (file)
index 0000000..e48e9ab
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+
+\page zypp-services Services
+
+\author Duncan Mac-Vicar P. <dmacvicar@suse.de>
+
+Services provide a list of repositories. So when the service is refreshed, the repositories belonging to this service are synced.
+
+\section service-types Classes of services
+
+There are two classes of services:
+
+\subsection services-remote Remote Services
+
+Remote services are a subscription to a remote server, that provide a list of repositories that will be synced with the previous list of repositories for the same service.
+
+By default, the remote services list is stored in the \ref /etc/zypp/services.d directory in standard INI files. Each file can contain more than one service.
+
+The format is the following:
+
+\verbatim
+[servicealias]
+name=My Service
+url=http://customers.maintenance.company.com/service
+enabled=true
+autorefresh=true
+type=nu
+\endverbatim
+
+The type "nu" stands for Novell Update, which gives a customer the repositories it is entitled to access. The type "ris" is an extended version of the "nu" type.
+Additionally, the file could contain "repostoenable" and "repostodisable" entries, which contain a list of repository aliases to enable or disable on the next refresh. This line is modified by other programs to force enabling or disabling a certain repository.
+
+From the remote side, the service url needs to provide a repoindex.xml file with the repository list:
+
+\verbatim
+<repoindex>
+  <repo url="http://foo.com/repo1" alias="a repository 1"/>
+  <repo url="http://foo.com/repo2" alias="a repository 2"/>
+...
+</repoindex>
+\endverbatim
+
+\subsection services-plugin Plugin Services
+
+\ref plugin-services "Plugin services" are simple scripts that return a list of repositories. They are installed by packages. For each script installed ZYpp will “see” a service of type “plugin”. When you refresh the service, the repositories this script returns will be added (or removed and kept in sync if they change afterwards).
+
+\section service-refresh Refreshing services
+
+Using zypper, you can refresh services by executing
+
+\verbatim
+zypper refs
+\endverbatim
+
+The repositories that are listed in the service will be added, using the reposotiy alias specified in the service index prefixed by the service alias: e.g. "myservice:myrepository". Repositories that vanished from the service will be automatically removed.
+
+\section service-examples Example usecases
+
+\subsection services-usecase-1 Usecase #1: The project with multiple repositories and layers
+
+Imagine the following usecase (with this one I am using some real request from our KDE guys)
+
+The build service provides a KDE4 repository. Which in turn requires the Qt4 repository, because is built on openSUSE 11.0 + the new Qt4 repo.
+
+When looking at this problem, repository dependencies is what comes to head in the first place. But forget about them. If package dependencies are complicated right now, imagine adding a secondary (and duplicated) layer of information. Packages already know their dependencies.
+
+Now imagine our KDE guys can provide an URL, which you add to zypper. And this url returns a dynamic list of repositories. And zypper adds and remove repositories based on the information returned by this url on every refresh.
+
+\subsection services-usecase-2 Usecase #2: Update repositories based on the customer
+
+This is actually where services where originated. Services were present in Novell ZenWorks. How it works?
+
+The service url nu.novell.com is added to the system. But in this url also a customer id is present as a http username. When you registered, Novell knows the product this customer is linked to, and can return a dynamic list of update repositories based on the customer preferences, products and entitlements and the customer does not need to keep them in sync.
+
+Now that we don’t have Zenworks in the base system, we still want this cool functionality for our customers.
+
+Technically, this even allows us to offer hotfixes to L3 supported customers on the fly!
+
+\subsection services-usecase-3 Usecase #3: Dynamic repository collections
+
+You are a build service user, and you have an account, and of course you have a list of watched projects you are interested to. What if you could keep your system repositores in sync with your watched project list.
+
+Or what if the build service could offer a service based on keywords or other data: like http://build.opensuse.org/services/mostpopular/repo/repoindex.xml would contain dynamically the 15 most popular repositories. You add that service, and then ZYpp does the work for you of adding new popular repositories, and remove the old ones.
+
+\section service-impl Developers: Implementation
+
+Services are implemented in the following classes:
+
+- \ref zypp::repo::ServiceType (Service types enumeration)
+- \ref zypp::parser::ServiceFileReader (.service file parser)
+- \ref zypp::ServiceInfo Information about a service (.service file data)
+- \ref zypp::repo::PluginServices (Services as plugins)
+- \ref zypp::parser::RepoindexFileReader (NU and RIS remote service index XML parser)
+- \ref zypp::repo::ServiceRepos (Repositories in a service)
+
+
+*/
\ No newline at end of file
diff --git a/doc/autoinclude/Testcases.doc b/doc/autoinclude/Testcases.doc
new file mode 100644 (file)
index 0000000..da545d0
--- /dev/null
@@ -0,0 +1,121 @@
+/** \page Testcases Writing and tunning testcases
+
+\section Introduction
+
+ZYpp has a suite of tests located in under test/ directory of the source tree.
+
+Right now, tests are grouped into
+
+- media : tests related to downloading and the http/ftp/nfs/iso abstraction layer
+- parser : tests related to classes that offer file format parsing
+- repo : tests related to repository handling
+- sat : tests related to the sat-solver integration
+- zypp : tests related to the main libzypp classes and APIs
+
+Tests are written using boost test library.
+
+- <a href="http://www.boost.org/doc/libs/1_36_0/libs/test/doc/html/index.html">Boost Test Library</a>
+- <a href="http://www.boost.org/doc/libs/1_36_0/libs/test/doc/html/utf.html">The Unit Test Framework</a>
+- <a href="http://www.boost.org/doc/libs/1_36_0/libs/test/doc/html/utf/testing-tools/reference.html">The UTF testing tools reference</a>
+
+\section Anatomy of a ZYpp testcase
+
+The file should be in one of the described groups, and by general rule it is named ClassName_test.cc where ClassName is the name of the class or module the test covers.
+
+Data and fixtures are stored in data/ directories in each test group. However groups may use and reference data from other test groups. The macro \ref TESTS_SRC_DIR is defined as the tests/ directory located in libzypp source directory. You can build the paths to the data/fixtures using that macro.
+
+A simple testcase:
+
+\code
+#include "zypp/Date.h"
+#include <boost/test/auto_unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE(date_test)
+{
+  std::string format = "%Y-%m-%d %H:%M:%S";
+  std::string date = "2009-02-14 00:31:30";
+  BOOST_CHECK_EQUAL(zypp::Date(date,format).form(format), date);
+}
+\endcode
+
+\section Building and running the testsuite
+
+- Build the testsuite
+
+\verbatim
+$ cd build
+$ cmake -DCMAKE_INSTALL_PREFIX=/prefix ..
+$ cd tests
+$ make
+\endverbatim
+
+- Run a simple test
+
+\verbatim
+$ zypp/Date_test
+Running 1 test case...
+
+*** No errors detected
+\endverbatim
+
+- Run all tests
+
+\verbatim
+$ ctest .
+\endverbatim
+
+\section
+
+
+\verbatim
+  - added tests/data/openSUSE-11.1 containing raw susetags metadata.
+  Keeping .solv files in svn is somewhat inconvenient, as you must rebuild them
+  if something in satsolver changes.
+\endverbatim
+
+\verbatim
+  - added  tests/include as location for includes that might be used in multiple
+  testcases.
+\endverbatim
+
+\verbatim
+  - added tests/include/TestSetup.h to ease building a test environment below
+  some tempdir. Currently supports easy setup of Target, RepoManager and
+  loading data (raw metadata and .solv files) into the pool.
+
+  That's how it currently looks like:
+
+          #include "TestSetup.h"
+
+          BOOST_AUTO_TEST_CASE(WhatProvides)
+          {
+            TestSetup test( Arch_x86_64 );  // use x86_64 as system arch
+            test.loadTarget(); // initialize and load target
+            test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
+
+  This is all you need to setup Target, RepoManager below some temp directory
+  and load the raw metadata into the pool.
+
+  In case you want to setup the system below some fix directory, use:
+
+          TestSetup test( "/tmp/mydir", Arch_x86_64 );
+
+  You directory is used as it is and not removed at the end.
+\endverbatim
+
+\verbatim
+  - Added support for loading helix files e.g. from testcases. This is what
+    you need to load all repos from a solver testcase into the pool:
+
+       #include "TestSetup.h"
+
+       BOOST_AUTO_TEST_CASE(test)
+        {
+               TestSetup test( Arch_x86_64 );
+               test.loadTestcaseRepos( "/suse/ma/BUGS/153548/YaST2/solverTestcase" );
+
+\endverbatim
+
+\section References
+
+*/
diff --git a/doc/autoinclude/g_BOOST.doc b/doc/autoinclude/g_BOOST.doc
new file mode 100644 (file)
index 0000000..df2fa86
--- /dev/null
@@ -0,0 +1,12 @@
+/** \namespace boost \ref BOOST
+*/
+/** \defgroup BOOST Boost libraries.
+
+Boost provides free peer-reviewed portable C++ source libraries.
+Several \c ::boost names were dragged or typedefed into
+namespace \c ::zypp.
+\see http://www.boost.org/
+
+*/
+
+
diff --git a/doc/autoinclude/g_SATSOLVER.doc b/doc/autoinclude/g_SATSOLVER.doc
new file mode 100644 (file)
index 0000000..97e67f0
--- /dev/null
@@ -0,0 +1,7 @@
+/** \namespace zypp::sat \ref SATSOLVER
+*/
+/** \defgroup SATSOLVER Satsolver interface
+
+Interface to sat-pool and sat-solver.
+
+*/
diff --git a/doc/autoinclude/groups.doc b/doc/autoinclude/groups.doc
new file mode 100644 (file)
index 0000000..84c0a69
--- /dev/null
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////
+// Short or empty group definitions. Move them to a sepearate .doc file if
+// more text is provided.
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_EnumerationClass  Enumeration Class
+*/
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_RAII RAII solutions
+  \see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
+*/
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_CRTP CRTP solutions
+  \see http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
+*/
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_BackenSpecific Backend Specific
+*/
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_Functor Filters and Functors
+*/
+////////////////////////////////////////////////////////////////////////////////
+/*! \defgroup g_Algorithm Algorithms
+*/
+////////////////////////////////////////////////////////////////////////////////
diff --git a/doc/autoinclude/notes/n_ResPool_nomorenameiter b/doc/autoinclude/notes/n_ResPool_nomorenameiter
new file mode 100644 (file)
index 0000000..2be1405
--- /dev/null
@@ -0,0 +1,71 @@
+
+I tagged the byName iterator as deprecated, so the compiler tells you
+where it is used. Please review the code and check whether byIdent is
+an appropriate replacement.
+
+
+--
+The 'old' zypp pool internally maintained a byName index, so it was a
+fast way to iterate the pool by name and filter by kind to visit e.g. 
+all packages named foo:
+
+    invokeOnEach( pool.byNameBegin( name ), pool.byNameEnd( name ),
+                  resfilter::ByKind( kind ),
+                 action() );
+
+    for_( it, pool.byNameBegin( name ), pool.byNameEnd( name ) )
+    {
+      if ( (*it)->kind() == kind )
+      {
+       ...
+      }
+    }
+
+This is no longer true.
+
+
+
+In contrary, byName now is a 'quite expensive' iteration. It's faster to 
+rewrite these loops using byIdent (and no filter):
+
+    invokeOnEach( pool.byIdentBegin( kind, name ), 
+                  pool.byIdentEnd( kind, name ),
+                 action() );
+
+    for_( it, pool.byIdentBegin( kind, name ), pool.byNameEnd( kind, name ) )
+    {
+      ...
+    }
+
+
+How to construct the byIdent iterator:
+
+    ResPool::byIdentBegin( poolItem )      // using this poolItems kind and name
+    ResPool::byIdentBegin( kind, name )    // explicit kind and name
+    ResPool::byIdentBegin<Package>( name ) // or templated kind
+
+
+
+--
+
+If you prefer using iterator in a for' loop, but dislike to figure out 
+the exact type of the iterator, you may find the 'for_' macro convenient:
+
+    #include "zypp/base/Easy.h"
+
+    for_( it, pool.byIdentBegin( kind, name ), 
+              pool.byIdentEnd( kind, name ) )
+    {
+      PoolItem copy = *it;
+    }
+
+
+instead of:
+
+    for ( ResPool::byIdent_iterator it = pool.byIdentBegin( kind, name ),
+                                    end = pool.byIdentEnd( kind, name );
+          it != end, ++it )
+    {
+      PoolItem copy = *it;
+    }
+
diff --git a/doc/downloaders-mediaset.txt b/doc/downloaders-mediaset.txt
new file mode 100644 (file)
index 0000000..715e183
--- /dev/null
@@ -0,0 +1,4 @@
+
+Downloaders and MediaSet
+========================
+dmacvicar@suse.de
diff --git a/doc/libzypp.zargo b/doc/libzypp.zargo
new file mode 100644 (file)
index 0000000..38595b7
Binary files /dev/null and b/doc/libzypp.zargo differ
diff --git a/doc/locks.5 b/doc/locks.5
new file mode 100644 (file)
index 0000000..ab89790
--- /dev/null
@@ -0,0 +1,199 @@
+.TH "locks" "5" "4.25.0" "libzypp" "System Tools"
+.SH "NAME"
+.LP
+locks - libzypp locking file
+
+.SH "DESCRIPTION"
+.LP
+The file \fI/etc/zypp/locks\fR is read by libzypp at startup if
+\fIzypp.conf\fR allows it. The entries are used for initial locking of
+packages. Locking a package means not allowing to install or uninstall
+it. Valid entries are
+.TP
+\fI attribute\fR\fB:\fR \fIvalue\fR
+Where attributes and their values are described below.
+.br
+Locks are separated by empty lines.
+
+.SH "ATTRIBUTES"
+.LP
+All attributes are lower-case.
+
+.TP
+.B repo
+specifies repository restriction. Only alias is accepted.
+.br
+By default all repositories match.
+
+.TP
+.B type
+resolvable type restriction
+.br
+The values can be \fBpackage\fR, \fBpatch\fR, \fBpattern\fR, \fBproduct\fR and \fBsrcpackage\fR.
+.br
+By default all types match.
+
+.TP
+.B case_sensitive
+if strings are matched case sensitive.
+The values are \fBtrue\fR, \fBfalse\fR, \fBon\fR, \fBoff\fR.
+.br
+The default is case insensitive.
+
+.TP
+.B install_status
+status of object. Possible states are \fBinstalled\fR,
+\fBnot-installed\fR and \fBall\fR. If more install statuses are
+specified then the last one is used.
+.br
+The values are \fBinstalled\fR for all packages which are installed, \fBnon-installed\fR for packages which can be installed or reinstalled and \fBall\fR for both.
+.br
+The default is \fBall\fR.
+
+.TP
+.B match_type 
+type of string matching in values. Does not affect \fBtype\fR and \fBrepo\fR which must be specified exactly.
+.br
+The values are \fBexact\fR, \fBsubstring\fR, \fBregex\fR for regular
+expressions, \fBglob\fR for matching as on the command line, and \fBword\fR.
+.br
+The default is \fBsubstring\fR.
+
+.TP
+.B query_string
+String to be matched in multiple attributes. Should be restricted by
+another attribute with empty value ( it is recommended, because without restriction expect some performance problems ). 
+
+.TP
+.B version
+Restrict the lock only to some versions. It contains two parts: an
+optional operator and the version.
+.br
+The operator is \fB==\fR,\fB!=\fR,\fB<\fR,\fB>\fR,\fB<=\fR,\fB>=\fR. If operator is not specified then \fB==\fR is used.
+.br
+The version has the format 
+.RB [ epoch: ] version [ -release ].
+.br
+Example: version: < 0:0.11.4-2
+
+.TP
+.B solvable_name 
+name of object (e.g. zypper)
+
+.TP
+.B solvable_summary
+summary of object
+
+.TP
+.B solvable_arch
+architecture of object (e.g. x86_64, i586) 
+
+.TP
+.B solvable_description
+description of object 
+
+.TP
+.B solvable_eula 
+license text of objects which request accepting license by user
+
+.TP
+.B solvable_license 
+license of package (only for package) (e.g. GPL2)
+
+.TP
+.B solvable_keywords 
+keywords which specify package (only for package)
+
+.TP
+.B solvable_authors
+authors of package (only for package)
+
+.TP
+.B solvable_group
+package group (only for package) (e.g. Development/Tools/Version Control )
+
+.TP
+.B update_reference_type
+reference for update (e.g. bugzilla,cve) (only for patches)
+
+.SH "EXAMPLES"
+.LP
+
+.TP
+.B Exact Package
+This is the way YaST UI does it. Lock k3b (e.g. you don't want to update it).
+.br
+-----locks-----
+.br
+type: package
+.br
+solvable_name: k3b
+.br
+match_type: exact
+.br
+case_sensitive: on
+
+.TP
+.B Package Wildcard
+This is the way "zypper addlock cross-*-gcc-icecream-backend" does it.
+.br
+-----locks-----
+.br
+type: package
+.br
+solvable_name: cross-*-gcc-icecream-backend
+.br
+match_type: glob
+.br
+case_sensitive: on
+
+.TP
+.B Versioned Lock
+Do not install new GCC. This format is used when converting from the
+openSUSE-10.3 lock format.
+.br
+-----locks-----
+.br
+solvable_name: gcc
+.br
+match_type: glob
+.br
+version: > 4.2
+
+.TP
+.B Anything named KDE
+Locks everything which contains kde in the name.
+.br
+-----locks-----
+.br
+solvable_name: kde
+
+.TP
+.B Anything mentioning KDE
+Locks everything which contains kde in the name, summary, or description.
+.br
+-----locks-----
+.br
+query_string: kde
+.br
+solvable_name:
+.br
+solvable_summary:
+.br
+solvable_description:
+
+.SH "HOMEPAGE"
+
+This manual page only covers the most important attributes. The
+complete list is available at
+http://en.opensuse.org/Libzypp/Locksfile
+
+.SH "AUTHORS"
+.LP
+Josef Reidinger <jreidinger@suse.cz>
+.br
+Manual page contributions by Martin Vidner <mvidner@suse.cz>.
+
+.SH "SEE ALSO"
+.LP
+zypper(8)
diff --git a/doc/solverstates.dot b/doc/solverstates.dot
new file mode 100644 (file)
index 0000000..8bdc0ae
--- /dev/null
@@ -0,0 +1,37 @@
+digraph solver_states {
+
+  size="8,5";
+
+  graph [fontsize=14];
+  edge [fontsize=12];
+  node [fontsize=12];
+  node [shape=doublecircle]; Installed Uninstalled;
+  node [shape=circle]; Satisfied Incomplete;
+  node [shape=box];
+//  ranksep = 1.5;
+//  rankdir = LR;
+//  nodesep = 0.5;
+  edge [style="setlinewidth(1)"];
+
+  Installed [label="Installed"];
+  Uninstalled [label="Uninstalled"];
+  Satisfied [label="Satisfied"];
+  Incomplete [label="Incomplete"];
+
+  Uninstalled -> To_be_installed [ label ="install,explicit"];
+  Uninstalled -> To_be_installed [ label ="install,implicit"];
+  Installed -> To_be_removed [ label ="remove,explicit"];
+  Installed -> To_be_removed [ label ="remove,implicit"];
+  To_be_installed -> Installed [ label ="commit"];
+  To_be_removed -> Uninstalled [ label ="commit"];
+
+  Uninstalled -> Satisfied [ label="establish,good" ];
+  Uninstalled -> Incomplete [ label="establish,bad" ];
+
+  Installed -> Incomplete [ label="establish,bad" ];
+
+  Incomplete -> To_be_installed [ label="resolve" ];
+  Incomplete -> To_be_removed [ label="remove" ];
+
+  Satisfied -> Installed [ label="install" ];
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e7ca22a
--- /dev/null
@@ -0,0 +1,14 @@
+## ############################################################
+
+FILE( GLOB ALLCC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" )
+STRING( REPLACE ".cc" ";" APLLPROG ${ALLCC} )
+FOREACH( loop_var ${APLLPROG} )
+  ADD_EXECUTABLE( ${loop_var}
+    ${loop_var}.cc
+  )
+  TARGET_LINK_LIBRARIES( ${loop_var}
+    zypp
+  )
+ENDFOREACH( loop_var )
+
+## ############################################################
diff --git a/examples/COW_debug.cc b/examples/COW_debug.cc
new file mode 100644 (file)
index 0000000..e7cab49
--- /dev/null
@@ -0,0 +1,230 @@
+#include <iosfwd>
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+// MyClass.h
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /** Simple class using a RWCOW_pointer to implementation.
+   *
+   * MyClass maintains an int value, manipulated via get/set.
+   * RWCOW_pointer provides the 'copy on write' functionality.
+  */
+  class MyClass
+  {
+  public:
+    /** Implementation (public, but hidden in MyClass.cc) */
+    struct Impl;
+
+  public:
+    MyClass( int val = 0 );
+
+    int get() const;
+
+    void set( int val );
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+}
+///////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////
+// MyClass.cc
+///////////////////////////////////////////////////////////////////
+#include <zypp/base/Debug.h>
+
+using std::endl;
+
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  //
+  namespace debug
+  {
+    /** Forward decl. Implemented after MyClass::Impl is defined,
+     * if you want to dynamic_cast TraceCAD<MyClass::Impl> back into
+     * MyClass::Impl. Otherwise you could implement it here.
+     */
+    template<>
+      void traceCAD( TraceCADBase::What what_r,
+                     const TraceCAD<MyClass::Impl> & self_r,
+                     const TraceCAD<MyClass::Impl> & rhs_r );
+  }
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  /** Implementation of MyClass providing the int value.
+   *
+   * To debug via TraceCAD, simply derive. Per default TraceCAD
+   * drops loglines. In this example we overload traceCAD<Impl>,
+   * to do a bit more stuff.
+  */
+  struct MyClass::Impl : public debug::TraceCAD<Impl>
+  {
+    Impl( int val = 0 )
+    : _value( val )
+    {}
+
+    int _value;
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  inline std::ostream & operator<<( std::ostream & str, const MyClass::Impl & obj )
+  { return str << "MyClass::Impl[" << &obj << "] value: " << obj._value; }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  // class MyClass
+  ///////////////////////////////////////////////////////////////////
+
+  MyClass::MyClass( int val )
+  : _pimpl( new Impl( val ) )
+  {
+    // e.g _PING to indicate ctor is done.
+    _pimpl->_PING();
+  }
+
+  /** Impl access via 'operator->() const' (readonly) */
+  int MyClass::get() const
+  { return _pimpl->_value; }
+
+  /** Impl access via 'operator->()' (write, creates a copy iff shared) */
+  void MyClass::set( int val )
+  { _pimpl->_value = val; }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  namespace debug
+  {
+    /** Performs all possible casts of self_r/rhs_r back into
+     * MyClass::Impl.
+     *
+     * CTOR,DTOR: self_r can't be casted, because MyClass::Impl
+     * is not yet constructed (TraceCAD is base class), or already
+     * deleted. rhs_r is meaningless (==self_r).
+     *
+     * COPYCTOR: self_r can't be casted (not yet constructed).
+     * rhs_r can be casted into MyClass::Impl. It's the object
+     * we copy from.
+     *
+     * ASSIGN: self_r and rhs_r can be casted. If MyClass::Impl
+     * defines an operator==, you have to alter the code to call
+     * TraceCAD::operator=. Otherwise it won't be triggered.
+     *
+     * PING: self_r can be casted, rhs_r is meaningless (==self_r).
+     * You have to alter MyClass::Impl to call '_PING()' to recieve
+     * the trigger. The only purpose is to provide an easy way to
+     * trigger some action, without much changes to the original code.
+     * Call _PING there and perfrorm the action here, if possible.
+    */
+    template<>
+      void traceCAD( TraceCADBase::What what_r,
+                     const TraceCAD<MyClass::Impl> & self_r,
+                     const TraceCAD<MyClass::Impl> & rhs_r )
+      {
+        static unsigned instanceCouter = 0;
+        // lazy #define ;)
+#define STATS "\t(total " << instanceCouter << ")"
+
+        switch( what_r )
+          {
+          case TraceCADBase::CTOR:
+            ++instanceCouter;
+            SEC << self_r << what_r << STATS << std::endl;
+            break;
+
+          case TraceCADBase::DTOR:
+            --instanceCouter;
+            SEC << self_r << what_r << STATS << std::endl;
+            break;
+
+          case TraceCADBase::PING:
+            SEC << dynamic_cast<const MyClass::Impl &>(self_r)
+                << what_r << STATS << std::endl;
+            break;
+
+          case TraceCADBase::COPYCTOR:
+            ++instanceCouter;
+            SEC << self_r << what_r << "( "
+                << dynamic_cast<const MyClass::Impl &>(rhs_r)
+                << ")" << STATS << std::endl;
+            break;
+
+          case TraceCADBase::ASSIGN:
+            SEC << dynamic_cast<const MyClass::Impl &>(self_r)
+                << what_r << "( "
+                << dynamic_cast<const MyClass::Impl &>(rhs_r)
+                << ")" << STATS << std::endl;
+            break;
+          }
+      }
+  }
+  //
+  ///////////////////////////////////////////////////////////////////
+}
+///////////////////////////////////////////////////////////////////
+
+/******************************************************************
+**
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+**
+**      DESCRIPTION :
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  using zypp::MyClass;
+
+  MyClass c; // MyClass::Impl CTOR
+  MyClass d( c ); // shares Impl
+  MyClass e( d ); // shares Impl
+
+  MIL << "c.get" << c.get() << endl;
+  MIL << "d.get" << d.get() << endl;
+  MIL << "e.get" << e.get() << endl;
+
+  DBG << "will d.set( 4 )..." << endl;
+  d.set( 4 ); // performs COW
+  DBG << "done d.set( 4 )" << endl;
+
+  MIL << "c.get" << c.get() << endl;
+  MIL << "d.get" << d.get() << endl;
+  MIL << "e.get" << e.get() << endl;
+
+  DBG << "will e=d..." << endl;
+  e = d; // shares Impl
+  DBG << "done e=d" << endl;
+
+  MIL << "c.get" << c.get() << endl;
+  MIL << "d.get" << d.get() << endl;
+  MIL << "e.get" << e.get() << endl;
+
+  DBG << "will e.set( 1 )..." << endl;
+  e.set( 1 ); // performs COW
+  DBG << "done e.set( c )" << endl;
+
+  MIL << "c.get" << c.get() << endl;
+  MIL << "d.get" << d.get() << endl;
+  MIL << "e.get" << e.get() << endl;
+
+  INT << "===[END]============================================" << endl;
+  return 0;
+  // Tree times MyClass::Impl DTOR
+}
diff --git a/examples/EditionCompare.cc b/examples/EditionCompare.cc
new file mode 100644 (file)
index 0000000..cddb325
--- /dev/null
@@ -0,0 +1,35 @@
+#include <iostream>
+#include <zypp/base/Logger.h>
+#include <zypp/base/Exception.h>
+
+#include <zypp/Edition.h>
+
+using namespace std;
+using namespace zypp;
+
+inline std::string asOp( int res )
+{ return res ? ( res < 0 ? "  <   " : "  >   " ) : "  ==  "; }
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  --argc;
+  ++argv;
+
+  Edition lhs;
+  Edition rhs;
+
+  if ( argc >= 1 )
+    lhs = Edition( argv[0] );
+  if ( argc >= 2 )
+    rhs = Edition( argv[1] );
+
+  cerr << "compare: " << lhs << asOp( lhs.compare( rhs ) ) << rhs << endl;
+  cerr << "match:   " << lhs << asOp( lhs.match( rhs ) )   << rhs << endl;
+
+  return 0;
+}
diff --git a/examples/README b/examples/README
new file mode 100644 (file)
index 0000000..5385f51
--- /dev/null
@@ -0,0 +1,7 @@
+
+ZyPP Examples
+=============
+
+* none so far :(
+  
+  
diff --git a/examples/whatprovides.cc b/examples/whatprovides.cc
new file mode 100644 (file)
index 0000000..e8e55fc
--- /dev/null
@@ -0,0 +1,31 @@
+#include <iostream>
+
+#include <zypp/ZYpp.h>
+#include <zypp/ZYppFactory.h>
+#include <zypp/PathInfo.h>
+#include <zypp/Capability.h>
+#include <zypp/sat/Solvable.h>
+#include <zypp/sat/WhatProvides.h>
+
+int main(int argc, char **argv) {
+       zypp::ZYpp::Ptr zyppPtr = zypp::ZYppFactory::instance().getZYpp();
+
+       zypp::Pathname sysRoot( "/" );
+
+       zyppPtr->initializeTarget( sysRoot, false );
+       zyppPtr->target()->load();
+
+       std::cout << "Looking for packages which provide " << argv[1] << std::endl;
+       zypp::Capability cap(argv[1]);
+       zypp::sat::WhatProvides wp(cap);
+
+       if (wp.empty()) {
+               std::cout << "No providers of " << argv[1] << " found" << std::endl;
+       } else {
+               zypp::sat::Solvable package(*wp.begin());
+               std::cout << "Provided by " << package.name() << " version " << package.edition().version()
+                       << std::endl;
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/libzypp.pc.cmake b/libzypp.pc.cmake
new file mode 100644 (file)
index 0000000..7d64861
--- /dev/null
@@ -0,0 +1,14 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@LIB_INSTALL_DIR@/zypp
+libdir=@LIB_INSTALL_DIR@
+includedir=@CMAKE_INSTALL_PREFIX@/include
+
+Name: @PACKAGE@
+Version: @VERSION@
+Description: Package, Patch, Pattern, and Product Management
+
+Libs: -L${libdir} -lzypp
+Cflags: -I${includedir} @ZYPP_CFLAGS@
+
+features=@ZYPP_FEATURES@
+
diff --git a/libzypp.spec.cmake b/libzypp.spec.cmake
new file mode 100644 (file)
index 0000000..1aa5263
--- /dev/null
@@ -0,0 +1,354 @@
+#
+# spec file for package libzypp
+#
+# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# This file and all modifications and additions to the pristine
+# package are under the same license as the package itself.
+#
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+
+# norootforbuild
+
+Name:           @PACKAGE@
+License:        GPLv2
+Group:          System/Packages
+BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+Summary:        Package, Patch, Pattern, and Product Management
+Version:        @VERSION@
+Release:        0
+Source:         %{name}-%{version}.tar.bz2
+Source1:        %{name}-rpmlintrc
+Provides:       yast2-packagemanager
+Obsoletes:      yast2-packagemanager
+
+# Features we provide (update doc/autoinclude/FeatureTest.doc):
+Provides:       libzypp(plugin) = 0
+Provides:       libzypp(plugin:commit) = 0
+Provides:       libzypp(plugin:services) = 0
+Provides:       libzypp(plugin:system) = 0
+Provides:       libzypp(plugin:urlresolver) = 0
+
+%if 0%{?suse_version}
+Recommends:     logrotate
+# lsof is used for 'zypper ps':
+Recommends:     lsof
+%endif
+BuildRequires:  cmake
+BuildRequires:  openssl-devel
+%if 0%{?suse_version} >= 1130
+BuildRequires:  libudev-devel
+%else
+BuildRequires:  hal-devel
+%endif
+BuildRequires:  boost-devel
+BuildRequires:  dejagnu
+BuildRequires:  doxygen
+BuildRequires:  gcc-c++
+BuildRequires:  gettext-devel
+BuildRequires:  graphviz
+BuildRequires:  libxml2-devel
+BuildRequires:  libproxy-devel
+
+BuildRequires:  libsatsolver-devel >= 0.14.17
+%if 0%{?suse_version} >= 1100
+%requires_eq    satsolver-tools
+%else
+Requires:       satsolver-tools
+%endif
+
+# required for testsuite, webrick
+BuildRequires:  ruby
+
+%if 0%{?suse_version}
+BuildRequires:  libexpat-devel
+%else
+BuildRequires:  expat-devel
+%endif
+
+%if 0%{?suse_version}
+BuildRequires:  rpm-devel
+Requires:       /usr/bin/uuidgen
+%if 0%{?suse_version} > 1020
+BuildRequires:  hicolor-icon-theme
+%endif
+%endif
+
+%if 0%{?fedora_version}
+BuildRequires:  glib2-devel
+BuildRequires:  popt-devel
+BuildRequires:  rpm-devel
+%endif
+
+%if 0%{?mandriva_version}
+BuildRequires:  glib2-devel
+BuildRequires:  librpm-devel
+# uuidgen
+Requires:       e2fsprogs
+%endif
+
+%if 0%{?suse_version}
+Requires:       gpg2
+%else
+Requires:       gnupg2
+%endif
+
+%define min_aria_version 1.1.2
+# ---------------------------------------------------------------
+%if 0%{?suse_version} >= 1110
+# (almost) common codebase, but on SLES11-SP1 (according to Rudi
+# suse_version == 1110) we have a patched libcurl-7.19.0-11.22,
+# and no aria2. Furthermore SLE may use it's own set of .po files
+# from po/sle-zypp-po.tar.bz2.
+
+# this check should use 7.19.0 if SLE and 7.19.4 if not (backported
+# CURLOPT_REDIR_PROTOCOLS)
+%define min_curl_version 7.19.0-11.22
+%endif
+
+# ---------------------------------------------------------------
+
+%if 0%{?suse_version}
+%if 0%{?suse_version} >= 1100
+# Code11+
+BuildRequires:  libcurl-devel >= %{min_curl_version}
+Requires:       libcurl4   >= %{min_curl_version}
+%else
+# Code10
+BuildRequires:  curl-devel
+%endif
+%else
+# Other distros (Fedora)
+BuildRequires:  libcurl-devel >= %{min_curl_version}
+Requires:       libcurl   >= %{min_curl_version}
+%endif
+
+%description
+Package, Patch, Pattern, and Product Management
+
+Authors:
+--------
+    Michael Andres <ma@suse.de>
+    Jiri Srain <jsrain@suse.cz>
+    Stefan Schubert <schubi@suse.de>
+    Duncan Mac-Vicar <dmacvicar@suse.de>
+    Klaus Kaempf <kkaempf@suse.de>
+    Marius Tomaschewski <mt@suse.de>
+    Stanislav Visnovsky <visnov@suse.cz>
+    Ladislav Slezak <lslezak@suse.cz>
+
+%package devel
+Requires:       libzypp = %{version}
+Requires:       libxml2-devel
+Requires:       openssl-devel
+Requires:       rpm-devel
+Requires:       glibc-devel
+Requires:       zlib-devel
+Requires:       bzip2
+Requires:       popt-devel
+Requires:       boost-devel
+Requires:       libstdc++-devel
+%if 0%{?suse_version} >= 1130
+Requires:       libudev-devel
+%else
+Requires:       hal-devel
+%endif
+Requires:       cmake
+%if 0%{?suse_version}
+%if 0%{?suse_version} >= 1100
+# Code11+
+Requires:  libcurl-devel >= %{min_curl_version}
+%else
+# Code10
+Requires:  curl-devel
+%endif
+%else
+# Other distros (Fedora)
+Requires:  libcurl-devel >= %{min_curl_version}
+%endif
+%if 0%{?suse_version} >= 1100
+%requires_ge    libsatsolver-devel
+%else
+Requires:       libsatsolver-devel
+%endif
+Summary:        Package, Patch, Pattern, and Product Management - developers files
+Group:          System/Packages
+Provides:       yast2-packagemanager-devel
+Obsoletes:      yast2-packagemanager-devel
+
+%description -n libzypp-devel
+Package, Patch, Pattern, and Product Management - developers files
+
+Authors:
+--------
+    Michael Andres <ma@suse.de>
+    Jiri Srain <jsrain@suse.cz>
+    Stefan Schubert <schubi@suse.de>
+    Duncan Mac-Vicar <dmacvicar@suse.de>
+    Klaus Kaempf <kkaempf@suse.de>
+    Marius Tomaschewski <mt@suse.de>
+    Stanislav Visnovsky <visnov@suse.cz>
+    Ladislav Slezak <lslezak@suse.cz>
+
+%prep
+%setup -q
+
+%build
+mkdir build
+cd build
+export CFLAGS="$RPM_OPT_FLAGS"
+export CXXFLAGS="$RPM_OPT_FLAGS"
+unset TRANSLATION_SET
+# SLE11-* might want its own translation set:
+%if 0%{?suse_version} == 1110
+if [ -f ../po/sle-zypp-po.tar.bz ]; then
+  export TRANSLATION_SET=sle-zypp
+fi
+%endif
+cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+      -DDOC_INSTALL_DIR=%{_docdir} \
+      -DLIB=%{_lib} \
+      -DCMAKE_BUILD_TYPE=Release \
+      -DCMAKE_SKIP_RPATH=1 \
+      -DUSE_TRANSLATION_SET=${TRANSLATION_SET:-zypp} \
+      ..
+make %{?_smp_mflags} VERBOSE=1
+make -C doc/autodoc %{?_smp_mflags}
+make -C po %{?_smp_mflags} translations
+
+%if 0%{?run_testsuite}
+  make -C tests %{?_smp_mflags}
+  pushd tests
+  LD_LIBRARY_PATH=$PWD/../zypp:$LD_LIBRARY_PATH ctest .
+  popd
+%endif
+
+#make check
+
+%install
+rm -rf "$RPM_BUILD_ROOT"
+cd build
+make install DESTDIR=$RPM_BUILD_ROOT
+make -C doc/autodoc install DESTDIR=$RPM_BUILD_ROOT
+%if 0%{?fedora_version}
+ln -s %{_sysconfdir}/yum.repos.d $RPM_BUILD_ROOT%{_sysconfdir}/zypp/repos.d
+%else
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/zypp/repos.d
+%endif
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/zypp/services.d
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/commit
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/services
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/system
+mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/zypp/plugins/urlresolver
+mkdir -p $RPM_BUILD_ROOT%{_var}/lib/zypp
+mkdir -p $RPM_BUILD_ROOT%{_var}/log/zypp
+mkdir -p $RPM_BUILD_ROOT%{_var}/cache/zypp
+
+make -C po install DESTDIR=$RPM_BUILD_ROOT
+# Create filelist with translations
+cd ..
+%{find_lang} zypp
+
+
+%post
+/sbin/ldconfig
+if [ -f /var/cache/zypp/zypp.db ]; then rm /var/cache/zypp/zypp.db; fi
+
+# convert old lock file to new
+# TODO make this a separate file?
+# TODO run the sript only when updating form pre-11.0 libzypp versions
+LOCKSFILE=%{_sysconfdir}/zypp/locks
+OLDLOCKSFILE=%{_sysconfdir}/zypp/locks.old
+
+is_old(){
+  # if no such file, exit with false (1 in bash)
+  test -f ${LOCKSFILE} || return 1
+  TEMP_FILE=`mktemp`
+  cat ${LOCKSFILE} | sed '/^\#.*/ d;/.*:.*/d;/^[^[a-zA-Z\*?.0-9]*$/d' > ${TEMP_FILE}
+  if [ -s ${TEMP_FILE} ]
+  then
+    RES=0
+  else
+    RES=1
+  fi
+  rm -f ${TEMP_FILE}
+  return ${RES}
+}
+
+append_new_lock(){
+  case "$#" in
+    1 )
+  echo "
+solvable_name: $1
+match_type: glob
+" >> ${LOCKSFILE}
+;;
+    2 ) #TODO version
+  echo "
+solvable_name: $1
+match_type: glob
+version: $2
+" >> ${LOCKSFILE}
+;;
+    3 ) #TODO version
+  echo "
+solvable_name: $1
+match_type: glob
+version: $2 $3
+" >> ${LOCKSFILE}
+  ;;
+esac
+}
+
+die() {
+  echo $1
+  exit 1
+}
+
+if is_old ${LOCKSFILE}
+  then
+  mv -f ${LOCKSFILE} ${OLDLOCKSFILE} || die "cannot backup old locks"
+  cat ${OLDLOCKSFILE}| sed "/^\#.*/d"| while read line
+  do
+    append_new_lock $line
+  done
+fi
+
+
+%postun -p /sbin/ldconfig
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+%files -f zypp.lang
+%defattr(-,root,root)
+%dir               %{_sysconfdir}/zypp
+%if 0%{?fedora_version}
+%{_sysconfdir}/zypp/repos.d
+%else
+%dir               %{_sysconfdir}/zypp/repos.d
+%endif
+%dir               %{_sysconfdir}/zypp/services.d
+%config(noreplace) %{_sysconfdir}/zypp/zypp.conf
+%config(noreplace) %{_sysconfdir}/zypp/systemCheck
+%config(noreplace) %{_sysconfdir}/logrotate.d/zypp-history.lr
+%dir               %{_var}/lib/zypp
+%dir               %{_var}/log/zypp
+%dir               %{_var}/cache/zypp
+%{_prefix}/lib/zypp
+%{_datadir}/zypp
+%{_bindir}/*
+%{_libdir}/libzypp*so.*
+%doc %{_mandir}/man5/locks.5.*
+
+%files devel
+%defattr(-,root,root)
+%{_libdir}/libzypp.so
+%{_docdir}/%{name}
+%{_includedir}/zypp
+%{_datadir}/cmake/Modules/*
+%{_libdir}/pkgconfig/libzypp.pc
+
+%changelog
diff --git a/mkChangelog b/mkChangelog
new file mode 100755 (executable)
index 0000000..96409ed
--- /dev/null
@@ -0,0 +1,159 @@
+#! /bin/bash
+#
+
+function errexit() {
+  exec >&2
+  echo "Error: $@"
+  exit 1
+}
+
+export LC_ALL=""
+EDITOR=${EDITOR:-vi}
+
+TDIR=$(dirname $0)
+test -n "$TDIR" && cd $TDIR
+
+CHANGESFILE=$(ls package/*.changes)
+test -f "$CHANGESFILE" || errexit "No changes file '$CHANGESFILE'"
+
+VERSIONFILE="VERSION.cmake"
+test -f "$VERSIONFILE" || errexit "No version file '$VERSIONFILE'"
+
+LANG="en"
+
+## Version.cmake tags in getversion() are still zypp specific.
+
+function usage() {
+  exec >&2
+cat <<EOF
+
+Usage:   $(basename $0) [OPTIONS]
+Options: -h,-?,--help   This page.
+
+$(basename $0) will load the changes file '$CHANGESFILE'
+into your editor (\$EDITOR=$EDITOR), providing a new changes
+entry template:
+
+    -------------------------------------------------------------------
+    Wed Jul 30 18:20:06 CEST 2008 - ma@suse.de
+
+    -
+    #---delete-or-release---# LAST RELEASED: 5.3.2 (2) NEW RELEASE: 5.4.0 (4)
+
+The line '#---delete-or-release---#...' shows the last version submitted
+to autobuild ('# LAST RELEASED:; tag in $VERSIONFILE). And also the current
+version, asuming you already updated the $VERSIONFILE according to your changes.
+(The number in parenthesis is _COMPATMINOR)
+
+
+- Delete the line if you don't want to submit the package to autobuild.
+
+- Leave the line in place if you want to submit the package.
+
+
+Closing the editor you are prompted:
+
+    #---delete-or-release---# LAST RELEASED: 5.3.2 (2) NEW RELEASE: 5.4.0 (4)
+    (a)bort, (c)ontinue, (e)dit :
+
+Choosing (c)ontinue will write the new changes file. The '#---delete-or-release---#'
+line is missing in case you deleted it. It's presence will remind you
+that it is going to be converted into:
+
+    - version 5.4.0
+
+and the '# LAST RELEASED:; tag in $VERSIONFILE will be updated accordingly.
+Now check the result, check in your changes, build the package and submit
+to autobuild.
+
+Released by accident? Don't mind. Nothing bad will happen. If you want to
+undo the change, restore the 'LAST RELEASED: ' entry in $VERSIONFILE and
+delete the '- version' line in $CHANGESFILE'.
+
+EOF
+  exit 1
+}
+
+case "$1" in
+  -[hH?]*)
+    usage
+    ;;
+  --help)
+    usage
+    ;;
+esac
+
+function getversion() {
+  cat "$VERSIONFILE" \
+  | awk '
+  function getnum() {
+    gsub("^[^\"]*\"","")
+    gsub("\".*$","")
+  }
+  /^ *SET *\( *LIBZYPP_MAJOR *"[0-9]+" *\)/       {getnum();major=$0}
+  /^ *SET *\( *LIBZYPP_MINOR *"[0-9]+" *\)/       {getnum();minor=$0}
+  /^ *SET *\( *LIBZYPP_PATCH *"[0-9]+" *\)/       {getnum();patch=$0}
+  /^ *SET *\( *LIBZYPP_COMPATMINOR *"[0-9]+" *\)/ {getnum();compatminor=$0}
+  /^# LAST RELEASED:/                             {gsub("^.*RELEASED: *","");gsub(" +$","");gsub(" +\\("," (");lastrelease=$0}
+  END {
+    thisrelease = major"."minor"."patch" ("compatminor")"
+    if ( thisrelease == lastrelease )
+      print "#---delete-or-release---# LAST RELEASED: "lastrelease" UNCHANGED RELEASE: "thisrelease
+    else
+      print "#---delete-or-release---# LAST RELEASED: "lastrelease" NEW RELEASE: "thisrelease
+  }
+  '
+}
+
+test -r /etc/sysconfig/mail && source /etc/sysconfig/mail
+EMAIL="${USER}@${FROM_HEADER:-$(hostname -f)}"
+
+GOTVERSION="$(getversion)"
+
+TMPFILE=$(mktemp)
+exec 3>&1-
+exec >$TMPFILE
+echo "-------------------------------------------------------------------"
+echo "$(date) - $EMAIL"
+echo ""
+echo "- "
+echo "$GOTVERSION"
+echo ""
+cat $CHANGESFILE
+exec >&3
+
+RES=e
+while [ "$RES" == "e" ]; do
+  $EDITOR $TMPFILE
+  echo
+  NEWREL=$(grep '#---delete-or-release---#' $TMPFILE)
+  test -n "$NEWREL" && echo "$NEWREL"
+  read -n 1 -p "(a)bort, (c)ontinue, (e)dit : " RES
+  echo
+  echo
+  case "$RES" in
+    [eE]*)
+      RES=e
+      ;;
+    [cC])
+      test -n "$NEWREL" && {
+        echo "Remember new release in $VERSIONFILE"
+        sed -i 's/^.*#---delete-or-release---#.*RELEASE:/- version/' $TMPFILE
+        NEWREL=$(sed 's/^.*#---delete-or-release---#.*RELEASE:/# LAST RELEASED:/' <<<"$NEWREL")
+        sed -i "s/^# LAST RELEASED:.*$/$NEWREL/" $VERSIONFILE
+      }
+
+      echo "Store new $CHANGESFILE"
+      cp $TMPFILE $CHANGESFILE
+
+      echo "$(sed 's/^.*#---delete-or-release---#.*RELEASE:/# CURRENT RELEASE:/' <<<"$GOTVERSION")"
+      awk '{print}/^----------/{n=n+1; if ( n == 2 ) exit 0; }' $CHANGESFILE
+
+      ;;
+    *)
+      echo "Leave $CHANGESFILE untouched"
+      ;;
+  esac
+done
+
+rm -f $TMPFILE
diff --git a/package/libzypp-rpmlint.cmake b/package/libzypp-rpmlint.cmake
new file mode 100644 (file)
index 0000000..4aa496b
--- /dev/null
@@ -0,0 +1 @@
+addFilter("libzypp.* shlib-policy-name-error")
diff --git a/package/libzypp.changes b/package/libzypp.changes
new file mode 100644 (file)
index 0000000..8151772
--- /dev/null
@@ -0,0 +1,8994 @@
+-------------------------------------------------------------------
+Mon Jul  4 10:41:42 CEST 2011 - ma@suse.de
+
+- Make fix for bnc#702576 more robust. 
+- Enhance fix for bnc#699435. Return an error if download in advance 
+  failed to provide all packages, so zypper does not silently quit.
+- version 9.8.3 (8)
+
+-------------------------------------------------------------------
+Thu Jun 30 01:13:43 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed Jun 29 12:23:24 CEST 2011 - ma@suse.de
+
+- Always compute transaction from pool (bnc#702576)
+- version 9.8.2 (8)
+
+-------------------------------------------------------------------
+Tue Jun 28 16:04:50 CEST 2011 - ma@suse.de
+
+- Add feature provides for supported plugins.
+
+-------------------------------------------------------------------
+Tue Jun 21 10:44:47 CEST 2011 - dmacvicar@suse.de
+
+- Add configuration template for automatic kernel 
+  purge (feature#312018) to zypp.conf
+
+-------------------------------------------------------------------
+Tue Jun 14 13:38:00 CEST 2011 - ma@suse.de
+
+- Fix download loop to skip non-install actions. (bnc#699435)
+- version 9.8.1 (8)
+
+-------------------------------------------------------------------
+Sun Jun 12 01:13:36 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Jun  9 09:34:51 CEST 2011 - ma@suse.de
+
+- Fix missing return value.
+
+-------------------------------------------------------------------
+Wed Jun  8 12:58:54 CEST 2011 - ma@suse.de
+
+- Simplify ZYppCommitResult by using filtered Transaction::action_iterator.
+- version 9.8.0 (8)
+
+-------------------------------------------------------------------
+Tue Jun  7 14:12:35 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Tue Jun  7 12:42:20 CEST 2011 - ma@suse.de
+
+- Fix transaction ORDER_BY_MEDIANR.
+- version 9.7.0 (5)
+
+-------------------------------------------------------------------
+Fri Jun  3 23:12:05 CEST 2011 - ma@suse.de
+
+- Basic commit plugins implemented. They may be used to implement pre/post
+  commit actions like taking file system snapshots (fate#303699)
+- version 9.6.0 (5)
+
+-------------------------------------------------------------------
+Tue May 31 17:27:45 CEST 2011 - ma@suse.de
+
+- Do commit based on sat::Transaction.
+- version 9.5.0 (5)
+
+-------------------------------------------------------------------
+Tue May 31 12:09:17 CEST 2011 - ma@suse.de
+
+- Assert rpm database directory is created before rpmtsInitDB is
+  called (bnc#697115)
+
+-------------------------------------------------------------------
+Tue May 31 11:46:57 CEST 2011 - dheidler@suse.de
+
+- fix var definition order in PackageProvider
+- fix operator~ return datatype for flags (ctor is explicit)
+- add method interactiveFlags to Patch describing what makes it
+  interactive
+- version 9.4.0 (3)
+
+-------------------------------------------------------------------
+Tue May 31 10:12:12 CEST 2011 - ma@suse.de
+
+- Recommend 'lsof' (for zypper ps) (bnc#694427, bnc#684466)
+
+-------------------------------------------------------------------
+Mon May 30 17:14:14 CEST 2011 - dheidler@suse.de
+
+- Switch patch interactive-check to flags
+- version 9.3.0 (3)
+
+-------------------------------------------------------------------
+Sun May 29 01:13:42 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri May 27 17:02:05 CEST 2011 - ma@suse.de
+
+- fix specfile to handle sles translations if available
+
+-------------------------------------------------------------------
+Fri May 27 09:51:13 CEST 2011 - ma@suse.de
+
+- Avoid using #elifdef directive.
+
+-------------------------------------------------------------------
+Thu May 26 01:13:43 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed May 25 09:22:28 CEST 2011 - ma@suse.de
+
+- Add a fallback gnome-packagekit updater in package-manager (bnc#667504)
+- Backport changelog entries from SLE11-SP1 branch. Bugs mentioned here
+  were either fixed without bnc# or do not apply to this branch. This is
+  to make the changelog checker happy:
+  - Set proper defaults for service repos (bnc#673943)
+
+-------------------------------------------------------------------
+Tue May 24 12:43:32 CEST 2011 - dheidler@suse.de
+
+- add option ignore_reboot_flag to Patch::interactive (bnc#665853)
+- version 9.2.0 (2)
+
+-------------------------------------------------------------------
+Mon May 23 16:12:32 CEST 2011 - dheidler@suse.de
+
+- respect patch content license when determinating interactive status
+- version 9.1.2 (1)
+
+-------------------------------------------------------------------
+Mon May 23 12:05:10 CEST 2011 - ma@suse.de
+
+- More rpm-4.9 fixes
+- Disable rpm V3toV4 conversion while not working with rpm 4.9
+
+-------------------------------------------------------------------
+Sun May 22 01:14:21 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri May 20 17:11:10 CEST 2011 - ma@suse.de
+
+- Fix for rpm-4.9
+- version 9.1.1 (1)
+
+-------------------------------------------------------------------
+Fri May 20 13:43:38 CEST 2011 - ma@suse.de
+
+- Disable use if rpm-4.4 legacy interface (dropped in 4.9) (bnc#691089)
+- Fix poolquery handling repo restrictions correctly. (bnc#661976)
+- version 9.1.0 (1)
+
+-------------------------------------------------------------------
+Wed May 18 14:10:20 CEST 2011 - dmacvicar@suse.de
+
+- Allow MediaCD to build against HAL or nothing
+  if udev is not present
+
+-------------------------------------------------------------------
+Mon May 16 14:42:09 CEST 2011 - dmacvicar@suse.de
+
+- forward port all changes to make it build in Code10
+
+-------------------------------------------------------------------
+Mon May 16 11:51:51 UTC 2011 - dmacvicar@suse.de
+
+- make -Werror=format-security optional and used only if
+  supported
+
+-------------------------------------------------------------------
+Sun May 15 01:13:47 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu May 12 01:13:54 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri May  6 13:22:35 CEST 2011 - dheidler@suse.de
+
+- delete old metadata temp directories
+- version 9.0.3 (0)
+
+-------------------------------------------------------------------
+Wed Apr 27 17:17:00 CEST 2011 - dheidler@suse.de
+
+- fix max_concurrent_connections option in MultiCurl (bnc#596089)
+- version 9.0.2 (0)
+
+-------------------------------------------------------------------
+Sun Apr 17 01:13:40 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Tue Apr 12 18:49:32 CEST 2011 - dheidler@suse.de
+
+- check for permissions before writing (fixes bnc#683509)
+- version 9.0.1 (0)
+
+-------------------------------------------------------------------
+Tue Apr  5 12:08:27 CEST 2011 - ma@suse.de
+
+- Switch ResPoolProxy to use multimap in order to allow iteration
+  of all Selectables of all kinds.
+- Add upper/lower_bound iteration to MapKVIterator.
+- Add stream output for multimap/set.
+- version 9.0.0 (0)
+
+-------------------------------------------------------------------
+Mon Apr  4 15:08:08 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Mon Apr  4 15:04:47 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Mon Apr  4 15:03:55 CEST 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Mar 10 01:13:39 CET 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Sun Mar  6 01:13:41 CET 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed Feb 16 14:24:27 UTC 2011 - dmacvicar@suse.de
+
+- Do not look for $releasever 's value if there is no variable
+  in the url: parses product file again and again
+- version 8.12.2 (10)
+
+-------------------------------------------------------------------
+Thu Feb 10 16:01:46 CET 2011 - ma@suse.de
+
+- Remove package-manager.desktop file (bnc#329635)
+- version 8.12.1 (10)
+
+-------------------------------------------------------------------
+Tue Jan 25 10:09:22 CET 2011 - ma@suse.de
+
+- Apply patch introducing armv7nhl:armv7hl
+
+-------------------------------------------------------------------
+Tue Jan 18 12:28:19 UTC 2011 - dmacvicar@suse.de
+
+- allow for Repo Index Services to set the enabled state of
+  repositories from the server side.
+- Fix priority not being set on reading service indexes
+- version 8.12.0 (10)
+
+-------------------------------------------------------------------
+Fri Jan 14 12:45:18 CET 2011 - ma@suse.de
+
+- Add new ServicePlugin Exceptions.
+
+-------------------------------------------------------------------
+Thu Jan 13 01:13:32 CET 2011 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Dec 23 14:42:02 CET 2010 - dheidler@suse.de
+
+- add method to check if there are manually configured urls
+- version 8.11.0 (10)
+
+-------------------------------------------------------------------
+Thu Dec 23 11:31:22 CET 2010 - dheidler@suse.de
+
+- create cache directory, if it doesn't exist
+  when saving mirrorlist
+- version 8.10.6 (10)
+
+-------------------------------------------------------------------
+Wed Dec 22 12:38:22 CET 2010 - dheidler@suse.de
+
+- moved keepPackages code to RepoInfo::Impl
+- version 8.10.5 (10)
+
+-------------------------------------------------------------------
+Tue Dec 21 13:29:06 CET 2010 - dheidler@suse.de
+
+- Fix caching of mirrorlist files
+- version 8.10.4 (10)
+
+-------------------------------------------------------------------
+Thu Dec 16 19:46:00 CET 2010 - ma@suse.de
+
+- Do not export keyring if rpmdb is initialised, but target isn't
+  (bnc#659494)
+- version 8.10.3 (10)
+
+-------------------------------------------------------------------
+Fri Dec 10 17:17:07 CET 2010 - dheidler@suse.de
+
+- Catch RPM-Exeption when instaling the Distribution (bnc#658714)
+- version 8.10.2 (10)
+
+-------------------------------------------------------------------
+Wed Dec  8 14:15:12 CET 2010 - dheidler@suse.de
+
+- fix retrieval of distributionVersion if target is not initialized
+- MultiCurl: make sure the server responds with "partial content",
+  fixes corrupt downloads if the metalink file contains no checksums
+- version 8.10.1 (10)
+
+-------------------------------------------------------------------
+Wed Dec  1 17:39:16 CET 2010 - dheidler@suse.de
+
+- Added ReplacerVar caching in RepoInfo
+- Automaticly remove empty mirrorlist-files
+- version 8.10.0 (10)
+
+-------------------------------------------------------------------
+Tue Nov 30 17:11:32 CET 2010 - dheidler@suse.de
+
+- Added MirrorList caching
+- version 8.9.0 (7)
+
+-------------------------------------------------------------------
+Mon Nov 29 12:31:37 CET 2010 - ma@suse.de
+
+- Icons for libzypp should go into desktop-data. (bnc#329635)
+- version 8.8.2 (7)
+
+-------------------------------------------------------------------
+Wed Nov 24 11:55:59 CET 2010 - ma@suse.de
+
+- Properly handle FTP response 550. (bnc#645747)
+- version 8.8.1 (7)
+
+-------------------------------------------------------------------
+Fri Nov 19 11:24:39 CET 2010 - mls@suse.de
+
+- fix access of freed memory in MultiCurl (bnc#654600)
+
+-------------------------------------------------------------------
+Sun Nov  7 01:13:39 CET 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed Nov  3 16:06:46 CET 2010 - dheidler@suse.de
+
+- added metalink support
+- version 8.8.0 (7)
+
+-------------------------------------------------------------------
+Thu Oct 21 18:15:31 CEST 2010 - ma@suse.de
+
+- Feed the ProvideFilePolicy progress callback in addition to any
+  connected media::DownloadProgressReport (bnc#545106)
+- version 8.7.1 (7)
+
+-------------------------------------------------------------------
+Wed Oct 13 16:14:13 CEST 2010 - dheidler@suse.de
+
+- Make MetaLinkParser accept InputStreams
+- Make MetaLinkParser accept Pathnames insted of strings
+- Fix MetaLinkv4 hash parsing
+- Add MetaLinkParser test
+- version 8.7.0 (7)
+
+-------------------------------------------------------------------
+Mon Oct 11 17:06:17 CEST 2010 - ma@suse.de
+
+- Use timeouts in plugin script communication.
+- Fix ExternalProgram to correctly remember exit status.
+- version 8.6.0 (5)
+
+-------------------------------------------------------------------
+Sun Oct 10 01:13:40 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri Oct  8 08:43:02 UTC 2010 - dmacvicar@novell.com
+
+- fix services not being linked to their file after being
+  saved
+
+-------------------------------------------------------------------
+Thu Oct  7 09:02:26 CEST 2010 - jkupec@suse.cz
+
+- Don't use aria2c for FTP (bnc #641328)
+
+-------------------------------------------------------------------
+Wed Oct  6 15:39:59 UTC 2010 - dmacvicar@novell.com
+
+- implementation for url resolver plugins
+- version 8.5.0 (5)
+
+-------------------------------------------------------------------
+Wed Oct  6 16:54:15 CEST 2010 - dheidler@suse.de
+
+- Use DownloadInHeaps as default, when there is nothing configured
+  and when the target root is set to "/". (bnc#591476)
+- version 8.4.0 (4)
+
+-------------------------------------------------------------------
+Tue Sep 28 17:12:51 CEST 2010 - dheidler@suse.de
+
+- fixed replacing releasever (for fedora systems) - (bnc#637470)
+- version 8.3.0 (0)
+
+-------------------------------------------------------------------
+Fri Sep 24 13:07:25 CEST 2010 - mls@suse.de
+
+- fix metalink4 parsing [bnc#641484]
+
+-------------------------------------------------------------------
+Thu Sep 23 01:13:45 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Mon Sep 20 15:31:31 UTC 2010 - dmacvicar@novell.com
+
+- Allow per repository proxy settings like yum does.
+  Including setting it to _none_ overriding the
+  system proxy.
+  Patch from Zhang, Qiang <qiang.z.zhang@intel.com>
+- version 8.2.1 (0)
+
+-------------------------------------------------------------------
+Fri Sep 10 17:06:34 CEST 2010 - dheidler@suse.de
+
+- fixed replacing basearch (for fedora systems) - (bnc#637473)
+- version 8.2.0 (0)
+
+-------------------------------------------------------------------
+Fri Sep 10 11:57:29 CEST 2010 - ma@suse.de
+
+- Report download failures in commit result (bnc#431854)
+- Fix Solvable::onSystemByUser returning true for uninstalled solvables.
+- version 8.1.3 (0)
+
+-------------------------------------------------------------------
+Tue Aug 31 20:01:26 CEST 2010 - ma@suse.de
+
+- Fix download-only not to omit source packages (bnc#635596)
+- version 8.1.2 (0)
+
+-------------------------------------------------------------------
+Thu Aug 26 01:13:38 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri Aug 13 12:33:26 UTC 2010 - dmacvicar@novell.com
+
+- fix basearch url variable
+- use the right release package name on fedora
+
+-------------------------------------------------------------------
+Tue Aug 10 13:18:08 CEST 2010 - ma@suse.de
+
+- MediaDISK: Use blkid to verify disk volumes. (bnc#623226)
+- version 8.1.1 (0)
+
+-------------------------------------------------------------------
+Sun Aug  8 01:13:37 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Aug  5 01:13:53 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Tue Aug  3 16:17:04 CEST 2010 - ma@suse.de
+
+- Fix memory leaks.
+
+-------------------------------------------------------------------
+Mon Aug  2 11:58:57 CEST 2010 - ma@suse.de
+
+- Enhance PoolItem interface to assist patch classification. (bnc#627316)
+- version 8.1.0 (0)
+
+-------------------------------------------------------------------
+Tue Jul 27 15:38:00 CEST 2010 - ma@suse.de
+
+- Fix bug in PoolQuery::addDependency
+- Disable MediaAria and enable MultiCurl as default http/ftp backend.
+  MultiCurl implements MetaLink and Zsync support using libcurl. In
+  case of trouble set ZYPP_MULTICURL=0 in the envirionment to disable
+  the new backend.
+- version 8.0.1 (0)
+
+-------------------------------------------------------------------
+Mon Jul 26 17:05:14 CEST 2010 - ma@suse.de
+
+- Bump heads major version after 11.3 branched away.
+- version 8.0.0 (0)
+
+-------------------------------------------------------------------
+Thu Jul 22 01:13:27 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed Jul  7 13:56:49 CEST 2010 - ma@suse.de
+
+- Add PoolQuery for name, edition AND architecture in one go. (bnc#614362)
+- version 7.8.0 (6)
+
+-------------------------------------------------------------------
+Mon Jul  5 13:03:57 CEST 2010 - ma@suse.de
+
+- Fix requirement to /usr/bin/uuidgen (bnc#613304)
+
+-------------------------------------------------------------------
+Sun Jul  4 01:13:29 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Tue Jun 29 17:46:13 CEST 2010 - ma@suse.de
+
+- Fix CURLOPT_MAX_RECV_SPEED_LARGE expecting a curl_off_t argument.
+
+-------------------------------------------------------------------
+Tue Jun 29 10:22:06 CEST 2010 - ma@suse.de
+
+- Respect zypp.conf policy settings when solving for update.
+
+-------------------------------------------------------------------
+Fri Jun 25 11:35:52 CEST 2010 - ma@suse.de
+
+- Don't bloat logfile by logging install progess values.
+
+-------------------------------------------------------------------
+Thu Jun 10 01:13:49 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Mon Jun  7 17:52:50 CEST 2010 - ma@suse.de
+
+- Add missing Date constant declarations.
+- version 7.7.5 (6)
+
+-------------------------------------------------------------------
+Mon Jun  7 11:46:58 CEST 2010 - ma@suse.de
+
+- Prevent against daemons launched in rpm %post, that do not close
+  their filedescriptors. Original fix was accidentally reverted.
+  (bnc#174548)
+- version 7.7.4 (6)
+
+-------------------------------------------------------------------
+Sun Jun  6 01:13:52 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri Jun  4 17:14:17 CEST 2010 - ma@suse.de
+
+- Fix default evaluation of recommendations of installed
+  packages (bnc#605490)
+- version 7.7.3 (6)
+
+-------------------------------------------------------------------
+Thu Jun  3 01:14:08 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri May 21 20:00:55 CEST 2010 - ma@suse.de
+
+- Fix broken IdStringType comparison (bnc#607572)
+- version 7.7.2 (6)
+
+-------------------------------------------------------------------
+Thu May 20 21:01:05 CEST 2010 - ma@suse.de
+
+- Fix packages provided via delta rpm being placed in
+  the wrong package cache (bnc#607583)
+- version 7.7.1 (6)
+
+-------------------------------------------------------------------
+Thu May 20 01:13:49 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Tue May 18 18:02:44 CEST 2010 - ma@suse.de
+
+- Fix arch detection on sprac (bug #566291)
+- Arch: add sparc64v and sparcv9v and armv7l
+- RepoManager: refresh repo if last refresh is in the future (bnc#593617)
+- version 7.6.1 (6)
+
+-------------------------------------------------------------------
+Mon May 17 15:25:14 CEST 2010 - ma@suse.de
+
+- Fix package-manager-su to support LXDE (Andrea Florio) (bnc#582235)
+- Cleanup spec file (Pavol Rusnak)
+- version 7.6.0 (6)
+
+-------------------------------------------------------------------
+Sun May 16 01:13:39 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Wed May 12 19:09:54 CEST 2010 - ma@suse.de
+
+- Add methods to evaluate gpg geys expiration dates.
+- Export all rpmDb keys to the zypp trusted keyring in one go.
+- version 7.6.0 (6)
+
+-------------------------------------------------------------------
+Wed May 12 17:35:44 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Sun May  9 01:13:45 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Sat May  8 01:13:43 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Fri May  7 01:13:55 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2 (Revision: 54959)
+
+-------------------------------------------------------------------
+Wed May  5 14:49:39 CEST 2010 - ma@suse.de
+
+- Update zypp-po.tar.bz2 (Revision: 54959)
+
+-------------------------------------------------------------------
+Tue May  4 01:13:43 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Mon May  3 18:12:36 CEST 2010 - ma@suse.de
+
+- Improve solver.cleandepsOnRemove result by evaluating the install
+  history to find packages installed on behalf of a user request (not
+  auto added by the solver).
+- version 7.5.0 (5)
+
+-------------------------------------------------------------------
+Sat May  1 01:13:43 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Fri Apr 30 01:13:46 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Thu Apr 29 17:10:29 CEST 2010 - ma@suse.de
+
+- Cleanup when deleting packages. New zypp.conf expert option
+  solver.cleandepsOnRemove telling whether the solver should per
+  default try to remove packages exclusively required by the ones
+  he's asked to delete (default false).
+- Resolver::cleandepsOnRemove API to allow applications to change
+  the solver option.
+- version 7.4.0 (4)
+
+-------------------------------------------------------------------
+Thu Apr 29 01:13:40 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Wed Apr 28 16:35:04 CEST 2010 - ma@suse.de
+
+- Enable splitprovides on update.
+
+-------------------------------------------------------------------
+Wed Apr 28 01:13:37 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Tue Apr 27 17:42:20 CEST 2010 - ma@suse.de
+
+- Selectable: Classify broken but locked patch as isUnwanted (bnc#577118)
+- version 7.3.0 (2)
+
+-------------------------------------------------------------------
+Tue Apr 27 11:25:17 CEST 2010 - ma@suse.de
+
+- Use libudev to detect available cd/dvd devices (bnc#590707,fate#308980)
+- Fix specfile to BuildRequire libudev-devel.
+- version 7.2.0 (2)
+
+-------------------------------------------------------------------
+Mon Apr 26 11:57:23 CEST 2010 - ma@suse.de
+
+- Support URLs and ISOs ending on 'Media1', 'Media2', etc., when
+  rewiting the URL to access a specific media number. By now only
+  nanmes ending on 'CD' or 'DVD' were supported. (bnc#594850)
+
+-------------------------------------------------------------------
+Fri Apr 23 01:13:34 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Wed Apr 21 01:13:38 CEST 2010 - ma@suse.de
+
+- Update translations.
+
+-------------------------------------------------------------------
+Tue Apr 20 16:56:59 CEST 2010 - ma@suse.de
+
+- /etc/zypp/locks: Allow to specify edition ranges with
+  solvable:name and dependencies.
+- version 7.1.1 (1)
+
+-------------------------------------------------------------------
+Thu Apr 15 12:49:01 CEST 2010 - ma@suse.de
+
+- Provide name of the lock holder in ZYppFactoryException. (bnc#580513)
+- version 7.1.0 (1)
+
+-------------------------------------------------------------------
+Wed Apr 14 15:09:18 CEST 2010 - ma@suse.de
+
+- Using boost-1.42 requires -fno-strict-aliasing (bnc#595545)
+- Bump major version for 11.3 development.
+- version 7.0.0 (0)
+
+-------------------------------------------------------------------
+Tue Apr 13 14:36:31 CEST 2010 - ma@suse.de
+
+- Fix parsing port from IPv6 URL (bnc#593385)
+
+-------------------------------------------------------------------
+Fri Mar 26 19:23:59 CET 2010 - ma@suse.de
+
+- Propagate ZConfig::setTextLocale to pool. (bnc#588850)
+- version 6.31.3 (31)
+
+-------------------------------------------------------------------
+Fri Mar 26 13:50:08 CET 2010 - ma@suse.de
+
+- Fix guessing package spec to match package names only. (bnc#590864)
+- version 6.31.2 (31)
+
+-------------------------------------------------------------------
+Tue Mar 23 16:55:56 CET 2010 - ma@suse.de
+
+- Add static Target::distributionLabel to return the baseproducts
+  shortName and summary. Mainlu used for the bootloader menu. (bnc #586303)
+- version 6.31.1 (31)
+
+-------------------------------------------------------------------
+Thu Mar 18 17:17:02 CET 2010 - ma@suse.de
+
+- Fix broken bit values in enum VendorSupportOption (bnc#589331)
+- version 6.31.0 (31)
+
+-------------------------------------------------------------------
+Fri Mar 12 14:46:25 CET 2010 - ma@suse.de
+
+- Fix handling of symlinked packages in package cache. (bnc #585409)
+- version 6.30.5 (19)
+
+-------------------------------------------------------------------
+Tue Feb 23 19:05:04 CET 2010 - jkupec@suse.cz
+
+- Avoiding use of 'guest' if 'credentials' is used when moutning
+  a CIFS share. This caused 'permission denied' error with certain
+  server configurations (bnc #560496).
+
+-------------------------------------------------------------------
+Tue Feb 23 17:31:05 CET 2010 - ma@suse.de
+
+- Check if a downloaded file actually exists even if aria2c returned 0.
+  (bnc #564816)
+- version 6.30.3 (19)
+
+-------------------------------------------------------------------
+Mon Feb 22 15:25:34 CET 2010 - jkupec@suse.cz
+
+- Made CURLE_PARTIAL_FILE an auto-retry error (bnc #471436)
+
+-------------------------------------------------------------------
+Mon Feb 22 13:09:09 CET 2010 - ma@suse.de
+
+- Turn off cookies when retrieving services repoindex.xml (bnc #573897)
+- Consider pending disable requests when removing service repositories. (bnc #572634)
+- version 6.30.1 (19)
+
+-------------------------------------------------------------------
+Sun Feb 21 14:11:49 CET 2010 - jkupec@suse.cz
+
+- RepoManager::cleanCacheDirGarbage added for removing directories
+  which do not belong to any of known repos (bnc #467693)
+- version 6.30.0 (19)
+
+-------------------------------------------------------------------
+Thu Feb 11 13:40:49 CET 2010 - ma@suse.de
+
+- On SLE aria2 is not required, so conflict with a too old aria2
+  version installed. (bnc #578052)
+- version 6.29.5 (19)
+
+-------------------------------------------------------------------
+Wed Feb 10 12:45:21 CET 2010 - ma@suse.de
+
+- Fix package-manager script wrongly escaping UTF-8 chars in path
+  names (bnc #571410)
+- version 6.29.4 (19)
+
+-------------------------------------------------------------------
+Mon Feb  8 13:21:40 CET 2010 - ma@suse.de
+
+- Support an alternate SLE-SP1 translation set.
+- version 6.29.3 (19)
+
+-------------------------------------------------------------------
+Wed Feb  3 15:57:13 CET 2010 - ma@suse.de
+
+- Remember the enabled state of removed service repositories. This
+  way we are able to restore service repositories correctly after a
+  subscrition expired and gets renewed. (bnc #572634)
+- version 6.29.2 (19)
+
+-------------------------------------------------------------------
+Thu Jan 28 16:12:25 CET 2010 - jkupec@suse.cz
+
+- Abort aria2c download when the progress callback receives 'false'
+  (bnc #545106)
+
+-------------------------------------------------------------------
+Tue Jan 19 14:50:15 CET 2010 - ma@suse.de
+
+- Evaluate SolvAttr::repositoryToolVersion to prevent loading
+  outdated solv files. (bnc #570623)
+- version 6.29.0 (19)
+
+-------------------------------------------------------------------
+Fri Jan 15 18:15:26 CET 2010 - jkupec@suse.cz
+
+- Use regex to parse aria2c progress lines (bnc #570917)
+- version 6.28.1 (19)
+
+-------------------------------------------------------------------
+Thu Jan  7 12:25:24 CET 2010 - jkupec@suse.cz
+
+- Set SignatureFileChecker context even if the key is not known
+  (bnc #495977)
+- version 6.28.0 (19)
+
+-------------------------------------------------------------------
+Thu Dec 10 17:18:34 CET 2009 - jkupec@suse.cz
+
+- RepoInfoBase::label() added for use in UI messages, plus
+  ZConfig::repoLabelIsAlias()
+
+-------------------------------------------------------------------
+Tue Dec  8 19:41:17 CET 2009 - ma@suse.de
+
+- Fix transaction building in presence of multiversion installable items.
+- version 6.27.1 (19)
+
+-------------------------------------------------------------------
+Fri Dec  4 17:19:58 CET 2009 - ma@suse.de
+
+- Improve multiversion status handling and installation. (fate #305311)
+- version 6.27.0 (19)
+
+-------------------------------------------------------------------
+Fri Dec  4 14:56:19 CET 2009 - jkupec@suse.cz
+
+- Don't allow an alias to start with '.' (bnc #473834)
+
+-------------------------------------------------------------------
+Thu Dec  3 12:00:16 CET 2009 - ma@suse.de
+
+- PickList and status interface for handling packages which are
+  installable in multiple versions. (fate #305311)
+- version 6.26.0 (19)
+
+-------------------------------------------------------------------
+Wed Dec  2 13:10:43 CET 2009 - ma@suse.de
+
+- Add Selectable::highestAvailableVersionObj. Returns the highest
+  available package version, ignoring priorities and policies. (bnc #557557)
+- version 6.25.0 (19)
+
+-------------------------------------------------------------------
+Mon Nov 30 17:56:02 CET 2009 - ma@suse.de
+
+- Also parse <product> tag from .prod files <upgrade> section.
+- version 6.24.3 (19)
+
+-------------------------------------------------------------------
+Fri Nov 27 11:20:34 CET 2009 - ma@suse.de
+
+- Fix chroot execution of update scripts. (bnc #558813)
+- version 6.24.3 (19)
+
+-------------------------------------------------------------------
+Thu Nov 26 16:06:52 CET 2009 - jkupec@suse.cz
+
+- Fixed parsing of download speed from aria2c (bnc #537870)
+
+-------------------------------------------------------------------
+Wed Nov 25 16:28:29 CET 2009 - ma@suse.de
+
+- Add ui::Selecatble interface for picking specific package versions
+  to install or delete if multiversion install is on.
+- version 6.24.0 (19)
+
+-------------------------------------------------------------------
+Fri Nov 20 13:30:02 CET 2009 - ma@suse.de
+
+- Parse zypp.conf multiversion option and make the setting available
+  in pool and resolver.
+- version 6.23.0 (19)
+
+-------------------------------------------------------------------
+Mon Nov 16 16:27:39 CET 2009 - ma@suse.de
+
+- Specfile fixes to build on sle11-sp1.
+- Fix repository probing and building in presence of productdir. (bnc #553712)
+- version 6.22.3 (19)
+
+-------------------------------------------------------------------
+Thu Nov 12 15:31:10 UTC 2009 - dmacvicar@suse.de
+
+- Forward port and document already present changes
+  from Code11-Branch
+  * void SEGV if trying to access data of installed packages, that were
+    deleted behind our back (bnc #530595)
+  * ProxyInfoSysconfig: take care variables get initialized.
+  * Fix parsing of rpm.install.excludedocs option (bnc #518883)
+  * Use rpm variables in specfile. (bnc #512466)
+  * Fix to compile with -Werror=format-security
+  * Fix packageand() in testcase generation
+  * Don't link unneeded libraries. (bnc #490895)
+  * Fix Patch::categoryEnum.
+  * Adapt to changed satsolver API. (bnc #480303)
+  * Taking ALL translations for generating GMO files (bnc #458739)
+  * Advice users to contact NCC if access to a 'novell.com'
+    repository is denied (bnc #464586).
+- version 6.22.2 (19)
+
+-------------------------------------------------------------------
+Thu Nov 12 11:53:04 CET 2009 - ma@suse.de
+
+- Raised the limit of redirections from 3 to 6 (bnc #465532)
+- Following redirections also for https (bnc #545722).
+- Following https redirections requires at least libcurl4-7.19.4. (bnc #553895)
+- Do not report cached packages as being downloaded. (bnc #545295)
+- Per default do not collect and report deleted files outside bin and lib
+  directories for 'zypper ps'. (bnc #554480)
+- version 6.22.1 (19)
+
+-------------------------------------------------------------------
+Wed Nov 11 13:54:52 CET 2009 - ma@suse.de
+
+- CheckAccessDeleted: Per default do not collect and report deleted files
+  that outside bin and lib directories. 'zypper ps' reporting false positive
+  seems to confuse. (bnc #554480)
+
+-------------------------------------------------------------------
+Wed Nov 11 11:45:01 CET 2009 - ma@suse.de
+
+- Following https redirections requires at least libcurl4-7.19.4. (bnc #553895)
+
+-------------------------------------------------------------------
+Fri Nov  6 22:16:10 CET 2009 - ma@suse.de
+
+- dup: Process drop list only if product actually changes. (bnc #552180)
+- Selectable: Consider allowed arch/noarch changes when comuting candiadates.
+- version 6.22.0 (19)
+
+-------------------------------------------------------------------
+Mon Nov  2 21:33:40 CET 2009 - ma@suse.de
+
+- Enhance interface for zypper. (bnc #551956)
+- version 6.21.4 (19)
+
+-------------------------------------------------------------------
+Mon Nov  2 20:03:31 CET 2009 - ma@suse.de
+
+- CIFS/SMB: Support mountoption 'noguest' to prevent passing 'guest' option
+  to mount. "cifs://server/share/path?mountoptions=noguest,ro" (bnc #547354)
+- version 6.21.3 (19)
+
+-------------------------------------------------------------------
+Mon Nov  2 16:35:33 CET 2009 - ma@suse.de
+
+- CheckAccessDeleted: Avoid reporting false positive due to insufficient
+  permission.
+
+-------------------------------------------------------------------
+Mon Nov  2 13:59:33 CET 2009 - ma@suse.de
+
+- Don't try to access droplist of dropped products. (bnc #551697)
+- version 6.21.2 (19)
+
+-------------------------------------------------------------------
+Fri Oct 30 12:30:48 CET 2009 - ma@suse.de
+
+- Don't try to use an empty proxy string. (bnc #551314)
+- MediaSMB failed to pass the --workgroup option to mount. (bnc #547354)
+- version 6.21.1 (19)
+
+-------------------------------------------------------------------
+Fri Oct 30 11:28:05 CET 2009 - ma@suse.de
+
+- New class PoolItemBest: Find the best candidates e.g. in a PoolQuery
+  result. ui::Selectabe enhancements. Both will aid applications to
+  install package sets determined by query results. (bnc # 548392)
+- Fix upgradeRepo solution to keep obsolete packages. (bnc #550915)
+- Updated iso3166-1 country codes (bnc #531350)
+- version 6.21.0 (19)
+
+-------------------------------------------------------------------
+Tue Oct 27 12:30:29 CET 2009 - ma@suse.de
+
+- Add Resolver::upgradingRepo demanded by GUI. (bnc #548551)
+- version 6.20.0 (19)
+
+-------------------------------------------------------------------
+Thu Oct 22 15:09:57 CEST 2009 - ma@suse.de
+
+- Fixes to make libzypp-bindings compile.
+- version 6.19.3 (19)
+
+-------------------------------------------------------------------
+Tue Oct 20 16:06:13 CEST 2009 - ma@suse.de
+
+- Credentials are passed as commandline options to aria2c, so strip any
+  'user@' from the URL. Otherwise aria will use an empty password for
+  this URL and authentication will fail. (bnc #544634)
+- version 6.19.2 (19)
+
+-------------------------------------------------------------------
+Mon Oct 19 13:39:31 CEST 2009 - ma@suse.de
+
+- Repository::setInfo: Propagate priority changes to the solver to
+  avoid reloading the whole repo (bnc #498266).
+- version 6.19.1 (19)
+
+-------------------------------------------------------------------
+Thu Oct 15 20:48:02 CEST 2009 - ma@suse.de
+
+- ResStatus: add isOrphaned to test whether a package is not provided
+  by any enabled repository. Orphaned packages are usually good candidates
+  for cleanup unless the providing repository was intentionally disabled.
+- version 6.19.0 (19)
+
+-------------------------------------------------------------------
+Thu Oct 15 17:28:38 CEST 2009 - dmacvicar@suse.de
+
+- aria2: pass credentials in a file instead of the command line
+  which is logged.
+- aria2: we get the url in the progress if there is no response
+  from the server yet, handle that to avoid flooding the log.
+- version 6.18.2 (17)
+
+-------------------------------------------------------------------
+Thu Oct 15 16:56:25 CEST 2009 - ma@suse.de
+
+- Performing a dist upgrade the solver may try to delete old and no
+  longer provided (dropped) packages, even if they do not cause any
+  dependency problem. This behaviour may be trurned off via zypp.conf
+  option solver.upgradeRemoveDropedPackages. (bnc #539543)
+- New zypp.conf option solver.upgradeRemoveDropedPackages (true).
+- Add Product::droplist: List of dropped packages, i.e. packages no
+  longer provided by a product.
+- version 6.18.1 (17)
+
+-------------------------------------------------------------------
+Wed Oct  7 16:45:21 CEST 2009 - ma@suse.de
+
+- Return update messages via ZYppCommitResult. Support variable
+  substitution in notification command. (fate #301175)
+- Fix evaluation of no_proxy entries (bnc #543337)
+- aria/curl: Fix header data in case the target is
+  not initialized when downloading.
+- version 6.18.0 (17)
+
+-------------------------------------------------------------------
+Thu Sep 24 17:21:45 CEST 2009 - ma@suse.de
+
+- Add zypp.conf option update.messages.notify: Command to be invoked
+  to send update messages. (fate #301175)
+- version 6.17.2 (17)
+
+-------------------------------------------------------------------
+Tue Sep 22 20:32:38 CEST 2009 - ma@suse.de
+
+- Add Selectable::updateCandidateObj returning the candidate for
+  update, if there is one. The updateCandidate must not violate
+  any active solver policy.
+- version 6.17.1 (17)
+
+-------------------------------------------------------------------
+Fri Sep 18 17:20:37 CEST 2009 - ma@suse.de
+
+- Make sure rpmReadConfigFiles was called before using librpm (bnc #539603).
+- Remove dead rpm database caching code from class RpmDb.
+- version 6.17.0 (17)
+
+-------------------------------------------------------------------
+Fri Sep 11 15:13:30 CEST 2009 - ma@km13.de
+
+- New commit.downloadMode option in zypp.conf. Allows to set a
+  prefered download policy for commit.
+- version 6.16.0 (11)
+
+-------------------------------------------------------------------
+Thu Sep 10 19:03:01 CEST 2009 - ma@suse.de
+
+- Support nfs4 (nfs4://... or nfs://...?type=nfs4) (fate #306451)
+- Added Url::schemeIsLocal, schemeIsRemote, schemeIsVolatile and
+  schemeIsDownloading.
+- version 6.15.0 (11)
+
+-------------------------------------------------------------------
+Wed Sep  9 15:02:18 CEST 2009 - ma@suse.de
+
+- Add Capability::guessPackageSpec; parser also supporting "name-ver-rel.arch"
+  formats for building Capabilities(originally "name.arch=ver-rel").
+- version 6.14.3 (11)
+
+-------------------------------------------------------------------
+Mon Sep  7 17:31:20 CEST 2009 - ma@suse.de
+
+- Fix resolution to force installation even if dependencies are missing.
+  (bnc #531564)
+- Rephrase solver resolution to point out if a package will break.
+  (bnc #520083)
+
+-------------------------------------------------------------------
+Fri Sep  4 12:19:50 CEST 2009 - ma@suse.de
+
+- Lock rpms architecture only on distupgrade of the running system.
+  (bnc #458520)
+- version 6.14.2 (11)
+
+-------------------------------------------------------------------
+Thu Sep  3 13:41:31 CEST 2009 - ma@suse.de
+
+- Fix PoolQuery comparison (bnc #528755)
+- Fix serialization and restore of predicated PoolQueries.
+- version 6.14.0 (11)
+
+-------------------------------------------------------------------
+Mon Aug 31 15:17:01 CEST 2009 - ma@suse.de
+
+- package-manager script: Fall back to package selection if no
+  packages are passed on the commandline. (bnc #529137)
+
+-------------------------------------------------------------------
+Fri Aug 28 17:33:55 CEST 2009 - dmacvicar@suse.de
+
+- package-manager script:
+  do not fail if kpackagekit is not installed (bnc #529510)
+- version 6.13.3 (11)
+
+-------------------------------------------------------------------
+Wed Aug 26 15:55:22 CEST 2009 - ma@suse.de
+
+- Tune CheckAccessDeleted to focus on libraries and executables.
+- version 6.13.2 (11)
+
+-------------------------------------------------------------------
+Thu Aug  6 18:18:21 CEST 2009 - ma@suse.de
+
+- Provide class CheckAccessDeleted and command zypp-CheckAccessDeleted
+  to check for running processes which access meanwhile deleted files or
+  libraries.  This may be used after commit, when trying to figure out
+  which services need to be restated. (fate #300763).
+- version 6.13.1 (11)
+
+-------------------------------------------------------------------
+Mon Aug  3 18:46:13 CEST 2009 - ma@suse.de
+
+- New Resolver::addUpgradeRepo to perform a dist upgrade restricted to
+  certain repositories.
+- version 6.13.0 (11)
+
+-------------------------------------------------------------------
+Fri Jul 31 17:55:33 CEST 2009 - ma@suse.de
+
+- Remove confusing newlines in vendor change info (bnc #503859)
+- Removing a package lock was not counted as state change (bnc #501850)
+- Take solver_allowVendorChange option into account when computing the
+  Selectables default candidate.
+- version 6.12.0 (11)
+
+-------------------------------------------------------------------
+Wed Jul 29 13:36:19 CEST 2009 - ma@suse.de
+
+- Avoid deadlock after fork and failed exec. (bnc 493152)
+- No need to manually detect the location of aria2 binary.
+- version 6.11.4 (11)
+
+-------------------------------------------------------------------
+Tue Jul 28 23:05:43 CEST 2009 - jkupec@suse.cz
+
+- Fixed parsing of download rate report (changed in aria2 1.4.0)
+  (bnc #513944)
+
+-------------------------------------------------------------------
+Mon Jul 27 16:05:43 CEST 2009 - ma@suse.de
+
+- Create LogControl on demand instead of using a static var. (bnc #525339)
+- version 6.11.2 (11)
+
+-------------------------------------------------------------------
+Thu Jul 23 00:22:50 CEST 2009 - ma@km13.de
+
+- New misc::defaultLoadSystem: Convenience to create the ZYpp instance
+  and load target and enabled repositories.
+
+-------------------------------------------------------------------
+Wed Jul 22 14:57:32 CEST 2009 - ma@suse.de
+
+- New class InstanceId to build strings to identify/retrieve specific
+  Solvables.
+- version 6.11.1 (11)
+
+-------------------------------------------------------------------
+Mon Jul 20 23:57:46 CEST 2009 - ma@km13.de
+
+- Add download policies to ZYppCommitPolicy, supporting DownloadOnly
+  and DownloadInAdvance. (fate #302159, fate #305624)
+- version 6.11.0 (11)
+
+-------------------------------------------------------------------
+Thu Jul 16 17:05:27 CEST 2009 - dmacvicar@suse.de
+
+- add support to the package-manager script to use kpackagekit
+  or gnome-packagekit if available, which allows to install local
+  rpms with one click from file manager following desktop policies
+  and fetching other dependencies if required.
+  (fate #306526)
+- version 6.10.5 (10)
+
+-------------------------------------------------------------------
+Thu Jul 16 16:25:24 CEST 2009 - ma@suse.de
+
+- New solver.upgradeTestcasesToKeep option in zypp.conf. It tells
+  how many dist upgrade solver testcases should be kept on the system.
+  Per default just the last two are kept.
+- version 6.10.4 (10)
+
+-------------------------------------------------------------------
+Wed Jul 15 17:53:43 CEST 2009 - ma@suse.de
+
+- Don't write a solver testcase when solving for dist upgrade,
+  but when actually committing.
+- version 6.10.3 (10)
+
+-------------------------------------------------------------------
+Wed Jul 15 16:45:39 CEST 2009 - ma@suse.de
+
+- Add new string Match::Mode STRINGSTART and STRINGEND.
+
+-------------------------------------------------------------------
+Tue Jul 15 14:38:51 CEST 2009 - jkupec@suse.cz
+
+- log redirections when cURL media backend is used (fate #305320).
+
+-------------------------------------------------------------------
+Tue Jul 14 18:15:51 CEST 2009 - ma@suse.de
+
+- Support "product version" detection on systems not using
+  /etc/product.d/baseproduct by looking for the first package
+  providing ZConfig::distroverpkg (defaults to redhat-release).
+- version 6.10.2 (10)
+
+-------------------------------------------------------------------
+Fri Jul 10 15:21:39 CEST 2009 - ma@suse.de
+
+- Adapt to boost_unit_test_framework-1.38.
+- version 6.10.1 (10)
+
+-------------------------------------------------------------------
+Wed Jul  8 16:19:31 CEST 2009 - ma@suse.de
+
+- Remove obsolete UpgradeStatistics class from libzypp.
+
+-------------------------------------------------------------------
+Tue Jul  7 17:45:47 CEST 2009 - ma@suse.de
+
+- Fix HistoryLog to initialize on demand.
+- version 6.10.0 (10)
+
+-------------------------------------------------------------------
+Fri Jul  3 13:01:13 CEST 2009 - ma@suse.de
+
+- Fix parsing of rpm.install.excludedocs option (bnc #518883)
+
+-------------------------------------------------------------------
+Fri Jul  3 09:20:56 CEST 2009 - ma@suse.de
+
+- When unmounting ISO images, don't mix up exceptions thrown by the
+  loop mounted ISO and those thrown by the media containing it.
+  (bnc #517856)
+
+-------------------------------------------------------------------
+Thu Jul  2 16:42:42 CEST 2009 - ma@suse.de
+
+- Adapt to satsolvers improved dataiterator handling.
+- version 6.9.3 (8)
+
+-------------------------------------------------------------------
+Wed Jul  1 18:05:54 CEST 2009 - ma@suse.de
+
+- Support PoolQuery for sub-structures attributes. (fate #305503)
+- version 6.9.2 (8)
+
+-------------------------------------------------------------------
+Wed Jul  1 13:44:04 CEST 2009 - ma@suse.de
+
+- Running as non-root user use a temporary @System solvfile in case the
+  global one is outdated and needed refresh. (bnc #517183)
+- version 6.9.1 (8)
+
+-------------------------------------------------------------------
+Tue Jun 30 16:32:08 CEST 2009 - ma@suse.de
+
+- Enhance LookupAttr to allow direct query of attributes within
+  sub-structures (flexarrays).
+
+-------------------------------------------------------------------
+Fri Jun 26 12:06:51 CEST 2009 - ma@suse.de
+
+- Enhance PoolQueryIterator to allow detailed inspection of attribute
+  matches.
+- Prefer datadir stored as repo attribute, but fallback searching in
+  solvbales (old solv files do this).
+- version 6.9.0 (8)
+
+-------------------------------------------------------------------
+Tue Jun 23 13:29:36 CEST 2009 - ma@suse.de
+
+- Allow building libzypp with rpm-5 (experimental)
+- version 6.8.3 (8)
+
+-------------------------------------------------------------------
+Wed Jun 17 15:39:17 CEST 2009 - ma@suse.de
+
+- Allow building libzypp without HAL (not recommended). Without HAL
+  CD/DVD device detection is limited to /dev/dvd and /dev/cdrom.
+- version 6.8.2 (8)
+
+-------------------------------------------------------------------
+Fri Jun  5 21:16:45 CEST 2009 - ma@suse.de
+
+- Fix solver to use IdSting to avoid failing vendor checks.
+- version 6.8.1 (8)
+
+-------------------------------------------------------------------
+Thu Jun  4 14:22:58 CEST 2009 - ma@suse.de
+
+- Cleanup and remove deprecated interface methods.
+- version 6.8.0 (8)
+
+-------------------------------------------------------------------
+Fri May 29 20:12:55 CEST 2009 - ma@suse.de
+
+- Improve PoolQuery to allow queries on dependencies. (bnc #475682)
+- version 6.7.0 (6)
+
+-------------------------------------------------------------------
+Thu May 28 12:53:02 CEST 2009 - ma@suse.de
+
+- New solver.allowVendorChange expert option in zypp.conf.
+- version 6.6.0 (6)
+
+------------------------------------------------------------------
+Wed May 20 14:23:07 CEST 2009 - ma@suse.de
+
+- Fix lost housekeeping data in modifyRepo (bnc #503207)
+
+-------------------------------------------------------------------
+Fri May  8 16:43:47 CEST 2009 - ma@suse.de
+
+- Allow service refresh to change a repositories url (bnc #502157)
+
+-------------------------------------------------------------------
+Tue May  5 13:39:08 CEST 2009 - ma@suse.de
+
+- Detect and compile with rpm 4.7 (bnc #444211)
+- version 6.5.2 (5)
+
+-------------------------------------------------------------------
+Mon May  4 16:19:54 CEST 2009 - ma@suse.de
+
+- Improve problem report on broken systemCheck rule (bnc #475144)
+
+-------------------------------------------------------------------
+Mon Apr 27 15:26:54 CEST 2009 - ma@suse.de
+
+- In update repos providing multiple release package versions for
+  the same product, link a product to the latest version. (bnc #497696)
+
+-------------------------------------------------------------------
+Mon Apr 27 14:28:07 CEST 2009 - ma@suse.de
+
+- New classes wraping satsolver datamatcher (Match and sat::AttrMatcher)
+- Extend LookupAttr to support matching specific string patterns.
+- Rewrote PoolQuery::Iterator (adapt to AttrMatcher, fixes and speedup)
+- version 6.5.0 (5)
+
+-------------------------------------------------------------------
+Thu Apr 16 12:49:40 CEST 2009 - ma@suse.de
+
+- Soft lock packages deleted on behalf of a user request.
+- version 6.4.1 (2)
+
+-------------------------------------------------------------------
+Tue Mar 31 15:51:32 CEST 2009 - ma@suse.de
+
+- New zypp.conf option 'download.media_preference': Hint which media
+  to prefer when installing packages (download vs. CD).
+- version 6.4.0 (2)
+
+------------------------------------------------------------------
+Thu Mar 12 18:38:30 CET 2009 - ma@suse.de
+
+- Add Resolver::setSolveSrcPackages. Per default disable solving
+  of source package dependencies. We will later allow to enable
+  it per package.
+- version 6.3.0 (2)
+
+-------------------------------------------------------------------
+Fri Mar  6 17:17:26 CET 2009 - dmacvicar@suse.de
+
+- aria2: show the download speed in the right unit
+- aria2: show the filename in progress, not the repository
+- aria2: don't show done twice
+
+-------------------------------------------------------------------
+Thu Mar  5 14:52:44 CET 2009 - ma@suse.de
+
+- Remove a lock if the locking process is in zombie state. (bnc #481577)
+
+-------------------------------------------------------------------
+Wed Mar  4 00:13:19 CET 2009 - jkupec@suse.cz
+
+- zypp.conf: fixed and enabled 'servicesdir'
+
+-------------------------------------------------------------------
+Tue Mar  3 13:38:11 CET 2009 - dmacvicar@suse.de
+
+- aria2: implement speed indicators (bnc#475506)
+- aria2: implement progress indicators correctly (bnc#473846)
+- aria2: fix broken pipe when looking for aria2c which caused
+         a fallback to curl. (bnc#480930)
+- aria2: implement saving and reading mirror stats data in
+         /var/cache/zypp/aria2.stats
+- aria2: handle failover correctly (bnc#481115)
+- aria2: various improvements in error and report  handling
+- aria2: curl: reset settings on attach to avoid duplicate
+  headers
+- version 6.2.1 (2)
+
+-------------------------------------------------------------------
+Tue Mar  3 12:44:24 CET 2009 - ma@suse.de
+
+- Adapt to changed satsolver API.
+
+-------------------------------------------------------------------
+Fri Feb 27 16:23:36 CET 2009 - dmacvicar@suse.de
+
+- Make sure Fetcher pass optional files as non-interactive
+- Fixes file does not exist error when key/sig does not exist
+- version 6.2.0 (2)
+
+-------------------------------------------------------------------
+Thu Feb 26 13:49:56 CET 2009 - ma@suse.de
+
+- Use correct default for zconfig(solver.checkSystemFile) (bnc# 475144)
+
+-------------------------------------------------------------------
+Thu Feb 26 01:25:33 CET 2009 - ma@suse.de
+
+- Prevent ResStatus from overriding user locks. (bnc #475230)
+
+-------------------------------------------------------------------
+Sun Feb 22 15:27:11 CET 2009 - ma@suse.de
+
+- Never refresh repositories from CD/DVD, once they are created. (bnc #476429)
+
+-------------------------------------------------------------------
+Sat Feb 21 18:31:17 CET 2009 - dmacvicar@suse.de
+
+Implemented the following options with aria backend:
+
+- download.max_concurrent_connections (default 2)
+  download.min_download_speed (default no limit)
+  download.max_download_speed (default no limit)
+  download.max_silent_tries (default 5)
+
+-------------------------------------------------------------------
+Fri Feb 20 16:28:19 CET 2009 - dmacvicar@suse.de
+
+- MediaAria2c: allow disabling aria2 using ZYPP_ARIA2C=0.
+  Various improvements including file existence checking
+  Disable HEAD request if aria2c >= 1.20
+  Restrict max connections to 2 for now.
+
+-------------------------------------------------------------------
+Wed Feb 18 15:51:38 CET 2009 - ma@suse.de
+
+- Neither lose packages with empty name, nor SEGV when processing them. (bnc #470011)
+
+-------------------------------------------------------------------
+Tue Feb 17 23:17:54 CET 2009 - ma@suse.de
+
+- Compute obsoletes based on names only (not considering provides) (bnc #471023)
+
+-------------------------------------------------------------------
+Tue Feb 17 12:56:57 CET 2009 - jkupec@suse.cz
+
+- Fixed FTP authentication (bnc #472879)
+
+-------------------------------------------------------------------
+Wed Feb 11 13:31:03 CET 2009 - ma@suse.de
+
+- Follow IEC and use the binary prefixes (KiB,MiB,GiB) for printing Byte
+  counts based on a power of 1024 (formerly just K,M,G). Byte counts based
+  on a power of 1000 stay unaffected (kB,MB,GB).
+
+-------------------------------------------------------------------
+Mon Feb  9 13:14:26 CET 2009 - ma@suse.de
+
+- Fix installation prompting for the wrong CD/DVD. (bnc #472892)
+
+-------------------------------------------------------------------
+Tue Feb  3 18:07:38 CET 2009 - ma@suse.de
+
+- Send any output from rpm install/delete scripts via callback, so
+  applications are able to display it. (bnc #369450)
+
+-------------------------------------------------------------------
+Mon Feb  2 14:12:08 CET 2009 - ma@suse.de
+
+- Add missing translations (bnc #256289)
+
+-------------------------------------------------------------------
+Wed Jan 28 14:37:28 CET 2009 - jkupec@suse.cz
+
+- HistoryLogReader added
+
+-------------------------------------------------------------------
+Tue Jan 27 13:35:44 CET 2009 - ma@suse.de
+
+- Respect content-file DATDIR when downloading packages. (bnc #468612)
+
+-------------------------------------------------------------------
+Tue Jan 27 13:28:54 CET 2009 - jkupec@suse.cz
+
+- Enabled CredentialManager for MediaSMB (bnc #460970).
+- Ignore URL's username, password, and query string in AuthData
+  comparator in CredentialManager.
+
+-------------------------------------------------------------------
+Mon Jan 26 12:41:07 CET 2009 - ma@suse.de
+
+- Let vendor checks per default differ between 'openSUSE Build Service'
+  and 'openSUSE' (bnc #467262).
+
+-------------------------------------------------------------------
+Fri Jan 23 12:34:44 CET 2009 - ma@suse.de
+
+- Fix handling of plaindir repos on mounted devices (e.g. USB) (bnc #464778)
+- Fix plaindir checksum computation not to to follow symlinks (bnc #464778)
+
+-------------------------------------------------------------------
+Thu Jan 22 10:41:27 CET 2009 - ma@suse.de
+
+- Tell satsolver about product buddies (bnc #466565)
+
+-------------------------------------------------------------------
+Fri Jan  9 17:01:10 CET 2009 - jkupec@suse.cz
+
+- handle HTTP 503 reponses as temporary errors (bnc #462545)
+
+-------------------------------------------------------------------
+Thu Dec 18 22:26:18 CET 2008 - ma@suse.de
+
+- Fixed lost user request to abort during commit. (bnc #388810, bnc #450273)
+- revision 11954
+- version 5.25.0 (23)
+
+-------------------------------------------------------------------
+Thu Dec 18 13:02:27 CET 2008 - ma@suse.de
+
+- Add Package::filelist, faster and less memory consuming
+  implementation of Package::filenames (now deprecated).
+- revision 11949
+
+-------------------------------------------------------------------
+Thu Dec 11 21:39:50 CET 2008 - ma@suse.de
+
+- Add str::hexencode and str::hexdecode to encode special characters
+  in a string as %XX.
+- Hexdecode modalias strings in rpm dependencies because rpm does not
+  allow comma, blank and other special chars. (bnc #456695)
+- revision 11927
+
+-------------------------------------------------------------------
+Thu Dec 11 17:13:06 CET 2008 - ma@suse.de
+
+- Catch and report media errors when proving packages. (bnc #457652)
+- revision 11926
+
+-------------------------------------------------------------------
+Wed Dec 10 16:09:08 CET 2008 - ma@suse.de
+
+- Remove obsolete zypp.conf::productsdir and deprecate
+  ZConfig::productsPath().
+- Monitor /etc/products.d to determine if @system.solv needs to be
+  rebuilt. (bnc #457933)
+- revision 11923
+- version 5.24.7 (23)
+
+-------------------------------------------------------------------
+Mon Dec  8 15:31:18 CET 2008 - ma@suse.de
+
+- Execute patch scripts chroot to the installed system. (bnc #456795)
+- revision 11908
+
+-------------------------------------------------------------------
+Mon Dec  8 10:39:30 CET 2008 - schubi@suse.de
+
+- Make the solver reset function public (bnc #439373)
+- Revision 11904
+
+-------------------------------------------------------------------
+Sun Dec  7 20:21:09 CET 2008 - coolo@suse.de
+
+- note for coolo: do not trust bash advisory from TPM colleagues
+
+-------------------------------------------------------------------
+Fri Dec  5 14:22:11 CET 2008 - ma@suse.de
+
+- Fix solvers inappropriate selection as byUSER (bnc 455965)
+- revision 11891
+- version 5.24.6 (23)
+
+-------------------------------------------------------------------
+Thu Dec  4 11:10:35 CET 2008 - coolo@suse.de
+
+- fix %post script to not warn on fresh install
+
+-------------------------------------------------------------------
+Mon Dec  1 19:38:24 CET 2008 - ma@suse.de
+
+- Fix install order computation losing some installed packages
+  pre-requirements. (bnc #439802)
+- revision 11845
+- version 5.24.5 (23)
+
+-------------------------------------------------------------------
+Fri Nov 28 16:33:56 CET 2008 - ma@suse.de
+
+- Prune soft locks to prevent installation but not update of
+  already installed packages.
+- revision 11829
+- version 5.24.4 (23)
+
+-------------------------------------------------------------------
+Fri Nov 28 11:36:03 CET 2008 - schubi@suse.de
+
+- Taking solutions which based on user requirements/conflict
+  "by User" solutions (bnc #442718)
+- revision 11825
+
+-------------------------------------------------------------------
+Fri Nov 28 00:05:17 CET 2008 - ma@suse.de
+
+- Take into account the requirements of all obsoleted packages uninstall
+  scripts when computing the installation order. (bnc #439802)
+- revision 11823
+- version 5.24.3 (23)
+
+-------------------------------------------------------------------
+Thu Nov 27 14:45:07 CET 2008 - dmacvicar@suse.de
+
+- fix maybeUnsuported() method returning wrong result
+- add testcase for future coverage
+- don't force time based uuid for anonymous id string (bnc #449654)
+- RELEASE: 5.24.2 (23)
+
+-------------------------------------------------------------------
+Wed Nov 26 17:18:00 CET 2008 - ma@suse.de
+
+- Call 'repo2solv -R' (recursive scan) for plaindir repos. (bnc #443350)
+- revision 11810
+
+-------------------------------------------------------------------
+Wed Nov 26 17:09:12 CET 2008 - dmacvicar@suse.de
+
+- SHA1SUMS.key is not imported by zypp as known key (bnc #446188)
+- path and url in add_on_products.xml is evaluated wrong
+  (bnc #446170)
+
+-------------------------------------------------------------------
+Mon Nov 24 17:47:35 CET 2008 - schubi@suse.de
+
+- activate locking for doUpdate (bnc #447684)
+- revision 11792
+- RELEASE: 5.24.1 (23)
+
+-------------------------------------------------------------------
+Fri Nov 21 16:56:10 CET 2008 - dmacvicar@suse.de
+
+- remove unused updaterepokey, replaced by repo
+  product information
+
+-------------------------------------------------------------------
+Fri Nov 21 15:12:52 CET 2008 - jkupec@suse.cz
+
+- fixed uninitialized value in OnMediaLocation (bnc #447010)
+- revision 11770
+
+-------------------------------------------------------------------
+Thu Nov 20 23:59:16 CET 2008 - ma@suse.de
+
+- Fix retrieval of deltarpm info.
+- revision 11764
+- version 5.24.0 (23)
+
+-------------------------------------------------------------------
+Thu Nov 20 22:15:28 CET 2008 - ma@suse.de
+
+- Fix retrieval of Repository attributes like timestamps, keywords
+  and product info.
+- revision 11760
+
+-------------------------------------------------------------------
+Thu Nov 20 19:30:05 CET 2008 - ma@suse.de
+
+- Enhance class LookupAttr and add convenience class LookupRepoAttr to
+  iterate those solv file attributes which are not acssociated with a
+  solvable. E.g. product or deltarpm info.
+- revision 11754
+
+-------------------------------------------------------------------
+Wed Nov 19 15:49:38 CET 2008 - jkupec@suse.cz
+
+- encode user-supplied URL strings before using them in the Url object
+  (bnc #446395, bnc #444267)
+- revision 11720
+
+-------------------------------------------------------------------
+Wed Nov 19 15:15:14 CET 2008 - ma@suse.de
+
+- Support loading helix files.
+- revision 11719
+
+-------------------------------------------------------------------
+Tue Nov 18 18:37:30 CET 2008 - ma@suse.de
+
+- Add class filesystem::Glob to find pathnames matching a pattern
+  by using ::glob.
+- revision 11708
+
+-------------------------------------------------------------------
+Thu Nov 13 11:07:56 CET 2008 - schubi@suse.de
+
+- Taking care for ppc64 while distupgrade (bnc #443685)
+- revision 11670
+
+-------------------------------------------------------------------
+Wed Nov 12 18:53:03 CET 2008 - ma@suse.de
+
+- Add Capability ctor from Arch and Name: (Arch_i386, "name") or
+  (Arch_i386, "name == 1.0").
+- revision 11669
+
+-------------------------------------------------------------------
+Wed Nov 12 14:31:20 CET 2008 - ma@suse.de
+
+- Take care to always reset CURLOPT_TIMECONDITION to prevent incomplete
+  downloads. (bnc #444109)
+- revision 11656
+- version 5.23.0 (23)
+
+-------------------------------------------------------------------
+Wed Nov 12 13:19:02 CET 2008 - ma@suse.de
+
+- Check for modaliases below /sys (bnc #430179)
+- revision 11653
+
+-------------------------------------------------------------------
+Tue Nov 11 18:36:33 CET 2008 - ma@suse.de
+
+- Avoid superfluous file copying and gpg invocation in keyring handling.
+- revision 11650
+
+-------------------------------------------------------------------
+Tue Nov 11 14:48:52 CET 2008 - ma@suse.de
+
+- Prevent fetcher from processing the same index file twice. (bnc #443644)
+- revision 11648
+- version 5.22.0 (21)
+
+-------------------------------------------------------------------
+Fri Nov  7 23:55:47 CET 2008 - ma@suse.de
+
+- Fix retrieval of patch contents and references attributes. (bnc #442200)
+- revision 11641
+- version 5.21.0 (21)
+
+-------------------------------------------------------------------
+Fri Nov  7 19:51:00 CET 2008 - ma@suse.de
+
+- Add CheckSum::asString.
+- revision 11634
+
+-------------------------------------------------------------------
+Fri Nov  7 17:55:48 CET 2008 - ma@suse.de
+
+- revision 11631
+- version 5.20.0 (20)
+
+-------------------------------------------------------------------
+Thu Nov  6 18:35:25 CET 2008 - dmacvicar@suse.de
+
+- re-add the flavor to the http header now using a flavor
+  cache that is updated on every target load.
+  Target::dstributionFlavor provides access to this cache
+  in case you need the last used flavor without loading
+  the target.
+
+-------------------------------------------------------------------
+Thu Nov  6 16:23:03 CET 2008 - schubi@suse.de
+
+- added flag: ignorealreadyrecommended to the testcases (bnc #432136)
+- revsion 11539
+
+-------------------------------------------------------------------
+Thu Nov  6 09:41:48 CET 2008 - schubi@suse.de
+
+- Adding rule rpm-arch for installed rpm package in order to avoid
+  unneeded architecture change. (bnc #441004)
+- revision 11585
+
+-------------------------------------------------------------------
+Tue Nov  4 13:21:35 CET 2008 - jkupec@suse.cz
+
+- handle MediaTimeoutException also in MediaSetAccess::provideFile()
+  (bnc #439983)
+- revision 11568
+
+-------------------------------------------------------------------
+Mon Nov  3 15:30:53 CET 2008 - dmacvicar@suse.de
+
+- merge contributions by Jon Nelson, the patches resolve
+  the following issues:
+- disable the "Pragma: nocache" header which is automatically
+  used by curl.
+  re-enables the use of a caching http proxy (like squid or others)
+  and corrects (bnc #326208)
+- don't generate an If-Modified-Since header if we don't have a
+  previous file to work with
+- don't generate a Proxy-Authenticate header if one is not called
+  for.
+
+-------------------------------------------------------------------
+Fri Oct 31 14:01:57 CET 2008 - ma@suse.de
+
+- Do not save solver locks (by APPL_HIGH).
+- revision 11558
+
+-------------------------------------------------------------------
+Fri Oct 31 13:53:39 CET 2008 - dmacvicar@suse.de
+
+- fetcher.setOptions( Fetcher::AutoAddIndexes ) allows
+  for automatic signed index discovery.
+- enqueueDir with checksum checking s now enqueueDigestedDir
+
+-------------------------------------------------------------------
+Thu Oct 30 14:24:57 CET 2008 - schubi@suse.de
+
+- solutions: keep/lock will be done by APPL_HIGH. So they will not be
+  saved in the lockfiles
+- "keep obsolete" will be handled with lock by APPL_HIGH (bnc #439134)
+- revision  11549
+
+-------------------------------------------------------------------
+Wed Oct 29 14:14:16 CET 2008 - jkupec@suse.cz
+
+- throw a MediaTimeoutException also on http 504 (gateway timeout)
+  (bnc #425035)
+- revision 11535
+
+-------------------------------------------------------------------
+Wed Oct 29 12:51:55 CET 2008 - ma@suse.de
+
+- Add 'sh4' architectures.
+- revision 11534
+
+-------------------------------------------------------------------
+Tue Oct 28 22:01:40 CET 2008 - dmacvicar@suse.de
+
+- don't free the header before curl_perform as curl does not
+  copy it. (bnc#439532)
+
+-------------------------------------------------------------------
+Tue Oct 28 16:47:36 CET 2008 - ma@suse.de
+
+- Add 'arm' architectures.
+- revision 11525
+
+-------------------------------------------------------------------
+Tue Oct 28 10:07:13 CET 2008 - schubi@suse.de
+
+- regarding "keep obsolete" in the solutions (bnc #439134)
+- revision 11517
+
+-------------------------------------------------------------------
+Mon Oct 27 10:51:01 CET 2008 - dmacvicar@suse.de
+
+- fix broken aria2c command line (bnc#438971)
+
+-------------------------------------------------------------------
+Sun Oct 26 14:53:56 CET 2008 - coolo@suse.de
+
+- adding svn r11488 to fix compile of PackageKit
+
+-------------------------------------------------------------------
+Fri Oct 24 18:12:34 CEST 2008 - ma@suse.de
+
+- Remove error prone methods from OnMediaLocation API to prevent
+  accidental missuse. (bnc #437328)
+- revision 11487
+- version 5.19.0 (19)
+
+-------------------------------------------------------------------
+Fri Oct 24 16:07:45 CEST 2008 - ma@suse.de
+
+- Provide the /etc/products.d enties filename as Product::referenceFilename.
+  Use it to remove orphan products. (bnc #432932)
+- Fix media exception handling in commit (bnc #395704)
+- revision 11485
+- version 5.18.0 (17)
+
+-------------------------------------------------------------------
+Fri Oct 24 10:29:19 CEST 2008 - schubi@suse.de
+
+- Taking "unlock" instead of setTransact(false) in the solutions (bnc #436923)
+- revision 11468
+- version 5.17.0 (17)
+
+-------------------------------------------------------------------
+Wed Oct 22 14:33:10 CEST 2008 - ma@suse.de
+
+- For retrieving a product license fall back to license.tar.gz. (bnc #372386)
+- revision 11447
+
+-------------------------------------------------------------------
+Wed Oct 22 14:16:33 CEST 2008 - dmacvicar@suse.de
+
+- move anonymous unique id to a private http header
+  X-ZYpp-AnonymousUniqueId (bnc#431571 )
+
+-------------------------------------------------------------------
+Wed Oct 22 12:58:35 CEST 2008 - ma@suse.de
+
+- Adapt to satsolver-0.12 API.
+- Add Locale:: bestMatch to find the best match within a set of
+  available Locales.
+- revision 11441
+
+-------------------------------------------------------------------
+Mon Oct 20 16:44:58 CEST 2008 - ma@suse.de
+
+- RepoInfo: Add methods to handle repository licenses. (bnc #372386)
+- revision 11419
+
+-------------------------------------------------------------------
+Mon Oct 20 16:39:41 CEST 2008 - ma@suse.de
+
+- Adapt to changed satsolver dataiterator API.
+- revision 11418
+
+-------------------------------------------------------------------
+Fri Oct 17 18:31:47 CEST 2008 - ma@suse.de
+
+- Secure download of license file on repo refresh (bnc #372386)
+- revision 11398
+
+-------------------------------------------------------------------
+Fri Oct 17 17:26:25 CEST 2008 - ma@suse.de
+
+- Call 'repo2solv.sh -o' instead of using output redirection. (bnc #420046)
+- revision 11397
+
+-------------------------------------------------------------------
+Thu Oct 16 17:57:15 CEST 2008 - jkupec@suse.cz
+
+- repository license methods added to RepoManager (bnc #372386)
+- revision 11377
+
+-------------------------------------------------------------------
+Thu Oct 16 16:26:01 CEST 2008 - dmacvicar@suse.de
+
+- don't take into account stat information when looking
+  for remote SHA1SUMS (part of bnc#409927)
+
+-------------------------------------------------------------------
+Wed Oct 15 18:01:48 CEST 2008 - jkupec@suse.cz
+
+- MediaSetAccess::provideOptionalFile() added
+- revision 11369
+
+-------------------------------------------------------------------
+Wed Oct 15 18:01:48 CEST 2008 - jkupec@suse.cz
+- version 5.16.1 (16)
+
+-------------------------------------------------------------------
+Tue Oct 14 16:15:09 CEST 2008 - jkupec@suse.cz
+
+- don't initialize servicesTargetDistro via global ZYpp instance in
+  RepoManagerOptions() constructor (bnc #435184)
+- revision 11342
+
+-------------------------------------------------------------------
+Mon Oct 13 19:12:40 CEST 2008 - jkupec@suse.cz
+
+- repository probing: check for other types of repo even on
+  MediaException, throw only if all fail (bnc #335906)
+- revision 11332
+
+-------------------------------------------------------------------
+Mon Oct 13 14:16:25 CEST 2008 - ma@suse.de
+
+- Adapt to satsolver changes.
+- revision 11331
+- version 5.16.0 (16)
+
+-------------------------------------------------------------------
+Mon Oct 13 13:28:48 CEST 2008 - ma@suse.de
+
+- Fix reading delta rpm checksum from solv file.
+- revision 11313
+
+-------------------------------------------------------------------
+Mon Oct 13 12:12:39 CEST 2008 - ma@suse.de
+
+- Fix package-manager script to properly quote arguments. (bnc #30903)
+- revision 11311
+
+-------------------------------------------------------------------
+Mon Oct 13 11:03:04 CEST 2008 - schubi@suse.de
+
+- regarding orphaned resolvables
+- removed old distupgrade algorithm
+- revision 11307
+
+-------------------------------------------------------------------
+Sun Oct 12 13:55:51 CEST 2008 - jkupec@suse.cz
+
+- handle ftp response 530 (login failed) like http 403
+  (bnc #433537)
+- revision 11305
+
+-------------------------------------------------------------------
+Thu Oct  9 17:37:18 CEST 2008 - ma@suse.de
+
+- Adapt to satsolvers 'big solv data change'.
+- revision 11294
+
+-------------------------------------------------------------------
+Thu Oct  9 17:18:18 CEST 2008 - schubi@suse.de
+
+- Taking the right solution actions for locked resolvables (bnc #400840)
+- revision 11289
+
+-------------------------------------------------------------------
+Thu Oct  9 13:52:03 CEST 2008 - ma@suse.de
+
+- Add required copy-ctor and assignment operator to WhatProvides.
+  (bnc #433087)
+- revision 11276
+
+-------------------------------------------------------------------
+Wed Oct  8 16:24:25 CEST 2008 - jkupec@suse.cz
+
+- detect and use unused loop device for mounting ISO images
+  (bnc #428009)
+- revision 11262
+- version 5.15.1 (13)
+
+-------------------------------------------------------------------
+Wed Oct  8 15:39:08 CEST 2008 - jkupec@suse.cz
+
+- fixed segfault when saving the probed repo type in
+  RepoManager::refreshMetadata() (bnc #431963)
+- revision 11261
+
+-------------------------------------------------------------------
+Wed Oct  8 11:33:00 CEST 2008 - dmacvicar@suse.de
+
+- improve log message (bnc#429114)
+
+-------------------------------------------------------------------
+Wed Oct  8 11:15:20 CEST 2008 - lslezak@suse.cz
+
+- disk usage - ignore /proc filesystem (bnc#418783)
+- revision 11258
+
+-------------------------------------------------------------------
+Tue Oct  7 13:24:10 CEST 2008 - ma@suse.de
+
+- Fixed detection of loopback mounted iso-files. The mtab entry does
+  not necessarily mention the iso-file. (bnc #432504)
+- revision 11252
+- version 5.15.0 (13)
+
+-------------------------------------------------------------------
+Thu Oct  2 19:46:13 CEST 2008 - ma@suse.de
+
+- Add zypp.conf option 'download.use_deltarpmr.always' to enable using
+  delta rpms even if the package is available on a local source.
+  (Axel C. Frinke)
+- revision 11235
+- version 5.14.0 (13)
+
+-------------------------------------------------------------------
+Thu Oct  2 18:50:11 CEST 2008 - ma@suse.de
+
+- Add product attribute: PRODUCTLINE.
+- revision 11234
+
+-------------------------------------------------------------------
+Thu Oct  2 17:18:19 CEST 2008 - ma@suse.de
+
+- Do not consider self provided requirements whan computing the
+  installation order.
+- revision 11231
+
+-------------------------------------------------------------------
+Wed Oct  1 16:07:36 CEST 2008 - ma@suse.de
+
+- Rephrase missleading error message. (bnc #431229)
+- revision 11222
+
+-------------------------------------------------------------------
+Tue Sep 30 17:23:48 CEST 2008 - ma@suse.de
+
+- Allow computation of disk usage per solvable.
+- revision 11218
+
+-------------------------------------------------------------------
+Mon Sep 29 23:59:10 CEST 2008 - dmacvicar@suse.de
+
+- Merge aria2c Media handler code from Google SOC 2008
+  (Gerard Farras)
+- Only activated by making env var ZYPP_ARIA=1
+
+-------------------------------------------------------------------
+Mon Sep 29 15:56:17 CEST 2008 - jkupec@suse.cz
+
+- history: tell which package failed before logging rpm output or
+  error message (bnc #430585)
+- no localization for history log comments
+- revision 11202
+
+-------------------------------------------------------------------
+Mon Sep 29 13:43:08 CEST 2008 - ma@suse.de
+
+- Fix zypp::WhatProvides::empty returning inverse result.
+- revision 11200
+- version 5.13.1 (13)
+
+-------------------------------------------------------------------
+Sat Sep 27 23:36:19 CEST 2008 - jkupec@suse.cz
+
+- fixed endless loop when CredentialManager returns bad password
+- CredentialManager now correctly updates password of existing
+  credentials
+- revision 11187
+
+-------------------------------------------------------------------
+Fri Sep 26 15:37:50 CEST 2008 - ma@suse.de
+
+- Fix computation of Product::flavor.
+- Add Product::productLine. A vendor specific string denoting the
+  product line.
+- revision 11182
+- version 5.13.0 (13)
+
+-------------------------------------------------------------------
+Fri Sep 26 15:25:33 CEST 2008 - jkupec@suse.cz
+
+- save user credentials after asking for them
+- revision 11181
+
+-------------------------------------------------------------------
+Thu Sep 25 23:19:55 CEST 2008 - jkupec@suse.cz
+
+- ZConfig::credentialsGlobal{File,Dir}() added
+  (/etc/zypp/credentials.{cat,d})
+- revision 11176
+
+-------------------------------------------------------------------
+Thu Sep 25 17:09:44 CEST 2008 - jkupec@suse.cz
+
+- Target::setInstallationLogfile() removed from zypp/Target.h
+- revision 11171
+
+-------------------------------------------------------------------
+Thu Sep 25 13:11:43 CEST 2008 - ma@suse.de
+
+- Remove obsolete zypp-query-pool binary. zypper provides all
+  the information one needs.
+- revision 11160
+
+-------------------------------------------------------------------
+Wed Sep 24 19:11:19 CEST 2008 - jkupec@suse.cz
+
+- HistoryLog added and used to log package installs/removes and
+  repository adds, removes, url and alias changes into
+  history.logfile (/var/log/zypp/history) (fate #110205)
+- str::escape(string, char) added
+- revision 11150
+- version 5.12.1 (12)
+
+-------------------------------------------------------------------
+Tue Sep 23 14:02:08 CEST 2008 - jkupec@suse.cz
+
+- CredentialManager: look for credentials with
+  wanted_url.startsWith(stored_url)
+- RepoManager: don't pass service credentials down to repos if
+  their urls are not based on service's url.
+- revision 11134
+
+-------------------------------------------------------------------
+Mon Sep 22 16:27:41 CEST 2008 - ma@suse.de
+
+- Remove superfluous PRODUCT_REFERENCES attribute.
+- revision 11127
+
+-------------------------------------------------------------------
+Mon Sep 22 16:13:08 CEST 2008 - ma@suse.de
+
+- Add Url::hasCredentialsInAuthority test for username or password
+  being encoded in the authority component. I.e. user:pass@host.
+- Handle repos to be disabled disable in service refresh.
+- revision 11126
+
+-------------------------------------------------------------------
+Mon Sep 22 12:10:34 CEST 2008 - jkupec@suse.cz
+
+- provided context to keyring callbacks (bnc #370223)
+- merged trust & import callbacks (bnc #366467)
+  (don't worry, still allows to trust && !import)
+- revision 11113
+
+-------------------------------------------------------------------
+Fri Sep 19 13:17:38 CEST 2008 - ma@suse.de
+
+- Moved DefaultAcceptBits enum to class KeyRing (formerly KeyRingReort).
+- revision 11102
+
+-------------------------------------------------------------------
+Thu Sep 18 16:11:23 CEST 2008 - ma@suse.de
+
+- Use service alias as namespace for it's repositories aliases.
+- revision 11097
+- version 5.11.0 (11)
+
+-------------------------------------------------------------------
+Thu Sep 18 12:45:25 CEST 2008 - jkupec@suse.cz
+
+- pass service's credentials down to repos
+- support ?credentials=filepath in URL to specify credentials
+- revision 11092
+
+-------------------------------------------------------------------
+Thu Sep 18 09:57:49 CEST 2008 - ma@suse.de
+
+- Add Product::isTargetDistribution to identify the systems installed
+  baseproduct. This should replace tests for Product::type is "base".
+- revision 11089
+
+-------------------------------------------------------------------
+Wed Sep 17 18:05:44 CEST 2008 - jkupec@suse.cz
+
+- save user credentials when adding repos/services with URLs containing
+  the credentials (bnc #425462)
+- revision 11085
+
+-------------------------------------------------------------------
+Wed Sep 17 15:08:54 CEST 2008 - ma@suse.de
+
+- Adapt to rpms new way of quoting whitespace in pathnames.(bnc #426924)
+- revision 11082
+
+-------------------------------------------------------------------
+Tue Sep 16 18:41:39 CEST 2008 - ma@suse.de
+
+- Service handling fixes. Added ServiceException.
+- revision 11077
+
+-------------------------------------------------------------------
+Mon Sep 15 11:40:04 CEST 2008 - ma@suse.de
+
+- Fix building of transaltions.
+- revision 11064
+
+-------------------------------------------------------------------
+Sun Sep 14 00:10:21 CEST 2008 - jkupec@suse.cz
+
+- ServiceInfo.clearReposTo{Enable,Disable}() methods added
+- revision 11060
+
+-------------------------------------------------------------------
+Fri Sep 12 18:24:28 CEST 2008 - ma@suse.de
+
+- Create new Service repos in disbaled sate.
+- revision 11056
+- version 5.10.0 (10)
+
+-------------------------------------------------------------------
+Fri Sep 12 16:04:54 CEST 2008 - ma@suse.de
+
+- Make registerTarget and registerRelease abvailable for installed
+  product. Required for registration.
+- revision 11043
+
+-------------------------------------------------------------------
+Fri Sep 12 12:59:50 CEST 2008 - ma@suse.de
+
+- Add url lists query to Product interface. A generic query and
+  convenience methods to query urls for "releasenotes", "register",
+  "updateurls", "extraurls",  "optionalurls" and "smolt" (bnc #413444)
+- revision 11029
+
+-------------------------------------------------------------------
+Fri Sep 12 01:38:25 CEST 2008 - jkupec@suse.cz
+
+- ServiceType and ServiceInfo::type() added (contains only RIS for
+  now), service type probing added.
+- Avoiding the use of 'path' for services (appending
+  the repoindex.xml's 'path' to the baseurl of created repos)
+- renamed ServiceInfo::*catalog*() methods to *repo*() methods
+- revision 11022
+
+-------------------------------------------------------------------
+Thu Sep 11 16:01:57 CEST 2008 - dmacvicar@suse.de
+
+- add Repostiroy::updateKeys and
+  Repository::providesUpdatesForKey(string) for repo and
+  product matching
+- Provide Repository::isUpdateRepo
+
+-------------------------------------------------------------------
+Thu Sep 11 14:41:28 CEST 2008 - ma@suse.de
+
+- Fix evaluation of vendor support flags.
+- Adapt retrieval of registration data (targetDistribution,
+  targetDistributionRelease and targetDistributionFlavor).
+- revision 11013
+
+-------------------------------------------------------------------
+Wed Sep 10 18:23:52 CEST 2008 - ma@suse.de
+
+- Follow gpgcheck tag in .repo file and do no check if disabled.
+- revision 11010
+- version 5.9.0 (8)
+
+-------------------------------------------------------------------
+Wed Sep 10 17:54:20 CEST 2008 - jkupec@suse.cz
+
+- added dumpAsXMLOn(stream,string) to ServiceInfo to print services
+  with content (repos)
+- revision 11004
+
+-------------------------------------------------------------------
+Wed Sep 10 17:31:41 CEST 2008 - ma@suse.de
+
+- Remove obsolete product attributes.
+- Store less packages in /var/lib/zypp/SoftLocks (bnc #418050)
+- revision 11001
+
+-------------------------------------------------------------------
+Wed Sep 10 16:40:00 CEST 2008 - ma@suse.de
+
+- Provide product::updaterepoKey: Update repository indicator string.
+- revision 11000
+
+-------------------------------------------------------------------
+Tue Sep  9 17:33:03 CEST 2008 - dmacvicar@suse.de
+
+- provide context about the repository (name/alias) if available when
+  checking signatures (bnc#370223)
+- 5.8.0
+
+-------------------------------------------------------------------
+Tue Sep  9 13:04:11 CEST 2008 - ma@suse.de
+
+- Add ServiceInfo interface to define a set of catalogs (repository
+  aliases) to be enabled on next refresh.
+- revision 10970
+
+-------------------------------------------------------------------
+Tue Sep  9 11:40:42 CEST 2008 - ma@suse.de
+
+- Fix reading of Traget::targetDistribution.
+- Allow to configure default answers in signature verification workflow.
+- revision 10968
+
+-------------------------------------------------------------------
+Mon Sep  8 17:11:02 CEST 2008 - jkupec@suse.cz
+
+- CredentialManager added to manage stored credentials
+- MediaCurl adapted to use CredentialManager to read credentials
+- revision 10958
+
+-------------------------------------------------------------------
+Fri Sep  5 11:40:05 CEST 2008 - jkupec@suse.cz
+
+- operator ==, !=, < definition moved to RepoInfoBase from
+  the derived classes
+- fixed RepoInfo::dumpAsIniOn() to not print 'type' if it is unknown
+  (bnc #407515)
+- {Repo,Service}Info::dumpAsXMLOn(ostream) added
+- RepoInfo::dumpRepoOn() deprecated in favor of dumpAsIniOn()
+- use shared_ptr instead of itrusive for {Repo,Service}Info
+- revision 10931:10942
+
+-------------------------------------------------------------------
+Thu Sep  4 16:47:07 CEST 2008 - jkupec@suse.cz
+
+- RepoInfo{,Base} setters made void
+- revision 10931
+
+-------------------------------------------------------------------
+Wed Sep  3 14:53:06 CEST 2008 - jkupec@suse.cz
+
+- skip repositories with non-matching target distro when reading
+  repoindex
+- revision 10926
+
+-------------------------------------------------------------------
+Tue Sep  2 11:04:52 CEST 2008 - schubi@suse.de
+
+- Enabled distupgrade of the SAT solver. In order to use the old
+  distupgrade you can set the environment variable ZYPP_NO_SAT_UPDATE.
+  This variable will can be used until the old distupgrade mechanism will
+  be removed from libzypp. Have a look to above changelogs.
+- revision 10911
+- version 5.7.0 (5)
+
+-------------------------------------------------------------------
+Tue Aug 19 16:36:47 CEST 2008 - dmacvicar@suse.de
+
+- add Package::maybeUnsupported to remove duplicated
+  code in clients dealing with Package::vendorSupport
+
+-------------------------------------------------------------------
+Tue Aug 19 11:04:52 CEST 2008 - schubi@suse.de
+
+- Reset transaction only if this solvable has no buddy (bnc #417799)
+  e.g. do not reset Products cause the concerning release package
+  could not already be installed.
+- revision 10883
+- version 5.6.1 (5)
+
+-------------------------------------------------------------------
+Mon Aug 18 17:31:02 CEST 2008 - dmacvicar@suse.de
+
+- don't report "may be outdated" for @System repo.
+
+-------------------------------------------------------------------
+Fri Aug 15 20:20:07 CEST 2008 - ma@suse.de
+
+- Add Target::targetDistribution. Returns "distribution-arch" of
+  the installed base product. Used for registration and Service
+  refresh. (for Fate #304915)
+- revision 10877
+- version 5.6.0 (5)
+
+-------------------------------------------------------------------
+Fri Aug 15 17:42:58 CEST 2008 - ma@suse.de
+
+- Add method Product::replacedProducts to identify installed
+  Products that would be replaced by installing a new Product.
+  (for Fate #301997)
+- revision 10876
+
+-------------------------------------------------------------------
+Fri Aug 15 15:30:32 CEST 2008 - ma@suse.de
+
+- Fixes to Selectable doing staus manipulation on non-USER level.
+- revision 10873
+
+-------------------------------------------------------------------
+Fri Aug 15 14:23:02 CEST 2008 - ma@suse.de
+
+- Add method ZYpp::getTarget that returns the Target or a NULL pointer,
+  if it is not yet initialized. This is to avoid try/catch blocks just
+  to test whether the Target is initialized. (bnc #417556)
+- Add method Target::assertRootPrefix. Pass a pathname and get back the
+  path prefixed with the tragets root, unless it already had that prefix.
+- revision 10870
+
+-------------------------------------------------------------------
+Thu Aug 14 18:44:45 CEST 2008 - schubi@suse.de
+
+- Regarding error messages for Products correctly (FATE #304502)
+- rev 10863
+
+-------------------------------------------------------------------
+Thu Aug 14 14:03:27 CEST 2008 - ma@suse.de
+
+- Let Selectable default to USER level.
+- revision 10850
+- version 5.5.1 (5)
+
+-------------------------------------------------------------------
+Wed Aug 13 21:01:25 CEST 2008 - ma@suse.de
+
+- Change Selectable API to support doing staus manipulation on
+  non-USER level.
+- revision 10847
+- version 5.5.0 (5)
+
+-------------------------------------------------------------------
+Wed Aug 13 16:48:49 CEST 2008 - dmacvicar@suse.de
+
+- support sat solver API for searching files
+
+-------------------------------------------------------------------
+Wed Aug 13 15:12:45 CEST 2008 - ma@suse.de
+
+- Add ResPool::reposFind to get repositories by alias.
+- revision 10835
+
+-------------------------------------------------------------------
+Tue Aug 12 19:37:36 CEST 2008 - ma@suse.de
+
+- Advise rpmdb2solv to parse the product database.
+- revision 10824
+- version 5.4.0 (4)
+
+-------------------------------------------------------------------
+Tue Aug 12 12:05:49 CEST 2008 - ma@suse.de
+
+- Offer a simpler, fate based status manipulation in ui::Selectable.
+  This is easier to handle, as the ui::Status always distinguishes
+  wheter an object is installed or not.
+- revision 10814
+
+-------------------------------------------------------------------
+Mon Aug 11 18:00:40 CEST 2008 - ma@suse.de
+
+- Propagate default rpm install flags from zypp.conf via ZConfig and
+  ZYppCommitPolicy down to the installer. (FATE #302952)
+- revision 10813
+
+-------------------------------------------------------------------
+Mon Aug 11 16:28:07 CEST 2008 - ma@suse.de
+
+- Add base::Flags (like qt's QFlags) a type-safe way of storing
+  OR-combinations of enum values.
+- revision 10811
+
+-------------------------------------------------------------------
+Fri Aug  8 17:01:00 CEST 2008 - ma@suse.de
+
+- Add static ui::Selectable::get methods as convenient ctor
+  substitute.
+- revision 10806
+
+-------------------------------------------------------------------
+Fri Aug  8 15:41:17 CEST 2008 - ma@suse.de
+
+- Adapt zypp-query-pool to new product handling.
+- revision 10803
+
+-------------------------------------------------------------------
+Fri Aug  8 14:11:03 CEST 2008 - ma@suse.de
+
+- Don't pass epoch to 'rpm -e', it does not support it.
+- revision 10800
+
+-------------------------------------------------------------------
+Fri Aug  8 13:27:42 CEST 2008 - ma@suse.de
+
+- Adapt to new product handling. Products are no longer pseudo
+  installed objects verified by the solver, but actually installed.
+  Thus removed satisfiedProduct iterator, fixed Selctables.
+- Removed class ProductInfo as we keep Product.
+- revision 10797
+
+-------------------------------------------------------------------
+Thu Aug  7 22:00:55 CEST 2008 - dmacvicar@suse.de
+
+- implement relogin suggested support (fate#304889)
+
+-------------------------------------------------------------------
+Wed Aug  6 18:21:30 CEST 2008 - ma@suse.de
+
+- Detect correct download path even if repository type
+  is not set. (bnc #386386)
+- revision 10768
+
+-------------------------------------------------------------------
+Wed Aug  6 17:15:48 CEST 2008 - ma@suse.de
+
+- Cleanup, mostly by removing, unused parser code and related classes.
+- revision 10765
+
+-------------------------------------------------------------------
+Wed Aug  6 16:23:27 CEST 2008 - ma@suse.de
+
+- Don't let exception escape MediaSetAccess dtor (bnc #415017)
+- revision 10763
+
+-------------------------------------------------------------------
+Mon Aug  4 19:34:27 CEST 2008 - ma@suse.de
+
+- Add new product attributes (flavor,referencePackage).
+- Add PoolItem buddies, i.e. two PoolItems sharing the same status
+  object. This is used to keep the product resolvable and the
+  package providing the product metadata in sync.
+- revision 10742
+
+-------------------------------------------------------------------
+Sat Aug  2 19:26:52 CEST 2008 - jkupec@suse.cz
+
+- support an optional url attribute in repoindex.xml's <repo>
+- revision 10729
+
+-------------------------------------------------------------------
+Thu Jul 31 21:52:11 CEST 2008 - ma@suse.de
+
+- New class ProductInfo to provide product related metadata that
+  might be associated with a package. This will replace the Product
+  resolvable.
+- revision 10715
+
+-------------------------------------------------------------------
+Thu Jul 31 19:01:54 CEST 2008 - dmacvicar@suse.de
+
+- generate a unique anonymous unique string per target
+  and add it to the agent string for better statistics
+
+-------------------------------------------------------------------
+Thu Jul 31 19:01:14 CEST 2008 - ma@suse.de
+
+- Follow solver policy and make repository priority the highest
+  key, when ordering packages. Then architecture, and edition last.
+- revision 10710
+
+-------------------------------------------------------------------
+Wed Jul 30 19:12:00 CEST 2008 - ma@suse.de
+
+- /var/lib/zypp and /var/cache/zypp should be owned by libzypp
+  (bnc #412094)
+- revision 10702
+
+-------------------------------------------------------------------
+Wed Jul 30 12:47:48 CEST 2008 - jkupec@suse.cz
+
+- Service renamed to ServiceInfo
+- RepoInfoBase added; RepoInfo and ServiceInfo now derive from it
+- revision 10695
+
+-------------------------------------------------------------------
+Tue Jul 29 12:59:46 CEST 2008 - jkupec@suse.cz
+
+- fixed yum repos to work with non '/' base url post fix
+  (bnc #341617)
+- revision 10662
+
+-------------------------------------------------------------------
+Mon Jul 28 18:13:24 CEST 2008 - ma@suse.de
+
+- Fixed SolvIterMixin::Selectable_iterator eating some solvables
+  (bnc #411339)
+- revision 10680
+
+-------------------------------------------------------------------
+Fri Jul 25 20:12:55 CEST 2008 - ma@suse.de
+
+- Several changes to make libzypp-bindings compile using the original
+  header files and no private copies. (bnc #391831)
+- revision 10668
+- version 5.3.0
+
+-------------------------------------------------------------------
+Wed Jul 23 17:17:47 CEST 2008 - jkupec@suse.cz
+
+- Service::enabled() added
+- revision 10657
+
+-------------------------------------------------------------------
+Tue Jul 22 17:18:28 CEST 2008 - jkupec@suse.cz
+
+- Removed FRESHENS dependency type
+- revision 10643
+
+-------------------------------------------------------------------
+Thu Jul 17 09:49:40 CEST 2008 - schubi@suse.de
+
+- Allow parallel installation of packages which have been defined
+  in zypp.conf (parameter "multiversion") Fate #302050
+- Additional check for broken system.
+  (defined in zypp.conv: solver.checkSystemFile)
+- revision 10600
+
+-------------------------------------------------------------------
+Wed Jul 16 14:39:50 CEST 2008 - ma@suse.de
+
+- Add ui::Selectable::isNeeded to indicate patch relevance (bnc #409150)
+- revision 10596
+
+-------------------------------------------------------------------
+Wed Jul 16 01:10:37 CEST 2008 - ma@suse.de
+
+- Remove Atom, Script, Message and other obsolete classes.
+- revision 10592
+
+-------------------------------------------------------------------
+Mon Jul 14 13:36:26 CEST 2008 - ma@suse.de
+
+- Also report the name of the locking process in ZYppFactoryException
+  (bnc #280537)
+- revision 10572
+
+-------------------------------------------------------------------
+Mon Jul 14 11:26:30 CEST 2008 - schubi@suse.de
+
+- corrected logging of solver settings
+- regard locking while doUpdate (bnc #405427)
+- revision 10564
+
+-------------------------------------------------------------------
+Sat Jul 12 22:14:32 CEST 2008 - jkupec@suse.cz
+
+- make curl use the right transfer mode
+  (CURLOPT_PROXY_TRANSFER_MODE) when proxy is used (bnc #306272)
+- revision 10559
+
+-------------------------------------------------------------------
+Sat Jul 12 11:19:56 CEST 2008 - jkupec@suse.cz
+
+- reuse existing disk mounts (applied Marius' patch) (bnc #208222)
+- revision 10557
+
+-------------------------------------------------------------------
+Wed Jul  9 18:13:58 CEST 2008 - ma@suse.de
+
+- Provide Package::url() if available in solv file. (bnc #402434)
+- A missing cookie file must not be treated as an error. Simply
+  rebuild the cache (bnc #405867)
+- Add 22x22 and 24x24 icons (bnc #329635)
+- revision 10528
+
+-------------------------------------------------------------------
+Mon Jul  7 13:39:09 CEST 2008 - schubi@suse.de
+
+- Do not update an already updated package (bnc #400422)
+- revision 10504
+
+-------------------------------------------------------------------
+Fri Jul  4 17:19:24 CEST 2008 - ma@suse.de
+
+- Also check if the fingerprint matches before importing updated keys.
+  (bnc #393160)
+- revision 10500
+
+-------------------------------------------------------------------
+Mon Jun 30 23:55:20 CEST 2008 - dmacvicar@suse.de
+
+- forward port add message attribute to patches.
+- port import newer keys if a trusted key is updated
+- (bnc#393160)
+- version 5.0.2
+
+-------------------------------------------------------------------
+Mon Jun 30 10:28:27 CEST 2008 - ma@suse.de
+
+- Fix permanent duplication of gpg keys in the rpm database. Also
+  retrieve correct creation and expire dates. (bnc #401259)
+- Invoke gpg with --homdir, otherwise command fails if executed
+  within a wrapper. (bnc #401259)
+- revision 10487
+
+-------------------------------------------------------------------
+Thu Jun 26 12:07:33 CEST 2008 - schubi@suse.de
+
+- version 5.0.1
+- revision 10464
+
+-------------------------------------------------------------------
+Thu Jun 19 17:55:35 CEST 2008 - ma@suse.de
+
+- Handle new patch messages and scripts in commit. Provide callbacks
+  to display the patch messages and give visual feedback on script
+  execution. (bnc #401220)
+- revision 10411
+
+-------------------------------------------------------------------
+Thu Jun 19 17:35:59 CEST 2008 - ma@suse.de
+
+- Fix wrong parenthesis causing bug 399320
+- version
+- revision
+
+-------------------------------------------------------------------
+Tue Jun 10 09:52:03 CEST 2008 - jreidinger@suse.cz
+
+- improve performance of gsub
+- change replace_all to replaceAll (same name convency)
+- add tests for gsub and replaceAll
+- revision 10366
+
+-------------------------------------------------------------------
+Fri Jun  6 13:29:59 CEST 2008 - ma@suse.de
+
+- Handle application/x-redhat-package-manager in package-manager.desktop
+  (bnc #391183)
+- revision 10361
+
+-------------------------------------------------------------------
+Thu Jun  5 18:01:15 CEST 2008 - jkupec@suse.cz
+
+- X-SuSE-ControlCenter-System category added
+  to package-manager.desktop (bnc #302324)
+- revision 10353
+
+-------------------------------------------------------------------
+Wed Jun  4 14:57:13 CEST 2008 - ma@suse.de
+
+- Fix crash when requesting disk usage without a target loaded. (bnc #396755)
+- revision 10340
+
+-------------------------------------------------------------------
+Wed Jun  4 14:05:09 CEST 2008 - ma@suse.de
+
+- Fix memory corruption in curl media handler (bnc #396979)
+- revision 10338
+
+-------------------------------------------------------------------
+Tue Jun  3 20:00:00 CEST 2008 - ma@suse.de
+
+- Take care satsolver recognizes 'Capability( "srcpackage:zypper" )'
+  as 'source package named zypper'. So these capabilities can be used
+  together with sat::Whatprovides or in resolver requests. (bnc #369893)
+- revision 10335
+
+-------------------------------------------------------------------
+Tue Jun  3 15:40:37 CEST 2008 - jreidinger@suse.cz
+
+- allow aborting progress during removing packages. (bnc #389238)
+- revision 10331
+
+-------------------------------------------------------------------
+Mon Jun  2 15:31:36 CEST 2008 - schubi@suse.de
+
+- New option for ignoring Obsoletes. This is used for installing more than
+  one pacakges with the same name but different versions.
+  Often used by kernel.
+- r 10299
+
+-------------------------------------------------------------------
+Sun Jun  1 23:14:34 CEST 2008 - - ma@suse.de
+
+- Revert inappropriate Selectable cleanup. Fix Selectable
+  status computation. Unmaintained packages were wrongly
+  reported as unsinstalled. (bnc #394630)
+- version 5.0.0 (4.x continued in SuSE-Linux-11_0-Branch)
+- revision 10295
+
+-------------------------------------------------------------------
+Fri May 30 12:14:47 CEST 2008 - tgoettlicher@suse.de
+
+- fixed typo
+
+-------------------------------------------------------------------
+Wed May 28 18:54:27 CEST 2008 - ma@suse.de
+
+- Reenable diskusage calculation (bnc #395051)
+- version 4.25.1
+- revision 10273
+
+-------------------------------------------------------------------
+Wed May 28 18:50:11 CEST 2008 - jkupec@suse.cz
+
+- RepoManager::packagesPath(RepoInfo) added (bnc #394728)
+- revision 10271
+
+-------------------------------------------------------------------
+Wed May 28 16:26:09 CEST 2008 - jkupec@suse.cz
+
+- RepoInfo: don't overwrite flags that have already been set externally
+  (bnc #394728)
+- revision 10256
+
+-------------------------------------------------------------------
+Wed May 28 15:30:24 CEST 2008 - ma@suse.de
+
+- Create missing directories when saving config files. (bnc #395026)
+- Fix undefined behaviour in RepoManager.
+- revision 10255
+
+-------------------------------------------------------------------
+Wed May 28 15:23:44 CEST 2008 - schubi@suse.de
+
+- SOLVER_ERASE_SOLVABLE_NAME: As we do not know, if this request has come
+  from resolvePool or resolveQueue we will have to take care for both
+  cases. (bnc#393969)
+- r 10252
+
+-------------------------------------------------------------------
+Tue May 27 22:35:37 CEST 2008 - coolo@suse.de
+
+- compile with RPM_OPT_FLAGS
+
+-------------------------------------------------------------------
+Mon May 26 17:23:24 CEST 2008 - jkupec@suse.cz
+
+- old2new locks file converter script added to %post (jredinger)
+- r 10227
+
+-------------------------------------------------------------------
+Mon May 26 11:48:35 CEST 2008 - schubi@suse.de
+
+- Do not regard packages with the same name while upgrading obsoleted
+  packages (bnc#394367)
+- r 10219
+
+-------------------------------------------------------------------
+Sat May 24 01:23:44 CEST 2008 - dmacvicar@suse.de
+
+- revert commit don't check for existence of keys,
+  to avoid a non needed HEAD request. (related bnc#381280)
+  as it creates popup error callbacks due to the 404's in
+  the keys. Leave however the OnMediaLocation::optional()
+  API to look for another fix strategy.
+
+-------------------------------------------------------------------
+Fri May 23 15:52:20 CEST 2008 - dmacvicar@suse.de
+
+- define path for messages and scripts and document
+  them in zypp.conf
+
+-------------------------------------------------------------------
+Fri May 23 14:42:34 CEST 2008 - schubi@suse.de
+
+- Added IgnoreAlreadyRecommended flag. So recomments/suggest will
+  be ignored for already INSTALLED packages (bnc #389694)
+- r 10202
+
+-------------------------------------------------------------------
+Fri May 23 10:22:47 CEST 2008 - schubi@suse.de
+
+- Packages which obsoletes and do NOT required other installed
+  packages will be installed if no other packages obsolete the installed package too.
+- r 10196
+
+-------------------------------------------------------------------
+Thu May 22 02:22:29 CEST 2008 - dmacvicar@suse.de
+
+- fix filelist for installed packages (bnc#392544)
+- fix changelog retrieval for installed packages
+
+-------------------------------------------------------------------
+Wed May 21 13:50:00 CEST 2008 - dmacvicar@suse.de
+
+- deprecate Repository::name() and use alias() to
+  be consistent. Related to (bnc#383553)
+- don't check for existence of keys, to avoid a non needed
+  HEAD request. (helps bnc#381280)
+- 4.25.0
+
+-------------------------------------------------------------------
+Wed May 21 11:37:23 CEST 2008 - schubi@suse.de
+
+- added onlyRequires in the testcase (bnc #389184)
+
+-------------------------------------------------------------------
+Tue May 20 12:12:27 CEST 2008 - jreidinger@suse.cz
+
+- allow installation and refreshing from repository with alias that
+  contains ' or " (bnc #392426)
+- r10158
+
+-------------------------------------------------------------------
+Mon May 19 21:11:41 CEST 2008 - jkupec@suse.cz
+
+- delta rpm support reenabled
+- r10150
+
+-------------------------------------------------------------------
+Mon May 19 18:13:19 CEST 2008 - schubi@suse.de
+
+- Resetting Delete Details in ResStatus correctly (bnc #391785)
+- r 10145
+
+-------------------------------------------------------------------
+Mon May 19 11:47:06 CEST 2008 - dmacvicar@suse.de
+
+- when setting status to non installed for uninstalled packages
+  set the user transaction so they go to soft locks.
+  (related to bnc#389739 )
+
+-------------------------------------------------------------------
+Fri May 16 16:56:04 CEST 2008 - schubi@suse.de
+
+- Added new calls : isInstalledBy (const PoolItem item);
+                    installs (const PoolItem item);
+- r 10125
+- 4.23.0
+
+-------------------------------------------------------------------
+Fri May 16 16:49:04 CEST 2008 - jreidinger@suse.cz
+
+- don't run merge in save when toAdd/Remove queue is empty
+- throw when locks cannot load its file
+- r10124
+
+-------------------------------------------------------------------
+Fri May 16 09:59:09 CEST 2008 - jreidinger@suse.cz
+
+- throw more describing exception when repo probing failed
+  (bnc #389690)
+- revision 10118
+
+-------------------------------------------------------------------
+Thu May 15 15:15:59 CEST 2008 - jreidinger@suse.cz
+
+- allow call only merge old locks and newly added/removed without
+  saving it to file
+- -revision 10104
+
+-------------------------------------------------------------------
+Tue May 13 17:37:11 CEST 2008 - dmacvicar@suse.de
+
+- report non packages as keep installed if satisfied to the
+  user interace (Selectables)
+- 4.21.3
+
+-------------------------------------------------------------------
+Tue May 13 15:50:28 CEST 2008 - jkupec@suse.cz
+
+- create /etc/zypp/products.d on install
+
+-------------------------------------------------------------------
+Mon May 12 17:21:19 CEST 2008 - jkupec@suse.cz
+
+- /etc/zypp/products.d added to file list (bnc #385868)
+- revision 10049
+- version 4.21.2
+
+-------------------------------------------------------------------
+Mon May 12 17:15:34 CEST 2008 - jkupec@suse.cz
+
+- call RemoveResolvableReport::problem() before finish() on error
+  (bnc #388810)
+- revision 10045
+
+-------------------------------------------------------------------
+Sat May 10 21:57:29 CEST 2008 - coolo@suse.de
+
+- fix file list
+
+-------------------------------------------------------------------
+Fri May  9 23:15:53 CEST 2008 - ma@suse.de
+
+- Product now retrieves all attributes from the solv file.
+- version 4.21.1
+- revision 10031
+
+-------------------------------------------------------------------
+Fri May  9 21:28:42 CEST 2008 - ma@suse.de
+
+- Add zypp.conf option configdir (/etc/zypp) and arrange
+  all config files and directories to follow {configdir}
+  per default.
+- Fix zypp-query-pool to print satisfied products and additional
+  products defined in {configdir}/products.d for registration.
+  (bnc #385868)
+- version 4.21.0
+- revision 10029
+
+-------------------------------------------------------------------
+Fri May  9 15:30:40 CEST 2008 - jreidinger@suse.cz
+
+- implement remove duplicate entries in lock file (bnc#385967)
+
+-------------------------------------------------------------------
+Fri May  9 15:15:32 CEST 2008 - ma@suse.de
+
+- Speedup rpmdb2solv by reusing an existing solv file.
+- version 4.20.1
+- revision 10012
+
+-------------------------------------------------------------------
+Thu May  8 18:13:54 CEST 2008 - ma@suse.de
+
+- Fix failed package download due to unkown repository type (bnc #386386)
+- revision 9995
+
+-------------------------------------------------------------------
+Thu May  8 16:33:37 CEST 2008 - ma@suse.de
+
+- Support optional root argument to RepoManagerOptions, to prefix all
+  path names taken from ZConfig. (bnc #388265)
+- version 4.20.0
+- revision 9993
+
+-------------------------------------------------------------------
+Thu May  8 14:21:51 CEST 2008 - schubi@suse.de
+
+- new solution action for removing requirements/conflicts (bnc #387631)
+- revision 9988
+
+-------------------------------------------------------------------
+Thu May  8 10:56:49 CEST 2008 - ma@suse.de
+
+- Provide enumerated patch category 'Patch::categoryEnum()' (bnc #159100)
+- revision 9984
+
+-------------------------------------------------------------------
+Wed May  7 13:52:24 CEST 2008 - schubi@suse.de
+
+- DistUpgrade: searching for providers -> regarding name onl
+- r 9977
+
+-------------------------------------------------------------------
+Tue May  6 17:35:59 CEST 2008 - dmacvicar@suse.de
+
+- add flag --registrable (-r) to query pool to avoid
+  using system as a filter
+- 4.19.1
+
+-------------------------------------------------------------------
+Tue May  6 07:37:22 CEST 2008 - coolo@suse.de
+
+- return values in non-void functions
+
+-------------------------------------------------------------------
+Mon May  5 14:08:20 CEST 2008 - jkupec@suse.cz
+
+- support multiple search strings in PoolQuery (ORed)
+- revision 9945
+
+-------------------------------------------------------------------
+Mon May  5 09:55:29 CEST 2008 - schubi@suse.de
+
+- Switch off the upgrade mode of the
+  SAT solver cause the packages have already been evaluated by
+  the distupgrade machanism of libzypp. (bnc #386375)
+- rev 9943
+
+-------------------------------------------------------------------
+Fri May  2 16:36:04 CEST 2008 - jreidinger@suse.cz
+
+- release file after copy to cache as soon as possible.
+  (bnc #381311)
+- r9940
+
+-------------------------------------------------------------------
+Fri May  2 16:28:05 CEST 2008 - schubi@suse.de
+
+- Bugfix: keep states by user has been removed it the
+  package has not been installed BUT has been recommended by another package.
+  (bnc #385832)
+- rev 9938
+
+-------------------------------------------------------------------
+Fri May  2 12:19:22 CEST 2008 - jreidinger@suse.cz
+
+- add isLocal function to Url which say if scheme is local or
+  internet.
+- r9932
+
+-------------------------------------------------------------------
+Fri May  2 09:36:18 CEST 2008 - jreidinger@suse.cz
+
+- cache decision for repository depend on his url.
+- http,ftp and smb cache packages.
+- revision 9929
+
+-------------------------------------------------------------------
+Thu May  1 00:50:51 CEST 2008 - ma@suse.de
+
+- Load and maintain persistent hard locks stored in /etc/zypp/locks.
+  Locks are loaded together with the target, and changes are writen
+  back on commit. zypp.conf option locksfile.apply can be used to turn
+  this feature on or off. (FATE #120352)
+- version 4.18.0
+- revision 9927
+
+-------------------------------------------------------------------
+Wed Apr 30 16:27:49 CEST 2008 - ma@suse.de
+
+- Add zypp.conf option solvfilesdir: Path where the repo solv files
+  are created. Default value: {cachedir}/solv.
+- Target and repositories now save their solvfiles below {solvfilesdir}
+  in directories named after the repositories alias.
+- version 4.18.0
+- revision 9913
+
+-------------------------------------------------------------------
+Wed Apr 30 14:19:16 CEST 2008 - jkupec@suse.cz
+
+- fixed filesystem::expandlink(Pathname) (bnc #368477)
+- r9906
+
+-------------------------------------------------------------------
+Tue Apr 29 16:37:19 CEST 2008 - schubi@suse.de
+
+- cleanup in return values of doUpgrade and doUpdate
+- r9886
+- 4.17.0
+
+-------------------------------------------------------------------
+Mon Apr 28 18:27:54 CEST 2008 - jkupec@suse.cz
+
+- check for valid pool in begin(), improve the code (bnc #384337)
+- r9872
+- 4.16.0
+
+-------------------------------------------------------------------
+Mon Apr 28 15:40:09 CEST 2008 - mvidner@suse.cz
+
+- Updated package-manager-su from xdg-utils-1.0.2-48 (bnc#339549).
+
+-------------------------------------------------------------------
+Mon Apr 28 15:25:46 CEST 2008 - schubi@suse.de
+
+- added translations
+
+-------------------------------------------------------------------
+Mon Apr 28 11:15:47 CEST 2008 - jkupec@suse.cz
+
+- ostream operator<<(ostream,TriBool) added
+- r9833
+
+-------------------------------------------------------------------
+Fri Apr 25 16:12:12 CEST 2008 - ma@suse.de
+
+- Prevent target::unload from creating a system repo in order
+  to unload it. (bnc 382297)
+- version 4.15.2
+- revision 9822
+
+-------------------------------------------------------------------
+Fri Apr 25 14:15:17 CEST 2008 - ma@suse.de
+
+- Prevent deselected or deleted items from being re-selected due to
+  recommends (aka. persistent soft locks). Unlike hard locked, those
+  items will be automatically selected if required. The list of soft
+  locked items is stored in /var/lib/zypp/SoftLocks.
+- version 4.15.1
+- revision 9818
+
+-------------------------------------------------------------------
+Wed Apr 23 22:24:00 CEST 2008 - ma@suse.de
+
+- Remove obsolete AdditionalCapabilities interface from ResPool.
+  Forward sat::Pool::RepositoryIterator. There's no more need to
+  maintain an extra Repository list in ResPool.
+- revision 9806
+
+-------------------------------------------------------------------
+Wed Apr 23 21:12:56 CEST 2008 - ma@suse.de
+
+- Support dependencies requiring a specific architecture:
+  "name[.arch] [op edition]". See class Capability for details
+  about how to construct dependencies. (bnc #305445)
+- version 4.15.0
+- revision 9805
+
+-------------------------------------------------------------------
+Tue Apr 22 17:39:28 CEST 2008 - dmacvicar@suse.de
+
+- patch attributes and deprecate old ones
+- 4.14.0
+
+-------------------------------------------------------------------
+Tue Apr 22 15:44:34 CEST 2008 - jreidinger@suse.cz
+
+- change locks api -
+- make more functions const
+- replace add/remove by selectable to add/remove by ident or name and kind
+- rename iterator to const_iterator to avoid confusion
+- revision 9781
+
+-------------------------------------------------------------------
+Tue Apr 22 13:55:14 CEST 2008 - schubi@suse.de
+
+- Do architecture changes while "dup" in the external distribution
+  upgrade ONLY. bnc #382274
+- Added "ignore" to the solutions
+- Added "self-conflicts" to the solution
+- added new solver mechanism "resolveQueue"
+- Bugfix broken/satisfied products
+- rev 9776
+
+-------------------------------------------------------------------
+Tue Apr 22 10:33:03 CEST 2008 - ma@suse.de
+
+- Added Pattern::core returning the packages required by a pattern.
+  (see also Pattern::depends and Pattern::contents).
+- revision 9771
+
+-------------------------------------------------------------------
+Mon Apr 21 15:38:10 CEST 2008 - ma@suse.de
+
+- Added Target::release(), returning the targets distribution
+  release string.
+- revision 9761
+
+-------------------------------------------------------------------
+Sat Apr 19 01:08:04 CEST 2008 - ma@suse.de
+
+- per default abort if package installation fails. (bnc #381203)
+- version 4.13.3
+- revision 9725
+
+-------------------------------------------------------------------
+Fri Apr 18 15:19:36 CEST 2008 - dmacvicar@suse.de
+
+- add ZYpp and curl version to http agent string
+  (bnc #381280)
+
+-------------------------------------------------------------------
+Thu Apr 17 16:29:15 CEST 2008 - ma@suse.de
+
+- Fixed pools package index wrongly including source packages. (bnc #380283)
+- version 4.13.2
+- revision 9683
+
+-------------------------------------------------------------------
+Wed Apr 16 15:00:19 CEST 2008 - ma@suse.de
+
+- Disable fast creation of @System.solv. It may produce wrong results
+  e.g. after a rebuilddb.
+- version 4.13.1
+- revision 9666
+
+-------------------------------------------------------------------
+Wed Apr 16 12:50:09 CEST 2008 - ma@suse.de
+
+- initializeTarget now takes an additional option, telling whether to
+  rebuild an existing rpm database before using it. Default is false.
+  (bnc #308352)
+- version 4.13.0
+- revision 9664
+
+-------------------------------------------------------------------
+Tue Apr 15 12:57:42 CEST 2008 - jreidinger@suse.cz
+
+- save do nothing if no locks added/removed
+- fix bug with multiple save lock
+- don't save same query multiple times
+- improve tests
+- revision 9644
+
+-------------------------------------------------------------------
+Tue Apr 15 09:34:29 CEST 2008 - schubi@suse.de
+
+- added new translations
+- activate zypp-query-pool
+- Revision 9637
+- 4.12.1
+
+-------------------------------------------------------------------
+Tue Apr 15 00:54:07 CEST 2008 - jkupec@suse.cz
+
+- Locks API cleaned-up, iterator added, light read() added
+- PoolQuery::attribute(SolvAttr) getter added
+- revision 9609
+
+-------------------------------------------------------------------
+Mon Apr 14 17:00:05 CEST 2008 - dmacvicar@suse.de
+
+- reenable zypp-query-pool
+- 4.11.1
+
+-------------------------------------------------------------------
+Mon Apr 14 12:53:09 CEST 2008 - ma@suse.de
+
+- Enable evaluation of hardware dependencies.
+- Enable evaluation of filesystem dependencies.
+- revision 9605
+
+-------------------------------------------------------------------
+Sun Apr 13 02:17:25 CEST 2008 - jkupec@suse.cz
+
+- RawMetadataRefreshPolicy: CheckIfNeededIgnoreDelay added
+  needed for explicit refresh request
+- revision 9574
+
+-------------------------------------------------------------------
+Fri Apr 11 21:55:21 CEST 2008 - ma@suse.de
+
+- Install ResPoolProxy index to speedup Solvable to Selectable
+  conversion.
+- version 4.11.0
+- revision 9558
+
+-------------------------------------------------------------------
+Fri Apr 11 18:02:14 CEST 2008 - kkaempf@suse.de
+
+- Implement update scripts installed by packages. After every
+  package install /var/adm/update-scripts is scanned for the first
+  file starting with "<name>-<version>.<release>-", which is then
+  executed.
+- revision 9547
+
+-------------------------------------------------------------------
+Fri Apr 11 14:43:05 CEST 2008 - ma@suse.de
+
+- Fix SolvIterMixin to avioid multiple visits of the same Selectable.
+- Add Resolvable::poolItem() providing access to the corresponding
+  PoolItem. API to query isRelevant/isSatisfied/isBroken was moved
+  to PoolItem.
+- Add ResPool::satisfiedProductsBegin/End iterator over all products
+  whose dependencies are satisfied. This reflects the status determined
+  by the last solver run. (#368104)
+- revision 9535
+
+-------------------------------------------------------------------
+Fri Apr 11 12:07:47 CEST 2008 - jreidinger@suse.cz
+
+- switch to new locks api
+- revision 9524
+
+-------------------------------------------------------------------
+Wed Apr  9 21:24:54 CEST 2008 - ma@suse.de
+
+- Enable ui::Selectable lookup by Solvable/PoolItem in ResPoolProxy.
+- Add SolvIterMixin: Base class providing PoolItem_iterator and
+  Selectable_iterator iterator types based on a Solvable iterator.
+- Enhanced WhatProvides and SolvableSet to PoolItem_iterator to offer
+  PoolItem_iterator and Selectable_iterator.
+- Add Solvable::SplitIdent: Helper class that splits an identifier
+  into kind and name.
+- Provide methods Pattern::contents returning a collection of packages
+  associated with the pattern/patch.
+- revision 9496
+
+-------------------------------------------------------------------
+Tue Apr  8 15:50:48 CEST 2008 - jreidinger@suse.cz
+
+- add comparing to PoolQuery
+- revision 9466
+
+-------------------------------------------------------------------
+Tue Apr  8 13:18:30 CEST 2008 - jreidinger@suse.cz
+
+- move RepoInfo to universal RepoException. This can enable more verbose output - for frontend. (helps with bnc #377137)
+- revision 9452
+
+-------------------------------------------------------------------
+Tue Apr  8 10:52:30 CEST 2008 - jreidinger@suse.cz
+
+- initial implementation of new locks (FATE #120118 and #120352)
+- revision 9442
+
+-------------------------------------------------------------------
+Tue Apr  7 15:18:22 CEST 2008 - dmacvicar@suse.de
+
+- selectable API updates and changes
+- 4.10.0
+
+-------------------------------------------------------------------
+Fri Apr  4 14:01:45 CEST 2008 - jreidinger@suse.cz
+
+- add split with respect to escaped delimeters and also for quotes
+- revision 9373
+
+-------------------------------------------------------------------
+Thu Apr  3 12:55:50 CEST 2008 - ma@suse.de
+
+- Fixed some missing package and source package attributes.
+- revision 9348
+
+-------------------------------------------------------------------
+Thu Apr  3 11:59:13 CEST 2008 - ma@suse.de
+
+- Allow to store a media label in MediaSetAccess. This label is
+  passed to a media change requests to describe which CD is
+  requested.  (bnc #330094)
+- Fixed some missing package and source package attributes.
+- revision 9347
+
+-------------------------------------------------------------------
+Wed Apr  2 13:48:52 CEST 2008 - schubi@suse.de
+
+- Moved poolItem.status().isSatisfied(),.... to poolItem.isSatisfied()
+- Removed establish state in ResStatus
+- revision 9337
+- version 4.7.0
+
+-------------------------------------------------------------------
+Wed Apr  2 10:24:17 CEST 2008 - ma@suse.de
+
+- Add PoolItem::isSatisfied()/isBroken() to test whether
+  the items requirements are met.
+- revision 9334
+
+-------------------------------------------------------------------
+Tue Apr  1 21:54:10 CEST 2008 - ma@suse.de
+
+- Extend sat::WhatProvides to allow to query for possible providers
+  of a collection of capabilies. E.g. all providers of a packages
+  requirements.
+- Fixed retrieval of translated texts from .solv files, provided the
+  solv file contains them.
+- revision 9328
+
+-------------------------------------------------------------------
+Tue Apr  1 16:17:44 CEST 2008 - jreidinger@suse.cz
+
+- initial implementation of serialize/recovery PoolQuery
+  (needed by FATE #120118)
+- revision 9325
+
+-------------------------------------------------------------------
+Wed Mar 26 16:15:24 CET 2008 - ma@suse.de
+
+- Allow prioritizing repos by adding a line 'priority=N' to the
+  .repo file. Where N is an integer number from 1 (highest prio)
+  to 99 (least and default). (bnc #369827, fate #302872)
+- version 4.6.1
+- revision 9276
+
+-------------------------------------------------------------------
+Mon Mar 24 21:17:05 CET 2008 - coolo@suse.de
+
+- support plaindir again (at least the most important parts)
+
+-------------------------------------------------------------------
+Fri Mar 21 13:23:13 CET 2008 - jreidinger@suse.cz
+
+- Throwing special exception MediaBadCAException in case of SSL
+  certificate validation failure.(bnc #223512)
+- revision 9250
+
+-------------------------------------------------------------------
+Fri Mar 21 12:38:39 CET 2008 - jreidinger@suse.cz
+
+- add new error IO_SOFT to media request callback for temporary
+  connection problem. (bnc #328822)
+- add new media exception timeout when somethink fail due to exceed
+  timeout
+- mediacurl throw timeout exception when timeouted
+- revision 9246
+
+-------------------------------------------------------------------
+Thu Mar 20 15:00:24 CET 2008 - jreidinger@suse.cz
+
+- return more information from checking if metadata need refresh,
+  so user can get better info. (bnc #307249)
+- revision 9231
+
+-------------------------------------------------------------------
+Tue Mar 18 21:59:04 CET 2008 - ma@suse.de
+
+- class sat::LocaleSupport: Convenience methods to manage support
+  for language specific packages.
+- revision 9197
+
+-------------------------------------------------------------------
+Tue Mar 18 13:54:16 CET 2008 - jkupec@suse.cz
+
+- removed obsolete capability handling stuff (ma)
+- version 4.5.0
+
+-------------------------------------------------------------------
+Tue Mar 18 11:42:51 CET 2008 - jreidinger@suse.cz
+
+- Don't mask skip and abort exception in Fetcher
+- revision 9188
+
+-------------------------------------------------------------------
+Tue Mar 18 11:00:22 CET 2008 - jreidinger@suse.cz
+
+- action is correctly set in mediaRequest callback
+- revision 9186
+
+-------------------------------------------------------------------
+Mon Mar 17 12:33:44 CET 2008 - ma@suse.de
+
+- Fix SEGV in commit (bnc# 371137)
+- version 4.4.3
+- revision 9174
+
+-------------------------------------------------------------------
+Fri Mar 14 17:28:39 CET 2008 - ma@suse.de
+
+- version 4.4.2
+
+-------------------------------------------------------------------
+Fri Mar 14 14:27:43 CET 2008 - dmacvicar@suse.de
+
+- look for openssl in cmake, actually we build require it
+- explicitely link against openssl and crypto, required to
+  compile in all platforms/distros.
+
+-------------------------------------------------------------------
+Fri Mar 14 12:07:41 CET 2008 - jreidinger@suse.cz
+
+- Save repo type during refresh if type is NONE (f.e. lazy probing).
+- revision 9153
+
+-------------------------------------------------------------------
+Fri Mar 14 11:34:24 CET 2008 - jreidinger@suse.cz
+
+- replace gpg escaped semicolon with real semicolon (bnc #355434)
+- revision 9151
+
+-------------------------------------------------------------------
+Fri Mar 14 10:17:41 CET 2008 - jreidinger@suse.cz
+
+- make strings from RpmDb and Keyring exceptions translatable
+- revision 9146
+
+-------------------------------------------------------------------
+Thu Mar 13 18:41:26 CET 2008 - dmacvicar@suse.de
+
+- fix retrieving keys (bnc #368099)
+- version 4.4.1
+
+-------------------------------------------------------------------
+Thu Mar 13 18:40:57 CET 2008 - jreidinger@suse.cz
+
+- enable frontend to rewrite add_probe settings.(bnc #309612)
+- Correct adding repo without type to lazy probing.
+- revision 9135
+
+-------------------------------------------------------------------
+Thu Mar 13 17:59:52 CET 2008 - jreidinger@suse.cz
+
+- get better message if something fail when trying run rpm
+  (bnc #344584)
+- revision 9133
+
+-------------------------------------------------------------------
+Thu Mar 13 12:33:13 CET 2008 - ma@suse.de
+
+- Add ExternalProgram::execError and ExternalProgram::command
+  to improve error reporting.
+- revision 9112
+
+-------------------------------------------------------------------
+Thu Mar 13 11:45:20 CET 2008 - jkupec@suse.cz
+
+- release all media before requesting another (bnc #336881)
+- revision 9110
+
+-------------------------------------------------------------------
+Thu Mar 13 11:32:56 CET 2008 - jkupec@suse.cz
+
+- getDetectedDevices added (fate #120298)
+- revision 9108
+
+-------------------------------------------------------------------
+Wed Mar 12 15:55:14 CET 2008 - jkupec@suse.cz
+
+- media backend release() methods changed to take string & instead
+  of bool (needed for FATE #120298)
+- media label, detected device list and current device arguments
+  added to the requestMedia callback
+- version 4.4.0
+
+-------------------------------------------------------------------
+Wed Mar 12 14:16:07 CET 2008 - coolo@suse.de
+
+- fix for bnc#369543
+
+-------------------------------------------------------------------
+Mon Mar 10 13:30:04 CET 2008 - jkupec@suse.cz
+
+- provide download rate info (average and curent) in the
+  media::DownloadProgressReport for ftp/http (bnc #168935)
+- r9074
+
+-------------------------------------------------------------------
+Mon Mar 10 12:45:03 CET 2008 - jkupec@suse.cz
+
+- cleanCache(): clean also .cookie files
+- cleanTargetCache() added
+- use escaped_alias() in rawcache_path_for_repoinfo() and
+  packagescache_path_for_repoinfo()
+- r9068
+
+-------------------------------------------------------------------
+Fri Mar  7 19:59:10 CET 2008 - jkupec@suse.cz
+
+- fixed location of RPMs in subdirs when parsing plaindir repo
+  recursively (bnc #368218)
+- revision 9060
+
+-------------------------------------------------------------------
+Thu Mar  6 13:42:53 CET 2008 - ma@suse.de
+
+- Do not filter any installed solvables.
+- revision 9031
+- version 4.3.2
+
+-------------------------------------------------------------------
+Wed Mar  5 11:33:26 CET 2008 - ma@suse.de
+
+- Try to rebuild broken solv files in Target::load.
+- revision 9015
+
+-------------------------------------------------------------------
+Tue Mar  4 18:17:41 CET 2008 - ma@suse.de
+
+- Try to rebuild broken solv files in RepoManager::loadFromCache.
+- Fix RepoStatus::operator&& and RepoStatus testsuite.
+- revision 9008
+
+-------------------------------------------------------------------
+Tue Mar  4 16:09:14 CET 2008 - schubi@suse.de
+
+- improved problem description while a vendor change
+- improved problem description if a requirement cannot be fulfilled. Bug #358560
+- revision 9002
+
+-------------------------------------------------------------------
+Tue Mar  4 12:57:58 CET 2008 - ma@suse.de
+
+- Save and restore requested locales on target load/commit.
+- revision 8999
+
+-------------------------------------------------------------------
+Mon Mar  3 17:10:26 CET 2008 - schubi@suse.de
+
+- (Update) Prevent reinstallation of installed packages.
+- revision 8984
+
+-------------------------------------------------------------------
+Sun Mar  2 16:13:16 CET 2008 - coolo@suse.de
+
+- refresh metadata if there is no cache to unbreak compat with
+  kiwi (that relied on "zypper sa <url> <alias>" to create a repo
+  that "zypper in" could work on)
+
+-------------------------------------------------------------------
+Sun Mar  2 10:28:13 CET 2008 - coolo@suse.de
+
+- create cache directory before calling rpmdb2solv (in an empty
+  chroot)
+- version 4.3.1
+
+-------------------------------------------------------------------
+Thu Feb 28 16:43:11 CET 2008 - jkupec@suse.cz
+
+- special exception message if server returns 403 response
+  (forbidden) (port from SP2)
+- MediaException messages marked for translation
+
+-------------------------------------------------------------------
+Wed Feb 27 10:31:24 CET 2008 - dmacvicar@suse.de
+
+- make sure we have target cache on target initialize
+- version 4.3.0
+
+-------------------------------------------------------------------
+Tue Feb 26 13:59:03 CET 2008 - lslezak@suse.cz
+
+- DiskUsageCounter.cc - ignore "vfat", "fat", "ntfs" and "ntfs-3g"
+  file systems (#333166)
+- rev. 8915
+
+-------------------------------------------------------------------
+Tue Feb 26 13:26:30 CET 2008 - ma@suse.de
+
+- Fixed Capabilites iterator exposing prereq marker.
+- revision 8914
+
+-------------------------------------------------------------------
+Tue Feb 26 11:29:05 CET 2008 - schubi@suse.de
+
+- postinstall script fixed
+- version 4.2.10
+
+-------------------------------------------------------------------
+Mon Feb 25 17:06:53 CET 2008 - schubi@suse.de
+
+- Testcases regards modaliases, rpmlib, ... correctly
+- Revision 8904
+
+-------------------------------------------------------------------
+Mon Feb 25 13:20:26 CET 2008 - ma@suse.de
+
+- Remove obsolete sql database. (bnc#363224)
+- revision 8898
+
+-------------------------------------------------------------------
+Fri Feb 22 14:03:59 CET 2008 - ma@suse.de
+
+- Take care target uses --root when creating solv files (bnc #363789)
+- revision 8881
+
+-------------------------------------------------------------------
+Fri Feb 22 10:10:14 CET 2008 - schubi@suse.de
+
+- Unmaintained packages which does not fit to the updated system
+  (broken dependencies) will be deleted.
+- revision 8867
+
+-------------------------------------------------------------------
+Fri Feb 22 07:20:35 CET 2008 - coolo@suse.de
+
+- let libzypp-devel require libsatsolver-devel
+
+-------------------------------------------------------------------
+Wed Feb 20 13:47:56 CET 2008 - ma@suse.de
+
+- Cleanup unused /var/lib/zypp/cache in migrate_sources (#305160)
+- revision 8833
+
+-------------------------------------------------------------------
+Tue Feb 19 16:21:12 CET 2008 - jkupec@suse.cz
+
+- media: fixed DownloadProgressReport.finish() url argument in
+  doGetFileCopy()
+- revision 8815
+
+-------------------------------------------------------------------
+Tue Feb 19 11:21:59 CET 2008 - dmacvicar@suse.de
+
+- hardlink when possible to optimize data transfer
+  and space across caches.
+- version 4.2.8
+
+-------------------------------------------------------------------
+Tue Feb 19 08:15:07 CET 2008 - coolo@suse.de
+
+- added some locale support to sat::Solvable
+- version 4.2.7
+
+-------------------------------------------------------------------
+Mon Feb 18 14:15:48 CET 2008 - dmacvicar@suse.de
+
+- handle error messages better in doesFileExist too which is
+  used during probing. (bnc #362608)
+
+-------------------------------------------------------------------
+Sun Feb 17 21:44:12 CET 2008 - dmacvicar@suse.de
+
+- Fetcher::reset() should not reset cache directories.
+  (bnc #348050)
+- version 4.2.6
+
+-------------------------------------------------------------------
+Fri Feb 16 15:47:00 CET 2008 - dmacvicar@suse.de
+
+- Use CURLOPT_NOBODY instead of a CURLOPT_RANGE of 1 byte
+  for http and https, but this time set CURLOPT_HTTPGET back to 1
+  so it actually works. This makes Media::doesFileExist
+  efficient for http and https.
+  (related to bnc #348050)
+- version 4.2.5
+
+-------------------------------------------------------------------
+Fri Feb 15 10:52:59 CET 2008 - coolo@suse.de
+
+- using .solv files only now (fate #303018)
+- revision 8699
+
+-------------------------------------------------------------------
+Tue Feb 12 17:10:48 CET 2008 - coolo@suse.de
+
+- fix architectures on distupgrade
+
+-------------------------------------------------------------------
+Fri Feb  8 12:50:30 CET 2008 - coolo@suse.de
+
+- fixes from trunk merged
+
+-------------------------------------------------------------------
+Fri Feb  1 17:45:07 CET 2008 - jkupec@suse.cz
+
+- fixed renaming a repo to an existing one (bnc #228216)
+- revision 8431
+
+-------------------------------------------------------------------
+Sun Jan 27 21:35:13 CET 2008 - coolo@suse.de
+
+- fix changelog
+
+-------------------------------------------------------------------
+Thu Jan 24 18:26:47 CET 2008 - jkupec@suse.cz
+
+- read .curlrc more robustly to obtain user-proxy (#330351)
+- revision 8368
+
+-------------------------------------------------------------------
+Fri Jan 18 12:19:09 CET 2008 - coolo@suse.de
+
+- always buildrequire openssl-devel
+- replacing strange utf-8 chars in changelog
+- revision 8317
+
+-------------------------------------------------------------------
+Thu Jan 17 15:07:52 CET 2008 - jkupec@suse.cz
+
+- Saner NFS timeo default (#350309)
+- revision 8314
+
+-------------------------------------------------------------------
+Thu Jan 17 12:03:10 CET 2008 - kkaempf@suse.de
+
+- support 'patterns.pat' and 'patterns.pat.gz' to read all
+  patterns in one go.
+- rev 8309
+
+-------------------------------------------------------------------
+Tue Jan 15 14:56:21 CET 2008 - lslezak@suse.cz
+
+- added RpmDb::removePubkey(), call it from
+  KeyRing::Impl::deleteKey() - remove the GPG key from RPM when it
+  is removed from the trusted keyring
+- revision 8288
+
+-------------------------------------------------------------------
+Mon Jan 14 17:11:15 CET 2008 - schubi@suse.de
+
+- Textchanges
+- reduced logging in SAT-solver
+- ordering solutions
+- version 4.1.8
+- revision 8276
+
+-------------------------------------------------------------------
+Thu Jan 10 16:50:26 CET 2008 - schubi@suse.de
+
+- Enabled SAT solver via default. (removed ZYPP_SAT_SOLVER)
+  ZYPP_RC_SOLVER=1 will enable the old RedCapet solver
+- Revision 8255
+- Version 4.1.7
+
+-------------------------------------------------------------------
+Wed Jan  2 18:24:34 CET 2008 - jkupec@suse.cz
+
+- Pathname zypp::filesystem::expandlink(const Pathname &) added
+- if the provided file is a symlink, expand it (#274651) (this
+  probably won't work for schemes other than file/dir and cd/dvd)
+- revision 8179
+
+-------------------------------------------------------------------
+Tue Dec 18 12:28:22 CET 2007 - aschnell@suse.de
+
+- fixed password handling in URLs (bug #347273)
+- revision 8118
+
+-------------------------------------------------------------------
+Mon Dec 17 13:47:44 CET 2007 - ma@suse.de
+
+- Fixed default text locale detection not to use static variables. (#346872)
+- version 4.1.6
+- revision 8116
+
+-------------------------------------------------------------------
+Mon Dec 10 12:46:26 CET 2007 - ma@suse.de
+
+- Log more details about zypp lock owner. (#294094)
+- revision 8088
+
+-------------------------------------------------------------------
+Fri Dec  7 15:47:53 CET 2007 - ma@suse.de
+
+- Remove runtime dependency for libboost_filesystem (#345773)
+- version 4.1.5
+- revision 8061
+
+-------------------------------------------------------------------
+Fri Nov 30 14:20:34 CET 2007 - schubi@suse.de
+
+- Enable SAT solver via environment variable ZYPP_SAT_SOLVER.
+  e.g.: ZYPP_SAT_SOLVER=1 zypper install foo
+- version 4.1.4
+- revision 7998
+
+-------------------------------------------------------------------
+Wed Nov 28 15:12:33 CET 2007 - aschnell@suse.de
+
+- make IniParser more strict (bug #306697)
+
+-------------------------------------------------------------------
+Mon Nov 26 13:19:34 CET 2007 - ma@suse.de
+
+- Fix missing packages in patch content list. (#340896)
+- revision 7925
+
+-------------------------------------------------------------------
+Fri Nov 16 12:46:39 CET 2007 - coolo@suse.de
+
+- fix build
+
+-------------------------------------------------------------------
+Wed Nov 14 14:23:23 CET 2007 - ma@suse.de
+
+- Output date strings in UTF-8. (#339423)
+- revision 7807
+
+-------------------------------------------------------------------
+Tue Nov 13 13:40:03 CET 2007 - schubi@suse.de
+
+- fixes for new gcc
+- version 4.1.3
+- r7788
+
+-------------------------------------------------------------------
+Mon Nov  5 15:22:33 CET 2007 - ma@suse.de
+
+- Don't mark failed patch scripts as installed. (#327523)
+- version 4.1.2
+- revision 7744
+
+-------------------------------------------------------------------
+Wed Oct 31 09:19:59 CET 2007 - dmueller@suse.de
+
+- update rpmlint suppression
+
+-------------------------------------------------------------------
+Fri Oct 26 15:12:24 CEST 2007 - aschnell@suse.de
+
+- fixed retrieval of epoch from rpmdb (bug #246680)
+
+-------------------------------------------------------------------
+Thu Oct 25 16:29:24 CEST 2007 - aschnell@suse.de
+
+- allow non-existing "packages" file in susetags parser (bug
+  #309235)
+
+-------------------------------------------------------------------
+Fri Oct 12 15:47:37 CEST 2007 - ma@suse.de
+
+- SMBIOS DMI modalias matching added (#333152)
+- revision 7494
+
+-------------------------------------------------------------------
+Sat Oct  6 16:37:11 CEST 2007 - jkupec@suse.cz
+- do not download the same file multiple times in one attach session
+  (#307098), r7456
+- special cdrom detection code for SCSI / Virtual CDROMs on iSeries
+  removed - should be correctly detected by HAL now
+  (#167629, #163971), r7452
+- version 4.1.1
+
+-------------------------------------------------------------------
+Fri Oct 05 10:35:47 CEST 2007 - aschnell@suse.de
+
+- filter architecture in plaindir parser (bug #330791)
+
+-------------------------------------------------------------------
+Thu Oct  4 16:31:58 CEST 2007 - ma@suse.de
+
+- Throw constructing malformed checksums. (#189096)
+- revision 7441
+
+-------------------------------------------------------------------
+Thu Oct  4 14:22:31 CEST 2007 - mvidner@suse.cz
+
+- Renamed templates back because proper qualification makes it work
+  too.
+- 4.1.0
+
+-------------------------------------------------------------------
+Thu Oct 04 11:23:19 CEST 2007 - aschnell@suse.de
+
+- only look for repositories in file ending ".repo" (bug #294779)
+
+-------------------------------------------------------------------
+Wed Oct  3 13:37:57 CEST 2007 - mvidner@suse.cz
+
+- Fixed compilation errors with GCC 4.3 by adding missing includes
+  and renaming templates: MaxBits to MaxBitsT, Mask to MaskT,
+  Compare<Edition> to CompareEd.
+- r7426
+
+-------------------------------------------------------------------
+Mon Oct  1 20:29:36 CEST 2007 - ma@suse.de
+
+- Incorporated patch from Michael Matz to speedup cache reading.
+- revision 7413
+
+-------------------------------------------------------------------
+Fri Sep 28 18:40:59 CEST 2007 - schubi@suse.de
+
+- Resolvertestcase:
+  - log Repository info
+  - set keep state in the testcase
+  - handle vendor
+- r 4707
+
+-------------------------------------------------------------------
+Thu Sep 27 17:13:14 CEST 2007 - ma@suse.de
+
+- Fixed pattern parser SEGV on broken pattern files. (#328546)
+- revision 7402
+
+-------------------------------------------------------------------
+Wed Sep 26 11:08:14 CEST 2007 - schubi@suse.de
+
+- QueuItemRequire: Filter out all provider which have worser architecture,
+  are NOT noarch and have not the same name as the requirement. The
+  last one is needed for updating packages via patch/atoms.
+  Bug 328081
+- Revert changes of r 7340
+- r 7386
+
+-------------------------------------------------------------------
+Tue Sep 25 15:51:34 CEST 2007 - ma@suse.de
+
+- Add missing '--install' parameter in desktop file. (#308640)
+- version 4.0.0
+- revision 7369
+
+-------------------------------------------------------------------
+Tue Sep 25 11:08:13 CEST 2007 - jkupec@suse.cz
+
+- release all attached media before attempting to eject (#293428)
+- fixed parsing of --proxy-user parameter of .curlrc (#309139)
+- revision 7352
+- version 3.26.0
+
+-------------------------------------------------------------------
+Mon Sep 24 16:10:32 CEST 2007 - dmacvicar@suse.de
+
+- provide a way to retrieve the metadata path. Used for
+  installation, which incorrectly creates a repository in
+  cache without adding it first, so metadata path is not
+  set and therefore it is not possible to setup a media
+  verifier on installation. Part of fix for (#293428)
+- 3.25.0
+
+-------------------------------------------------------------------
+Mon Sep 24 15:48:47 CEST 2007 - schubi@suse.de
+
+- If more than one resolvables provide a requirements and have different
+  architecture take thatone with the best architecture. (Not regarding the
+  name). Bug: Branching too much while an installation of a multi-arch-DVD
+- r 7340
+- version 3.24.8
+
+-------------------------------------------------------------------
+Fri Sep 21 15:50:30 CEST 2007 - schubi@suse.de
+
+- Checking the queue if an item will be deleted. If yes, the requirements
+  are not needed anymore. Bug 326384
+- version 3.24.7
+- r 7329
+
+-------------------------------------------------------------------
+Thu Sep 20 17:37:30 CEST 2007 - jkupec@suse.cz
+
+- don't probe the repository type upon saving if disabled (#326769)
+- version 3.24.6
+- revision 7319
+
+-------------------------------------------------------------------
+Thu Sep 20 15:19:53 CEST 2007 - ma@suse.de
+
+- Avoid calling rpm repeatedly in case of an error. This is fault-prone,
+  esp. if the error occurred executing the packages post-install script.
+- version 3.24.5
+- revision 7317
+
+-------------------------------------------------------------------
+Thu Sep 20 11:00:04 CEST 2007 - ma@suse.de
+
+- If a package is deselected by user, apply this soft lock to all
+  available versions of this package.
+- version 3.24.4
+- revision 7316
+
+-------------------------------------------------------------------
+Wed Sep 19 17:45:12 CEST 2007 - schubi@suse.de
+
+- Update: Do not set an item to installation if there has been already set
+  one for installation which has the same NVA. Bug  326286
+- version 3.24.3
+- r 7311
+
+-------------------------------------------------------------------
+Wed Sep 19 14:19:49 CEST 2007 - ma@suse.de
+
+- Enable package cache during commit. (#326249)
+- revision 7309
+
+-------------------------------------------------------------------
+Tue Sep 18 14:01:02 CEST 2007 - schubi@suse.de
+
+- Do not regarding requirements for packages which will be deleted in the
+  same solver run. Bug 310618
+- r 7292
+- version 3.24.2
+
+-------------------------------------------------------------------
+Mon Sep 17 16:33:14 CEST 2007 - jkupec@suse.cz
+
+- don't download filelists.xml.gz (#307105)
+- version 3.24.1
+- revision 7269
+
+-------------------------------------------------------------------
+Mon Sep 17 15:51:04 CEST 2007 - ma@suse.de
+
+- Improve estimated diskusage while there is no valid
+  solver result. (#325617)
+- revision 7266
+
+-------------------------------------------------------------------
+Mon Sep 17 15:22:47 CEST 2007 - schubi@suse.de
+
+-Bugfix in vendor change of a required resolvable (Correct error message)
+ Bug 310455
+- r 7262
+
+-------------------------------------------------------------------
+Mon Sep 17 12:36:28 CEST 2007 - lslezak@suse.cz
+
+- fixed DU parsing in inst-sys (#308659)
+- revision 7256
+
+-------------------------------------------------------------------
+Fri Sep 14 22:57:54 CEST 2007 - ma@suse.de
+
+- Enable using patch and delta rpms. (#309124)
+- version 3.24.0
+- revision 7253
+
+-------------------------------------------------------------------
+Thu Sep 13 17:59:55 CEST 2007 - ma@suse.de
+
+- On update do not delete unmaintained non-SuSE packages.
+- version 3.23.2
+- revision 7239
+
+-------------------------------------------------------------------
+Thu Sep 13 13:02:39 CEST 2007 - lslezak@suse.cz
+
+- ZYppImpl::getPartitions() - don't return the current partitioning
+  when the partitioning hasn't been set - fixes DU parsing in
+  inst-sys (#308659)
+
+-------------------------------------------------------------------
+Thu Sep 13 11:30:47 CEST 2007 - dmacvicar@suse.de
+
+- Throw specific exceptions during commit (#308511)
+- 3.23.1
+
+-------------------------------------------------------------------
+Wed Sep 12 18:48:00 CEST 2007 - ma@suse.de
+
+- Various disk space calculation fixes. Susetags, plaindir and rpmdb
+  now provide more detailed disk usage information.
+  Yum metadata don't, so we book the package size to '/'. (#308362)
+- version 3.23.0
+- revision 7225
+
+-------------------------------------------------------------------
+Wed Sep 12 15:48:41 CEST 2007 - schubi@suse.de
+
+- Regarding keep state while recycle old valid solver results. Bug 286889
+- r 7209
+
+-------------------------------------------------------------------
+Wed Sep 12 12:24:49 CEST 2007 - jkupec@suse.cz
+
+- report 100% progress on finishing RPM removal (bug #309431)
+- revision 7200
+
+-------------------------------------------------------------------
+Tue Sep 11 17:25:35 CEST 2007 - schubi@suse.de
+
+- Update: rename language packages --> take that package which fits to the
+  selected language Bug 308098
+- r 7919
+
+-------------------------------------------------------------------
+Tue Sep 11 16:11:20 CEST 2007 - dmacvicar@suse.de
+
+- restore deltas and patch rpms from the cache (#309124)
+- 3.22.8
+
+-------------------------------------------------------------------
+Tue Sep 11 14:54:44 CEST 2007 - ma@suse.de
+
+- Don't fail if a product is deleted multiple times (e.g. due to
+  obsoletes and an explicit deleted request). (#308746)
+- version 3.22.7
+- revision 7184
+
+-------------------------------------------------------------------
+Tue Sep 11 13:52:47 CEST 2007 - jkupec@suse.cz
+
+- SYSCONFDIR variable added for modifying /etc
+
+-------------------------------------------------------------------
+Tue Sep 11 12:18:11 CEST 2007 - schubi@suse.de
+
+- uninstallable resolvable -->suggested solution: delete; Bug 308164
+- r 7177
+
+-------------------------------------------------------------------
+Mon Sep 10 16:19:05 CEST 2007 - schubi@suse.de
+
+- new translations added
+- r 7166
+- version 3.22.6
+
+-------------------------------------------------------------------
+Mon Sep 10 15:50:49 CEST 2007 - lslezak@suse.cz
+
+- fixed disk usage counting of updated packages (#308362)
+
+-------------------------------------------------------------------
+Mon Sep 10 14:17:29 CEST 2007 - schubi@suse.de
+
+- Splitting packages: Take the package with the best
+  architecture,edition ONLY; Bug 308591
+- r 7160
+
+-------------------------------------------------------------------
+Mon Sep 10 10:55:45 CEST 2007 - lslezak@suse.cz
+
+- properly report fallback disk usage size (in kB instead of bytes)
+  when disk usage is not known (YUM repos) (#308475)
+
+-------------------------------------------------------------------
+Fri Sep  7 13:27:26 CEST 2007 - ma@suse.de
+
+- Install a sample /etc/zypp.conf. (#306615)
+- Fixed missing soversion symlink in package.
+- version 3.22.5
+- revision 7150
+
+-------------------------------------------------------------------
+Fri Sep  7 13:03:48 CEST 2007 - schubi@suse.de
+
+- RequirementIsMet: return true only if ALL Atoms are NOT incomplete; Bug
+  308252
+- r 7143
+
+-------------------------------------------------------------------
+Thu Sep  6 18:22:36 CEST 2007 - schubi@suse.de
+
+- Error: Select two candidate with the same name while update.
+  Solution: If there is a candidate which is already selected for installation -->
+  take thatone #308082
+- r 7132
+
+-------------------------------------------------------------------
+Thu Sep  6 14:11:58 CEST 2007 - ma@suse.de
+
+- Work arround installed patterns providing an empty vendor string. (#307743)
+- Let the solver treat vendor suse and opensuse as equivalent.
+- version 3.22.4
+
+-------------------------------------------------------------------
+Thu Sep  6 12:33:27 CEST 2007 - schubi@suse.de
+
+- Checking item before evaluating the concerning vendor. bug #307941
+- r 7119
+
+-------------------------------------------------------------------
+Wed Sep  6 02:24:37 CEST 2007 - dmacvicar@suse.de
+
+- Fix for bug #307163 - empty package descriptions
+  a.k.a shared tag not 100% implemented
+- r 7117
+- version 3.22.3
+
+-------------------------------------------------------------------
+Wed Sep  5 17:52:37 CEST 2007 - schubi@suse.de
+
+- logging "reverse" NEEDED_BY in the detail description of solver
+  problems.
+- improved error message if a requiremnt is not fulfilled Bug 307743
+- Add "ignore" option to the solution if a requirement is not fulfilled
+  Bug 304276
+- revision 7113
+
+-------------------------------------------------------------------
+Wed Sep  5 14:33:02 CEST 2007 - jkupec@suse.cz
+
+- fixed the order of operands of susetags local metadata status
+  computation which caused the YaST repositories to always get
+  refreshed (part of bug #304310)
+- revision 7107
+- version 3.22.2
+
+-------------------------------------------------------------------
+Tue Sep  4 12:09:10 CEST 2007 - schubi@suse.de
+
+- comparing vendor with VendorAttr::equivalent
+- revision 7103
+
+-------------------------------------------------------------------
+Mon Sep  3 18:43:51 CEST 2007 - schwab@suse.de
+
+- Use $RPM_OPT_FLAGS.
+
+-------------------------------------------------------------------
+Mon Sep  3 17:14:02 CEST 2007 - ma@suse.de
+
+- Reset transact bits when switching status from
+  "update" to "protected" (#246976)
+- version 3.22.1
+- revision 7094
+
+-------------------------------------------------------------------
+Mon Sep  3 13:56:42 CEST 2007 - schubi@suse.de
+
+- new translations added
+- rev 7083
+
+-------------------------------------------------------------------
+Fri Aug 31 22:10:55 CEST 2007 - ma@suse.de
+
+- Added ability to switch off use of patch and delta rpms via zypp.conf (#305864)
+   [main]
+   download.use_patchrpm = no
+   download.use_deltarpm = no
+- version 3.22.0
+- revision 7069
+
+-------------------------------------------------------------------
+Fri Aug 31 19:56:40 CEST 2007 - ma@suse.de
+
+- On demand translate patch requirements into a list of atoms.
+  Required by the UI to display packages acssociated with a patch.
+  (#300612)
+- version 3.21.1
+- revision 7065
+
+-------------------------------------------------------------------
+Fri Aug 31 13:59:54 CEST 2007 - kkaempf@suse.de
+
+- enrich ResolverInfo with the reason if a user-initiated request
+  fails (#304325, #306240)
+- r 7051
+
+-------------------------------------------------------------------
+Thu Aug 30 20:10:00 CEST 2007 - jkupec@suse.cz
+
+- added missing implementation of LogControl::setLineFormater()
+  (lslezak)
+- version 3.21.0
+- revision 7041
+
+-------------------------------------------------------------------
+Thu Aug 30 18:55:15 CEST 2007 - jkupec@suse.cz
+
+- enable changing url in requestMedia callback (#294481)
+- revision 7037
+
+-------------------------------------------------------------------
+Thu Aug 30 18:22:32 CEST 2007 - ma@suse.de
+
+- Filter readonly mount points in DiskUsageCounter (#297405)
+- revision 7030
+
+-------------------------------------------------------------------
+Thu Aug 30 17:31:33 CEST 2007 - jkupec@suse.cz
+
+- remember the cause of the RepoException when refreshing metadata
+  (#301022)
+- r7023
+
+-------------------------------------------------------------------
+Thu Aug 30 17:25:14 CEST 2007 - ma@suse.de
+
+- Safe fix for bug #299680.
+- version 3.20.1
+- revision 7026
+
+-------------------------------------------------------------------
+Thu Aug 30 15:48:03 CEST 2007 - schubi@suse.de
+
+- Bugfix: If a requirement has been fulfilled by more than one language
+  resolvables only thatone will be taken which fits to the selected
+  language.
+- r 7018
+
+-------------------------------------------------------------------
+Thu Aug 30 15:14:23 CEST 2007 - jkupec@suse.cz
+
+- correct error code for media errors in MediaCurl::doGetFileCopy()
+  affects only zypper error output, does not affect YaST
+- r7013
+
+-------------------------------------------------------------------
+Wed Aug 29 18:28:41 CEST 2007 - jkupec@suse.cz
+
+- reverted blocking of requestMedia from r6271 (#301710)
+- r6999
+
+-------------------------------------------------------------------
+Wed Aug 29 18:09:29 CEST 2007 - kkaempf@suse.de
+
+- prevent progress report in destructor (#299680)
+- r6998
+
+-------------------------------------------------------------------
+Wed Aug 29 18:02:36 CEST 2007 - jkupec@suse.cz
+
+- treat non-filelists.xml <file> entries as file provides capabilities
+  in YUM parser (#304701)
+- r6992
+
+-------------------------------------------------------------------
+Wed Aug 29 16:32:01 CEST 2007 - kkaempf@suse.de
+
+- the media.1/media uniquely identifies a 'susetags' repo, not
+  the content file (#304200)
+
+-------------------------------------------------------------------
+Wed Aug 29 15:42:17 CEST 2007 - jkupec@suse.cz
+
+- fixed locale dir (#304649)
+- r6984
+
+-------------------------------------------------------------------
+Wed Aug 29 15:39:52 CEST 2007 - kkaempf@suse.de
+
+- don't treat normal package license as "license to confirm"
+  (#305906)
+
+-------------------------------------------------------------------
+Wed Aug 29 15:28:53 CEST 2007 - ma@suse.de
+
+- Fixed fix for #293039. Segfault due to uninitialzed data.
+- version 3.19.3
+- revision 6980
+
+-------------------------------------------------------------------
+Wed Aug 29 15:10:20 CEST 2007 - schubi@suse.de
+
+- reduced too much verbosed ResolverContext logging; Bug 303971
+- r 6977
+
+-------------------------------------------------------------------
+Wed Aug 29 13:55:28 CEST 2007 - ma@suse.de
+
+- Fixed PlainDir repositories to provide real disk usage data. For
+  repomd and others that do not provide any detailed disk usage info,
+  assume the packgage size is required below "/". Peviously they were
+  treated as being empy.
+- version 3.19.2
+- revision 6972
+
+-------------------------------------------------------------------
+Wed Aug 29 13:36:42 CEST 2007 - schubi@suse.de
+
+- Add a new solver solution in the case of running in a timeout:
+  ProblemSolutionDoubleTimeout.h
+  #Bug 302496
+- revision 6970
+
+-------------------------------------------------------------------
+Wed Aug 29 11:23:26 CEST 2007 - dmacvicar@suse.de
+
+- bug in fix for (#292986)
+
+-------------------------------------------------------------------
+Tue Aug 28 18:48:26 CEST 2007 - dmacvicar@suse.de
+
+- (#297001) - libzypp: can't skip broken packages
+- re enable importing zypp keyring from rpm.(#302379)
+- 3.19.1
+
+-------------------------------------------------------------------
+Tue Aug 28 17:30:30 CEST 2007 - kkaempf@suse.de
+
+- rename ResolvableQuery::iterateResolvablesByKindsAndStrings
+  to ResolvableQuery::iterateResolvablesByKindsAndStringsAndRepos
+  in order to support query-by-repo (#305384)
+- fix iterateResolvablesByKindsAndStringsAndRepos to take any
+  number of kinds, names, or repos (#305347)
+- remove ResolvableQuery::iterateResolvablesByKind, not needed
+- version 3.19.0
+- rev 6935
+
+-------------------------------------------------------------------
+Tue Aug 28 11:27:53 CEST 2007 - dmacvicar@suse.de
+
+- real fix for reading signature ids. (#390535).
+- delete metadata when removing repo (#301037).
+
+-------------------------------------------------------------------
+Mon Aug 27 23:52:41 CEST 2007 - dmacvicar@suse.de
+
+- following behaviour for setPartitions
+  - if they are not set, they are detected
+  - if they are set, that value is used.
+  - if value set or detected is empty, all disk usage
+    information is read. Otherwise just values in those
+    mount points.
+    Should work for installation as long as detectPartitions
+    is empty at installation.(#293039)
+
+-------------------------------------------------------------------
+Mon Aug 27 17:08:41 CEST 2007 - kkaempf@suse.de
+
+- Add ZConfig::overrideSystemArchitecture() to override zypp arch
+  from external, e.g. for the testcases
+- Honor ZYPP_CONF environment variable to override the buit-in
+  /etc/zypp/zypp.conf
+- Check architecture at handout() to prevent NULL ptr reference.
+- Bug 301286
+- rev 6908
+
+-------------------------------------------------------------------
+Mon Aug 27 15:53:52 CEST 2007 - schubi@suse.de
+
+-The solver generate an establish call for all
+ resolvables which has filesystemcaps if there is not a valid result from a
+ former solver run available. This covers:
+ * Initial solver run
+ * Changing of filesystem whithin a workflow, cause the solver results will
+   be reset if the filesystem dependencies have been changed
+ Bug 271912
+- r 6901
+
+-------------------------------------------------------------------
+Sun Aug 26 15:49:27 CEST 2007 - kkaempf@suse.de
+
+- pass location to plaindir package (#303751)
+- Add name of file in question to checksum/signature related
+  exceptions.
+- pass basename of file to verifyFileSignatureWorkflow (instead of
+  empty string).
+- filter out incompatible architectures when parsing sustags
+  repos (first half of #301286)
+- r 6882
+
+-------------------------------------------------------------------
+Fri Aug 24 11:52:27 CEST 2007 - dmacvicar@suse.de
+
+- don't run source migration if yast is running in
+  intsys mode (#297136)
+- signature and checksum verification fixes. Still pending
+  problem ZYpp getting no output from gpg when running from zypper.
+  (#302059)
+
+-------------------------------------------------------------------
+Thu Aug 23 13:51:41 CEST 2007 - schubi@suse.de
+
+- Do not strip resolvables which have the same name but different kind
+  (ResolverInfo*)
+- Flag info NEEDEDBY correctly if it will be used by freshen/supplement
+- r 6830
+
+-------------------------------------------------------------------
+Wed Aug 22 18:58:44 CEST 2007 - mvidner@suse.cz
+
+- Do not use "a-z" in regexes. Fixes "Invalid Url scheme 'http'" in
+  the Estonian locale (#302525).
+
+-------------------------------------------------------------------
+Wed Aug 22 17:59:39 CEST 2007 - aschnell@suse.de
+
+- added remembering of exception history at various places
+
+-------------------------------------------------------------------
+Wed Aug 22 16:52:09 CEST 2007 - schubi@suse.de
+
+- Bugfixes concerning vendor handling:
+- first bug:
+  Installed A-1.0(vendor SuSE)
+  Available A-2.0(other vendor)
+  A will not be regarded as "unmaintained". So it will not be deleted.
+- second bug:
+  A need B-2.0. B-1.0 is installed but has another vendor. Report a
+  proper errmessage.
+- Testcase : solution-tests/vendor-test.xml
+- r 6812
+- 3.18.4
+
+-------------------------------------------------------------------
+Tue Aug 21 15:38:08 CEST 2007 - dmacvicar@suse.de
+
+- ignore HASH key for download (#300982)
+
+-------------------------------------------------------------------
+Tue Aug 21 15:17:23 CEST 2007 - schubi@suse.de
+
+- Added explicitly_requested as parameter in ResolverContext::Uninstall
+  Bug 299819
+- revision 6794
+
+-------------------------------------------------------------------
+Tue Aug 21 13:06:44 CEST 2007 - dmacvicar@suse.de
+
+- If no mount information is available, parse all DU entries.
+- read only hack mode for migrate-sources. We actually do add
+  repositories with it, but no harm. (#292986)
+- fix some typos in exceptions (#301331)
+- 3.18.3
+
+-------------------------------------------------------------------
+Mon Aug 20 16:06:36 CEST 2007 - schubi@suse.de
+
+- Do not regard explicit request in order to recognize updated packages
+  correctly. Bug 301676
+- Updated translations
+- r 6766
+- 3.18.2
+
+-------------------------------------------------------------------
+Mon Aug 20 15:35:46 CEST 2007 - mvidner@suse.cz
+
+- package-manager script: Call /sbin/yast2 with full path because of
+  gnomesu (#269873).
+
+-------------------------------------------------------------------
+Mon Aug 20 15:23:14 CEST 2007 - dmacvicar@suse.de
+
+- add support for the HASH key. (#300982)
+- Use ContentFileParser in Downloader (instead of implementing
+  the parser again, it has a reason, Downloader was written first)
+- update testcases and data to cover the new HASH key
+- 3.18.1
+
+-------------------------------------------------------------------
+Mon Aug 20 13:21:34 CEST 2007 - kkaempf@suse.de
+
+- unify query API for kind and name, summary, description
+  (incomplete)
+- rev 6761, version 3.18.0
+
+-------------------------------------------------------------------
+Fri Aug 17 12:30:55 CEST 2007 - kkaempf@suse.de
+
+- add iterateResolvablesByKindsAndName
+- rev 6735, version 3.17.13
+
+-------------------------------------------------------------------
+Fri Aug 17 11:45:24 CEST 2007 - kkaempf@suse.de
+
+- rename ResolvableQuery::queryByName to iterateResolvablesByName
+- fix reading of kind and repository in ResolvableQuery
+- add reverse lookups in CacheTypes
+- add iterateResolvablesByKind
+- rev 6733
+
+-------------------------------------------------------------------
+Fri Aug 17 10:37:56 CEST 2007 - schubi@suse.de
+
+- shorten solver error messages Bug 259894
+- rev 6723
+
+-------------------------------------------------------------------
+Thu Aug 16 23:43:50 CEST 2007 - kkaempf@suse.de
+
+- fix ResolvableQuery::query(), add ResolvableQuery::queryByName()
+  install zypp/cache header files.
+- rev 6719, version 3.16.13
+
+-------------------------------------------------------------------
+Thu Aug 16 17:21:52 CEST 2007 - kkaempf@suse.de
+
+- discard pattern files with incompatbile architecture, both
+  for download and for parsing. (#298716)
+- rev 6711
+
+-------------------------------------------------------------------
+Thu Aug 16 17:12:45 CEST 2007 - kkaempf@suse.de
+
+- run sqlite asynchronously and add sql index files where
+  appropriate, gives 6x performance on certain operations.
+  Bumping cache schema version to 1004.
+  (#300998)
+- rev 6710
+
+-------------------------------------------------------------------
+Thu Aug 16 17:08:58 CEST 2007 - kkaempf@suse.de
+
+- fix String::endsWith (#301038)
+- rev 6709
+
+-------------------------------------------------------------------
+Thu Aug 16 11:16:25 CEST 2007 - schubi@suse.de
+
+- added an _explicitly_requested in QueueItemConflict ( as already in
+  QueueItemEstablish, QueueItemInstall, QueueItemUninstall ) in order to
+  remove the conflicting item without an error message. Bug 299819
+- rev 6699
+
+-------------------------------------------------------------------
+Thu Aug 16 07:23:31 CEST 2007 - jkupec@suse.cz
+
+- forgot to set default refresh policy in checkIfToRefreshMetadata
+- repo.refresh.delay default set to 10 minutes
+- 6693
+
+-------------------------------------------------------------------
+Wed Aug 15 15:32:36 CEST 2007 - schubi@suse.de
+
+- Bugfix while regarding correct vendor in update
+- r6677
+- version 3.15.0
+
+-------------------------------------------------------------------
+Wed Aug 15 13:57:04 CEST 2007 - jkupec@suse.cz
+
+- repo.refresh.delay=<minutes> (ZConfig, "main" section) support
+  added to delay next check & refresh until the specified number of
+  minutes has passed from the last check or refresh (FATE #301991).
+  Revisions: 6654, 6656, 6666, and 6667.
+- filesystem::touch(Pathname) added (r6666)
+- RepoManager::touchIndexFile(RepoInfo) added
+- RepoManager::checkIfToRefreshMetadata(RepoInfo,Url,policy):
+  decision to do the refresh moved to this public method.
+- r6667
+
+-------------------------------------------------------------------
+Wed Aug 15 12:38:26 CEST 2007 - schubi@suse.de
+
+- generate a resolver problem if addRequires does not find a resovable
+  #299486
+- rev 6660
+
+-------------------------------------------------------------------
+Tue Aug 14 15:54:15 CEST 2007 - schubi@suse.de
+
+- Prioritized delete request by the user BEFORE delete requests due
+  missing dependencies or conflicting dependencies. Bug 298322
+- rev 6640
+
+-------------------------------------------------------------------
+Fri Aug 10 20:49:20 CEST 2007 - jkupec@suse.cz
+
+- support also "Plaindir" as valid repo type name (#298622)
+- revision 6616
+- version 3.14.0
+
+-------------------------------------------------------------------
+Fri Aug 10 18:03:06 CEST 2007 - dmacvicar@suse.de
+
+- fix segfault in Progress reporting
+- progress report use name instead of alias (#298035)
+- repoinfo returns alias if name is empty
+
+-------------------------------------------------------------------
+Fri Aug 10 16:04:42 CEST 2007 - dmacvicar@suse.de
+
+- merge patch by dmueller to get rid of boost-regex
+
+-------------------------------------------------------------------
+Fri Aug 10 13:50:40 CEST 2007 - dmacvicar@suse.de
+
+- dont create a second CacheStore in the same scope, will lock...
+  #297627
+
+-------------------------------------------------------------------
+Thu Aug  9 15:04:21 CEST 2007 - jkupec@suse.cz
+
+- fixed some RepoManager exception docs & history
+- r6558
+
+-------------------------------------------------------------------
+Thu Aug  9 13:33:59 CEST 2007 - dmacvicar@suse.de
+
+- feature #302135: Graceful update of 3rd party packages
+  Automatic upgrading only sees packages from same vendor
+  This allows not needed to have those locked.
+- Add persistent locks file which allow wildcards. Users
+  can lock certain packages adding lines like "kde* < 3.5"
+- add applyLocks() to apply persistent locks before solving
+
+-------------------------------------------------------------------
+Thu Aug  9 13:28:20 CEST 2007 - schubi@suse.de
+
+-  recognize changes in the pool (e.g. changing /etc/sysconfig/storage
+  #271912 wq
+- Added locking resolvables in the testcases
+- rev 6544
+
+-------------------------------------------------------------------
+Wed Aug  8 16:04:07 CEST 2007 - dmacvicar@suse.de
+
+- add migrate-sources to %post (#292986)
+- 3.13.15
+
+-------------------------------------------------------------------
+Wed Aug  8 13:32:52 CEST 2007 - jkupec@suse.cz
+
+- fixed bug with using wrong files from raw metadata cache
+  (bug #297611) (duncanmv) (r6501, already released in 3.13.14)
+
+-------------------------------------------------------------------
+Wed Aug  8 12:42:39 CEST 2007 - schubi@suse.de
+
+- fix in "ignore conflicts" if the conflict has been caused by an obsolete
+  Bug# 297795
+- r 6517
+
+-------------------------------------------------------------------
+Tue Aug  7 17:04:47 CEST 2007 - dmacvicar@suse.de
+
+- implement susetags support for compressed metadata
+  and testcases. (feature #301916)
+- implement disk usage in cache. For installation requires
+  some changes in YaST to setup the ZYpp getPartitions()
+  before repos are cached. (bug #293039)
+- added testcases for diskusage
+- 3.13.14
+
+-------------------------------------------------------------------
+Mon Aug  6 10:19:12 CEST 2007 - jkupec@suse.cz
+
+- fixed YUM parser to properly create source packages
+- disabled reading of filelists.xml.gz by default (the data are
+  currently not stored anyway)
+- revision 6481
+- version 3.13.13
+
+-------------------------------------------------------------------
+Sat Aug  4 01:03:19 CEST 2007 - ma@suse.de
+
+- Don't download unwanted translation files (#293740).
+- revision 6470
+
+-------------------------------------------------------------------
+Fri Aug  3 23:52:18 CEST 2007 - ma@suse.de
+
+- Fix susetags repo to parse dikusage data (#293039)
+- revision 6467
+- version 3.13.12
+
+-------------------------------------------------------------------
+Fri Aug  3 18:56:47 CEST 2007 - ma@suse.de
+
+- Add product attribute 'type' (aka 'category' which is now
+  deprecated). Adapted sustags and yum parsers to parse and
+  provide this value.
+- revision 6464
+- version 3.13.11
+
+-------------------------------------------------------------------
+Fri Aug  3 17:29:56 CEST 2007 - dmacvicar@suse.de
+
+- fix modalias rel column number
+- version 3.13.10
+
+-------------------------------------------------------------------
+Fri Aug  3 16:35:36 CEST 2007 - dmacvicar@suse.de
+
+- Implemented option repo.add.probe to allow probing
+  the added repositories
+- version 3.13.9
+
+-------------------------------------------------------------------
+Fri Aug  3 16:07:46 CEST 2007 - schubi@suse.de
+
+- Fixed detection of renamed packages while update.
+- Added new translations
+- rev 6445
+
+-------------------------------------------------------------------
+Fri Aug  3 15:02:28 CEST 2007 - ma@suse.de
+
+- Speed up retrieving MediaNr attribute, as it slows down install
+  order calculation. (#297173)
+- revision 6442
+
+-------------------------------------------------------------------
+Fri Aug  3 14:13:36 CEST 2007 - dmacvicar@suse.de
+
+- Fixed cache schema upgrade
+
+-------------------------------------------------------------------
+Fri Aug  3 13:38:17 CEST 2007 - ma@suse.de
+
+- Fixed pattern includes and extends attributes.
+- revision 6431
+- version 3.13.8
+
+-------------------------------------------------------------------
+Fri Aug  3 12:33:41 CEST 2007 - schubi@suse.de
+
+- API for retrieving additional dependencies" solver/detail/Resolver.h
+- Handle additional dependencies in the testcases
+- Handle system and language dependencies in the testcases correctly.
+- r 6418
+
+-------------------------------------------------------------------
+Thu Aug  2 18:14:12 CEST 2007 - ma@suse.de
+
+- Indicate changed pool content if /etc/sysconfig/storage USED_FS_LIST
+  has changed. Resolver must discard any cached filesystem dependencies.
+  (required for #271912)
+- revision 6404
+
+-------------------------------------------------------------------
+Thu Aug  2 16:37:06 CEST 2007 - ma@suse.de
+
+- Indicate changed pool content to the resolver. (required for #271912)
+- revision 6398
+- version 3.13.7
+
+-------------------------------------------------------------------
+Thu Aug  2 10:16:16 CEST 2007 - schubi@suse.de
+
+- speedup error handling. Do not log ResolveInfo anymore. Set limit of 20
+  problems. Bug 280387
+- r 6378
+- version 3.13.6
+
+-------------------------------------------------------------------
+Wed Aug  1 18:51:12 CEST 2007 - ma@suse.de
+
+- Added interface to install source packages via zypper.
+- revision 6373
+- version 3.13.5
+
+-------------------------------------------------------------------
+Tue Jul 31 22:01:08 CEST 2007 - ma@suse.de
+
+- Added package attributes Package::sourcePkgName and
+  Package::sourcePkgEdition. Name and edition of the source
+  rpm this package was built from.
+- Added ZYpp::installSrcPackage to install a single source package.
+- revision 6353
+- version 3.13.4
+
+-------------------------------------------------------------------
+Tue Jul 31 19:06:54 CEST 2007 - ma@suse.de
+
+- Temorary files and directories created by makeSibling use the
+  same protection as the original.
+- revision 6344
+- version 3.13.3
+
+-------------------------------------------------------------------
+Tue Jul 31 10:37:13 CEST 2007 - schubi@suse.de
+
+- Removed keepExtras from resolvePool. This will be handled
+  in the solver internally now. #294727
+- Checking if item really exists (#295544; ResolverInfoContainer.cc)
+- revision 6317
+- version 3.13.2
+
+-------------------------------------------------------------------
+Mon Jul 30 17:23:19 CEST 2007 - ma@suse.de
+
+- Remove tribool from RepoInfo's interface.
+- revision 6301
+- version 3.13.1
+
+-------------------------------------------------------------------
+Mon Jul 30 13:59:33 CEST 2007 - ma@suse.de
+
+- Fixed wrong media number reported by script, message and patch.
+- Fixed script API to provide the scripts location on media (if not
+  inlined).
+- Introduced ScripProvider to make a script available on the local
+  disk.
+- revision 6288
+- version 3.13.0
+
+-------------------------------------------------------------------
+Fri Jul 27 18:08:42 CEST 2007 - jkupec@suse.cz
+
+- don't request media chage if the media is not changeable (like
+  e.g. http)
+- revision 6271
+- version 3.12.1
+
+-------------------------------------------------------------------
+Fri Jul 27 17:40:44 CEST 2007 - dmacvicar@suse.de
+
+- progress ticks for clean cache
+- ZConfig: remove default from names.
+- re enable reading cache callbacks
+- ini parser without boost::regexp
+  patch by dmueller (#152447)
+
+-------------------------------------------------------------------
+Fri Jul 27 17:16:27 CEST 2007 - ma@suse.de
+
+- Fixed package to provide the location media number. (#294496)
+- revision 6263
+
+-------------------------------------------------------------------
+Fri Jul 27 16:31:38 CEST 2007 - jkupec@suse.cz
+
+- RepoFileReader - ignore empty url keys (baseurl, mirrorlist,
+  gpgkey) instead of throwing bad url exception
+- revision 6259
+
+-------------------------------------------------------------------
+Thu Jul 26 16:41:30 CEST 2007 - kkaempf@suse.de
+
+- Make clearing of extra dependencies/conflicts configurable when
+  resolvePool(). Leave the default as before (clear extras).
+  Bug # 294727
+- revision 6233
+- version 3.12.0
+
+-------------------------------------------------------------------
+Thu Jul 26 10:58:27 CEST 2007 - dmacvicar@suse.de
+
+- /var/lib/zypp/cache -> /var/cache/zypp
+  (#292419)
+- ini parser without boost::regexp, patch by dmueller
+  (#152447)
+
+-------------------------------------------------------------------
+Wed Jul 25 13:50:06 CEST 2007 - ma@suse.de
+
+- Make temp directory configurable  via environment
+  variable ZYPPTMPDIR.
+- revision 6202
+- version 3.11.11
+
+-------------------------------------------------------------------
+Tue Jul 24 17:55:49 CEST 2007 - ma@suse.de
+
+- Fixed bug in smart pointer comparison.
+
+-------------------------------------------------------------------
+Mon Jul 23 13:05:53 CEST 2007 - ma@suse.de
+
+- Fix failing rename of metadata download directories across
+  filesystem boundaries.
+
+-------------------------------------------------------------------
+Fri Jul 20 16:10:59 CEST 2007 - ma@suse.de
+
+- Fixed malicious gettext include.
+- Make ZConfig a singleton.
+- revision 6123
+
+-------------------------------------------------------------------
+Thu Jul 19 15:52:03 CEST 2007 - ma@suse.de
+
+- Fixed repo::provideFile to set a deleter for downloaded files
+  (#293004).
+- revision 6094
+- version 3.11.10
+
+-------------------------------------------------------------------
+Thu Jul 19 12:58:12 CEST 2007 - ma@suse.de
+
+- Query ByRepository now takes as well an alias.
+
+-------------------------------------------------------------------
+Wed Jul 18 16:40:35 CEST 2007 - dmacvicar@suse.de
+
+- fix retrieval of container attributes in cache
+  (#292698)
+
+-------------------------------------------------------------------
+Wed Jul 18 16:12:06 CEST 2007 - ma@suse.de
+
+- Port zypp-query-pool (#292404)
+- revision 6069
+- version 3.11.9
+
+-------------------------------------------------------------------
+Wed Jul 18 16:09:04 CEST 2007 - schubi@suse.de
+
+- added new calls in Resolver.h: addRequire,addConflict
+
+-------------------------------------------------------------------
+Wed Jul 18 14:43:04 CEST 2007 - ma@suse.de
+
+- Fixed IniParser to allow '=' in values (#292669)
+- revision 6063
+
+-------------------------------------------------------------------
+Wed Jul 18 14:16:59 CEST 2007 - dmacvicar@suse.de
+
+- allow / in alias (#292628)
+
+-------------------------------------------------------------------
+Wed Jul 18 12:38:55 CEST 2007 - ma@suse.de
+
+- Fixed repo::provideFile to throw on error.
+- Fixed ResolvableQuery to use 0 as default for non existing
+  numerical values.
+- revision 6058
+
+-------------------------------------------------------------------
+Tue Jul 17 15:57:03 CEST 2007 - ma@suse.de
+
+- Fixed OnMediLocation to use safe defaults. Added setLocaltion and
+  additional ctor for convenience.
+- revision 6047
+
+-------------------------------------------------------------------
+Tue Jul 17 10:45:39 CEST 2007 - schubi@suse.de
+
+- Evalute update canditate:
+  Take canditates only which are really installable. Bug 292077
+  r 6034
+
+-------------------------------------------------------------------
+Mon Jul 16 15:41:31 CEST 2007 - jkupec@suse.cz
+
+- fixed some tribool bugs in RepoInfo
+- revision 6022
+
+-------------------------------------------------------------------
+Mon Jul 16 11:43:33 CEST 2007 - dmacvicar@suse.de
+
+- fix reading of non existant repo
+- r6018
+- first submission to stable
+- fix keywords parsing in susetags parser
+- version 3.11.8
+
+-------------------------------------------------------------------
+Wed Jul 11 19:14:47 CEST 2007 - jkupec@suse.cz
+
+- make resolvable query complete exceptionless with the database
+- test that packages have some attributes
+- disable progress adaptor for now
+- revision 5977
+- version 3.11.7
+
+-------------------------------------------------------------------
+Wed Jul 11 14:37:02 CEST 2007 - ma@suse.de
+
+- fixed unresolved symbols
+- revision 5972
+- version 3.11.6
+
+-------------------------------------------------------------------
+Wed Jul 11 13:31:39 CEST 2007 - ma@suse.de
+
+- fixed TranslatedText creating unwanted entries.
+- fixed capability processing
+- revision 5964
+- version 3.11.5
+
+-------------------------------------------------------------------
+Tue Jul 10 20:01:18 CEST 2007 - jkupec@suse.cz
+
+- MediaSetAccess::release() added
+- Use attachDesiredMedia in MediaProducts
+- Progress reporting improved
+- revision 5959
+- version 3.11.4
+
+-------------------------------------------------------------------
+Tue Jul 10 14:23:13 CEST 2007 - ma@suse.de
+
+- fixed parsing translated texts.
+- added source packages.
+- revision 5947
+- version 3.11.3
+
+-------------------------------------------------------------------
+Tue Jul 10 13:11:31 CEST 2007 - jkupec@suse.cz
+
+- repo callbacks fixed
+- Fixed YUM parser progress reporting
+- Added CombinedProgressData
+- make RepoImpl::resolvables() load lazy
+- MediaProducts added for scanning products file
+- Implement cache schema versioning and automatic invalidation of
+  cache when schema changes
+- revision 5942
+- version 3.11.2
+
+-------------------------------------------------------------------
+Fri Jul  6 13:51:50 CEST 2007 - ma@suse.de
+
+- Propagate pools repository_iterator to the UI
+- revision 5911
+- version 3.11.1
+
+-------------------------------------------------------------------
+Thu Jul  5 17:02:08 CEST 2007 - ma@suse.de
+
+- Package::location is now returns an OnMediaLocation
+- archivesize() renamed to downloadSize()
+- Allow to iterate all Repositories that contribute
+  Resolvables to the Pool.
+- MediaSetAccess::provideDir added
+- remove useless url check
+- add old-api-style wrapper
+- Add MediaProducts class
+- revision 5892
+- version 3.11.0
+
+-------------------------------------------------------------------
+Thu Jul  5 09:31:02 CEST 2007 - schubi@suse.de
+
+- function isInstalledBy/installs
+  Added an initial installation flag which shows if the item has been
+  triggered for installation, or the dependency is already satisfied.
+- Revision 5884
+- Version 3.4.0
+
+-------------------------------------------------------------------
+Tue Jul  3 13:07:31 CEST 2007 - jkupec@suse.cz
+
+- removed unused %{prefix}/lib/zypp from %files in spec file
+- revision 5870
+
+-------------------------------------------------------------------
+Tue Jul  3 10:37:37 CEST 2007 - jkupec@suse.cz
+
+- Old API (SourceManager, metadata parsers) dropped in favor of the
+  new refactored ones (RepoManager, RepoParser(s), cache subtree,
+  repo subtree).
+- Some new API improvements.
+- revision 5868
+- version 3.10.0 (bumped minor to 10 to indicate refactoring branch)
+
+-------------------------------------------------------------------
+Fri Jun 22 15:28:03 CEST 2007 - schubi@suse.de
+
+- New API calls which provides more information about one
+  resolvable after a solverrun:
+  isInstalledBy (const PoolItem_Ref item);
+  installs (const PoolItem_Ref item);
+- Revision 5835
+
+-------------------------------------------------------------------
+Thu Jun 21 15:58:16 CEST 2007 - adrian@suse.de
+
+- fix changelog entry order
+
+-------------------------------------------------------------------
+Wed Jun 20 11:47:33 CEST 2007 - schubi@suse.de
+
+- Ignore conflicting items which are uninstallable
+- Create a "needed by" info if a requirement is still fulfilled.
+- Allow only one needed_by and needed_by_capability in QueueItemInstall
+- Added capability and type (REQUIRE, RECOMMEND,....) to
+  ResolverInfoNeededBy
+- Evaluate ResolverInfoNeededBy for more information in the error
+  messages
+- Enlarge detail description in the error messages
+- Revision 5807
+
+-------------------------------------------------------------------
+Tue Jun 19 13:59:09 CEST 2007 - ma@suse.de
+
+- Fixes for gcc-4.2
+- revision 5786
+- version 3.3.1
+
+-------------------------------------------------------------------
+Mon Jun 18 17:20:45 CEST 2007 - mvidner@suse.cz
+
+- fixed so versioning from libtool to cmake
+- 3.3.0
+
+-------------------------------------------------------------------
+Mon Jun 18 09:47:32 CEST 2007 - dmacvicar@suse.de
+
+- Use gpg2 instead of gpg for keyring and make
+  the package depend on it (#284211)
+
+-------------------------------------------------------------------
+Mon Jun 11 17:02:49 CEST 2007 - schubi@suse.de
+
+- Required kmp packges FOR EACH installed/to_be_installed kernel will be installed.
+  New dependency "packageand(foo:bar)" which provides an AND dependency by
+  injecting a supplement/freshen.
+  e.G. package novell-cluster-services-kmp-smp
+  supplements: packageand(kernel-smp:novell-cluster-services-kmp)
+  Bug 255011
+- Dont check for architecture changes in atoms (#266178)
+- Revision 5720
+
+-------------------------------------------------------------------
+Wed May 23 17:03:29 CEST 2007 - ma@suse.de
+
+- Fixed package-manager script (#275847)
+- revision 5614
+
+-------------------------------------------------------------------
+Wed May 23 13:32:02 CEST 2007 - schubi@suse.de
+
+- fixed cmake
+- version 3.2.2
+
+-------------------------------------------------------------------
+Wed May 23 11:25:55 CEST 2007 - schubi@suse.de
+
+- Reduced logging in order to speedup solving Bug 275100
+- revision 5603
+- version 3.2.1
+
+-------------------------------------------------------------------
+Tue May 15 14:03:34 CEST 2007 - ma@suse.de
+
+- Fix excess calls to releaseFile. (#274357)
+- revision 5545
+
+-------------------------------------------------------------------
+Wed May  9 18:44:20 CEST 2007 - dmacvicar@suse.de
+
+- Fix importing keys into rpm. (#270125)
+- r5527
+
+-------------------------------------------------------------------
+Wed Apr 18 14:56:17 CEST 2007 - ma@suse.de
+
+- Support filesystem dependencies to add needed filesystem RPMs
+  automatically (Fate 301966).
+- revision 5404
+- version 3.2.0
+
+-------------------------------------------------------------------
+Mon Apr 16 11:23:15 CEST 2007 - jkupec@suse.cz
+
+- avoiding attaching media where not needed (#263207)
+- r5381
+
+-------------------------------------------------------------------
+Fri Apr 13 17:32:55 CEST 2007 - dmacvicar@suse.de
+
+- fix FileCap with attributes
+- r5376
+
+-------------------------------------------------------------------
+Thu Apr 12 02:33:18 CEST 2007 - ma@suse.de
+
+- Fixed computation of install order. Take requirements of
+  an installed packages uninstall scripts into account, if
+  the package is updated. (#258682)
+- revision 5349
+- version 3.1.1
+-------------------------------------------------------------------
+Wed Apr 11 15:24:01 CEST 2007 - ma@suse.de
+
+- Parse and provide package keywords. (Fate 120368)
+- revision 5338
+- version 3.1.0
+
+-------------------------------------------------------------------
+Thu Apr  5 15:50:15 CEST 2007 - schubi@suse.de
+
+- Upgrade: Do NOT delete packages which have unresolved dependencies -->
+  Ask the user. Bug 258322
+- revision 5305
+- version 3.0.3
+
+-------------------------------------------------------------------
+Wed Apr  4 19:39:12 CEST 2007 - ma@suse.de
+
+- Fix restoring of Sources id root prefix is used. (#238165)
+- revision 5299
+- version 3.0.2
+
+-------------------------------------------------------------------
+Wed Apr  4 15:33:54 CEST 2007 - jkupec@suse.cz
+
+- MediaManager::attachDesiredMedia() added to support multiple
+  (CD/DVD) drives (fate #3974)
+- r5296
+
+-------------------------------------------------------------------
+Wed Mar 14 14:33:26 CET 2007 - schubi@suse.de
+
+- If there is no valid solver result and NOT all resolvables ( other
+  architecture) has been regarded, let the user decide making a new
+  solver run with ALL available resolvables. Bug 223440
+- reducing logging (error -> debug)  bug 252921
+- Revision 5219
+
+-------------------------------------------------------------------
+Fri Mar  9 00:08:38 CET 2007 - ma@suse.de
+
+- Allow configuration of trusted vendors via
+  /var/lib/zypp/db/trustedVendors. (#186636)
+- revision 5194
+- version 3.0.1
+
+-------------------------------------------------------------------
+Wed Mar  7 15:20:29 CET 2007 - dmacvicar@suse.de
+
+- libzypp-devel -> libzypp requirement is not versioned
+  (#251086)
+- r5181
+
+-------------------------------------------------------------------
+Tue Mar  6 17:25:49 CET 2007 - schubi@suse.de
+
+- Using already existing valid solver results for further solver runs.
+  (partiell solving)
+- r5169
+
+-------------------------------------------------------------------
+Fri Mar  2 16:11:16 CET 2007 - dmacvicar@suse.de
+
+- fix link order
+- r5165
+
+-------------------------------------------------------------------
+Tue Feb 27 15:50:39 CET 2007 - dmacvicar@suse.de
+
+- merging from 10.2 / SP1
+- #247459 ftp probing
+  denied == dont exists in ftp
+- r5124
+
+-------------------------------------------------------------------
+Tue Feb 27 15:35:06 CET 2007 - schubi@suse.de
+
+- Merging solver related stuff from SuSE-Linux-10_2-Branch ( till r5111):
+- Simultaneouqusly establishing of items which are conflicting eachother is
+  useless. So only one will be established. Fixed in QueueItemInstall.cc
+  Bug 243595
+- Added new upgrade options to fine tune version and patch handling.
+  (F301990)
+- The context of establishPool will be stored in Resolver and will be
+  regarded for the next solver run everytime. So it will be not reset by
+  any solver run anymore.
+  bug 191810 ( A broken patch will not be installed again)
+
+-------------------------------------------------------------------
+Fri Feb 23 17:40:23 CET 2007 - jkupec@suse.cz
+
+- adding sotf,timeo=X nfs mount options by default (#235211)
+- r5093
+
+-------------------------------------------------------------------
+Fri Feb 23 14:16:19 CET 2007 - jkupec@suse.cz
+
+- support for HTTP authentication prompt added (#190609)
+- fixed problem with empty path in URL
+  in MediaCurl::doGetFileCopy()
+- r5085
+
+-------------------------------------------------------------------
+Mon Feb 12 15:28:08 CET 2007 - jkupec@suse.cz
+
+- Merged revisions 4926-4993,4995,4998-5006 via svnmerge from
+  SuSE-Linux-10_2-Branch
+
+- verifySystem: Regarding patterns too. Bug 239750 (schubi)
+- verifySystem: The result will be set to APPL_HIGH, so it will be not
+  reset by a second "normal" solver run. #239281 (schubi)
+- yast2 reports invalid URL as 'unknown source type'
+  (#209961) (dmacvicar)
+- Added freshen language dependency in supplemements too if there is no
+  entry in supplements.
+  #240617;IPA fonts are not installed even if select Japanese
+  language (schubi)
+- Setting allowed authentication methods to "basic,digest" if none
+  provided in URL (#243006) (jkupec)
+- Stopping after 50 valid solver results. Anymore would be useless. Bug
+  243595 (schubi)
+- r 5007
+
+-------------------------------------------------------------------
+Mon Feb 12 10:37:02 CET 2007 - mvidner@suse.cz
+
+- package-manager: use a generic su script from XDG (#235303, #244442)
+
+-------------------------------------------------------------------
+Fri Feb  9 15:21:55 CET 2007 - jkupec@suse.cz
+
+- Setting allowed authentication methods to "basic,digest" if none
+  provided in URL (#243006)
+- fixed gettext and rpath problems in configure.ac and Makefile.cvs
+  (mvidner)
+- r4999
+
+-------------------------------------------------------------------
+Wed Feb  7 15:52:29 CET 2007 - jkupec@suse.cz
+
+- MediaCurlException::dumpOn() adjusted for ncurses dialogue
+  (#222602)
+- 4984
+
+-------------------------------------------------------------------
+Mon Jan 29 13:10:37 CET 2007 - dmacvicar@suse.de
+
+- Merged revisions 4907-4926 from SuSE-Linux-10_2-Branch
+
+- update packages: changing architecture is only valid while an
+  system update and NOT while an update via a patch. Last fix does
+  not fit for every case.
+  Bug 230685
+- Patch has selected not the concerning package for update but
+  a package which has provided the required dependencies too.
+  Algorithmus: If there are exactly two providers which differ in architecture
+              prefer the better arch.
+  Fix: Regarding NVRA too. ( only if equal )
+  Bug 238284
+- Download only English and Local translation
+  (#208457)
+- Added solver parameter:
+   tryAllPossibilities: regarding every solver branch ( not only
+                        branches with e.G. best architectures
+   preferHighestVersion: Prefer solver results which have a higher
+                         version number.
+  Bug #238087
+- update packages: changing architecture is only valid while an
+  system update and NOT while an update via a patch.
+  Bug 230685 - x86_64 MozillaFirefox binaries in security update
+  repository
+  Fix of version 2.11.2 has not worked if the first founded item
+  had had another architecture.
+- r4927
+
+-------------------------------------------------------------------
+Wed Jan 24 14:50:33 CET 2007 - lslezak@suse.cz
+
+- added ZYpp::getPartitions() - return the partitinoning
+
+-------------------------------------------------------------------
+Wed Jan 24 14:20:28 CET 2007 - dmacvicar@suse.de
+
+- Merged revisions 4705-4906 via svnmerge from SuSE-Linux-10_2-Branch
+- Corrupt download cannot be skipped
+  (#217425 and #224216)
+- Enable package read ahead/caching in commit per default to reduce
+  interactive media changes. If the environment variable
+  ZYPP_COMMIT_NO_PACKAGE_CACHE is set, caching will be disabled. (F100182)
+- added parameter not to reset resolver results while calling
+  freshen pool Bug: 235761
+- Prepare package read ahead/caching of packages in commit. Adatped the
+  workflow. Caching details are now hidden inside CommitPackageCache.
+  The current implementation still performs no read ahead. (for F100182)
+- Skip invalid or broken rpm database entries. (#231211)
+- verifySystem: check if the solution is valid after calling freshenPool()
+  Bug: 235761
+- Added own call for Resolver::verifySystem with additional
+  hardware/language check in order to keep binary compatibility
+  Fate #301224
+- Rpm requires additional quoting of special chars in filenames.
+  (#233967)
+- Resolver::verifySystem checks for new hardware now by calling
+  freshenPool. This is configureable. Fate #301224
+- merged texts from proofread
+- Don't consider patch/delta rpms if package architecture changes.
+  (#231254)
+- update packages: changing architecture is only valid while an
+  system update and NOT while an update via a patch.
+  Bug 230685 - x86_64 MozillaFirefox binaries in security update repository
+- verifySystem: Do only regard items which will be on the system after the
+  commit. Fate 301178
+- feature #301369
+  Import listed GPG Keys from an trusted installation source
+- fix return call in new xml parser
+- Enable system resolvables in Helix parser (Revision 4787)
+- deptestomatic:
+  Resetting transaction with the correct call;
+  bugfix; Added kind in keep state (Revision 4788)
+- Wrong behaviour in soft install/uninstall. --> Setting Transact with
+  soft (Revision 4789)
+- New call added: maySetToBeUninstalledSoft (Revision 4789)
+  Both are only functions for the solver (Revision 4789)
+- Resetting "by causer" in order to distinguish from state
+  "keep by user". ( function setLock in order to remove lock)
+  (Revision 4789)
+- Better fix for Bug 217574: Checking if the resolveable CAN be deleted soft
+  in QueueItemUninstall.cc (Revision 4790)
+- Regarding "keep state by user". So avoiding "reselecting" by
+  other requirements.
+  Give a corresponding problem solution if a resolvable satisfy
+  a dependency, but has been set to keep by the user.
+  Bug : 222531 (Revision 4791)
+- Allow to disable autoprotection of foreign vendor items. Required
+  in zmd-backend. (F301735).
+- use sqlite-zmd if using > 10.2 in spec. Use the one available
+  durin compile.
+- Item could has already been selected to soft uninstall (e.G. remove
+  pattern which recommend this item ). Do not throw an exception anymore.
+  bug#225278
+- Added syscontent::Reader: Parse serialized set of ResObjects.
+  (for F300729)
+- If there has already been selected another item by the solver (e.g. from
+  another source) we will take thatone in order to avoid parallel
+  installation and there concerning error messages.
+  Bug 224698
+- In order to handle conflicting resolvable we try to update the
+  conflicting item. While evaluating these canditates an already selected
+  candidate will not be regarded. So it could be that an older package
+  will be selected for update although a newer has already been selected. Revision 4765
+- Conflicting items: The resolvable will be obsoleted by another. So it is useless finding an
+  update candidate and evaluate additional branches.
+- r4907
+
+-------------------------------------------------------------------
+Mon Dec  4 15:38:12 CET 2006 - mt@suse.de
+
+- Improved realpath() wrapper in media handler class (#222521).
+- revision 4758
+
+-------------------------------------------------------------------
+Thu Nov 30 09:15:20 CET 2006 - ma@suse.de
+
+- version 3.0.0 (2.x.x now in SuSE-Linux-10_2-Branch)
+- revision 4713
+
+-------------------------------------------------------------------
+Wed Nov 29 19:20:24 CET 2006 - dmacvicar@suse.de
+
+- use sqlite-zmd package for the non yet shipped zypp2/ stuff
+  because backend uses this sqlite and it is no fun to
+  install one and the other to develop (as the -devel packages)
+  conflict.
+- add cmake support for building zypp/ lib.
+  TODO: soinfo, compile testcases, devel, zypp2,docs
+  find rpm, curl and others.
+- dont serialize interactive, as it is
+  calculated now.
+  (it was already fixed as we don't reimplement
+  the method, but we still serialized, parsed)
+- r4709
+
+-------------------------------------------------------------------
+Tue Nov 28 22:40:12 CET 2006 - ma@suse.de
+
+- fixed Patch::interactive to return true as well, if the patch
+  itself has a licence. (#224192)
+- revision 4702
+- version 2.9.2
+
+-------------------------------------------------------------------
+Tue Nov 28 16:36:56 CET 2006 - mt@suse.de
+
+- Added search for /sbin/vol_id tool - that is in /lib/udev/vol_id
+  on the installation image (#213852).
+- revision 4700
+
+-------------------------------------------------------------------
+Tue Nov 28 11:44:52 CET 2006 - schubi@suse.de
+
+- Avoid duplicate pool entries; Bug 223750; second part of the fix
+- r4698
+- Version 2.9.1
+
+-------------------------------------------------------------------
+Mon Nov 27 18:29:58 CET 2006 - schubi@suse.de
+
+- Pool has multi instances of an item in the pool. Reduced this error
+  to items which are identically at least. #217574 and #223750
+- r4695
+
+-------------------------------------------------------------------
+Mon Nov 27 17:37:44 CET 2006 - dmacvicar@suse.de
+
+- replace spaces to underscores in product names
+- 2.8.7
+- r4688
+
+-------------------------------------------------------------------
+Mon Nov 27 16:54:03 CET 2006 - dmacvicar@suse.de
+
+- Mark some strings for translation (#219783 need it)
+- r4682
+
+-------------------------------------------------------------------
+Mon Nov 27 16:17:07 CET 2006 - mt@suse.de
+
+- Added translations marks to hal, url and mutex exceptions (#23771)
+- revision 4680
+
+-------------------------------------------------------------------
+Wed Nov 22 19:24:57 CET 2006 - dmacvicar@suse.de
+
+- don't make libzypp-devel depend on sqlite-devel as
+  headers from zypp2 are not installed yet
+- r4663
+
+-------------------------------------------------------------------
+Wed Nov 22 15:23:55 CET 2006 - ma@suse.de
+
+- Return an error if fork failed. (#204807)
+- Make Script execution abortable by user request. (#212949, F100233)
+- revision 4660
+- version 2.8.6
+
+-------------------------------------------------------------------
+Tue Nov 21 16:25:03 CET 2006 - mvidner@suse.cz
+
+- Added package-manager wrapper script, with icon and .desktop (#222757).
+  (Used by gnome-main-menu)
+- version 2.8.5
+- r4640
+
+-------------------------------------------------------------------
+Tue Nov 21 13:11:06 CET 2006 - mt@suse.de
+
+- Fixed target/hal - one more dbus_connection_close found (#216035)
+- revision 4636
+- version 2.8.4
+
+-------------------------------------------------------------------
+Mon Nov 20 14:15:38 CET 2006 - schubi@suse.de
+
+- translation added
+  rev 4630
+  version 2.8.3
+
+-------------------------------------------------------------------
+Mon Nov 20 11:02:04 CET 2006 - ma@suse.de
+
+- Process obsoletes when installing non-package objects. (#217352)
+- revision 4621
+- version 2.8.2
+
+-------------------------------------------------------------------
+Fri Nov 17 16:57:39 CET 2006 - mt@suse.de
+
+- Implemented volume device check using /sbin/vol_id (#213852).
+- Revision 4619
+
+-------------------------------------------------------------------
+Fri Nov 17 15:53:58 CET 2006 - schubi@suse.de
+
+- new translation added
+
+-------------------------------------------------------------------
+Fri Nov 17 12:42:04 CET 2006 - mt@suse.de
+
+- Implemented an reuse of already existing foreign CD/DVD mount points
+  (e.g. automounted) - depends on REUSE_FOREIGN_MOUNTS flag (#220206).
+- Added a fallback check of the volume.mount_point HAL property to
+  isAutoMountedMedia(); info.hal_mount.created_mount_point seems
+  to be not avaliable in newer HAL versions (on 10.2).
+- Revision 4615
+- Version 2.8.1
+
+-------------------------------------------------------------------
+Thu Nov 16 23:39:15 CET 2006 - ma@suse.de
+
+- removed unused methods from Patch and PatchImplIf. Provided
+  reasonable default implementation for Patch::interactive.
+  (#221476).
+- revision 4610
+- Version 2.8.0
+
+-------------------------------------------------------------------
+Thu Nov 16 15:32:04 CET 2006 - dmacvicar@suse.de
+
+- Handle media eject failures (#216545)
+- r4606
+
+-------------------------------------------------------------------
+Wed Nov 15 22:41:51 CET 2006 - ma@suse.de
+
+- Reimplemented RpmDb::checkPackage using librpm API instead
+  of parsing "rpm --checksig" output. (#163202)
+- Version 2.7.4
+- revision 4600
+
+-------------------------------------------------------------------
+Wed Nov 15 15:39:26 CET 2006 - schubi@suse.de
+
+- NEW behaviour of the solver:
+  Obsolete virtual provides. E.G.:
+
+  Installed:
+  -----------
+  Name:           test-1.0-0
+
+  Name:           moretest-1.0-0
+  Provides:       test
+
+  To be installed
+  ------------------
+  Name:           nomoretest-1.0-0
+  Obsoletes:      test
+
+  Result
+  --------
+  test-1.0-0 AND  moretest-1.0-0 will be deleted. In former versions only
+  test-1.0-0 had been deleted. Bug 220999
+
+- Translations added
+
+Version 2.7.3
+rev 4593
+
+-------------------------------------------------------------------
+Tue Nov 14 16:32:49 CET 2006 - schubi@suse.de
+
+- Bugfix in generating solver testcases:
+   - added kind of capabilities in description file
+   - removed unneded channel from uninstall in command file
+
+-------------------------------------------------------------------
+Mon Nov 13 18:35:01 CET 2006 - mt@suse.de
+
+- Try to call /bin/eject utility if the eject-ioctl fails.
+- r4568
+
+-------------------------------------------------------------------
+Mon Nov 13 17:00:51 CET 2006 - dmacvicar@suse.de
+
+- ignore empty capabilities
+- r4565
+
+-------------------------------------------------------------------
+Fri Nov 10 17:52:10 CET 2006 - dmacvicar@suse.de
+
+- make progress strings translatable (#219783)
+- r4556
+
+-------------------------------------------------------------------
+Tue Nov  7 16:40:28 CET 2006 - schubi@suse.de
+
+-  While deleting a selection all concerning recommended
+   packages will be deleted too.
+   BUT those packages should not be deleted which have been
+   set to KEEP by the user. bug 217574
+   rev 4526
+   VERSION: 2.7.2
+
+-------------------------------------------------------------------
+Tue Nov  7 13:12:49 CET 2006 - schubi@suse.de
+
+- Makefile in zypp2 fixed
+  Revision 4520
+
+-------------------------------------------------------------------
+Tue Nov  7 11:38:10 CET 2006 - schubi@suse.de
+
+- Translations added
+  Revision 4514
+  VERSION: 2.7.1
+
+-------------------------------------------------------------------
+Mon Nov  6 12:32:22 CET 2006 - dmacvicar@suse.de
+
+- Make the parser more strict, rejecting broken sources
+  but showing the error line.
+  Last fix making the parser relax would break multitag
+  descriptions with empty lines, now we check dependencies
+  at a higher level. (reference #160607)
+- r4501
+
+-------------------------------------------------------------------
+Fri Nov  3 11:04:19 CET 2006 - schubi@suse.de
+
+- New problem solution added: Unlock ALL resovables in order to speed up
+  problem solution. Bug 206453
+
+-------------------------------------------------------------------
+Fri Nov  3 10:29:08 CET 2006 - schubi@suse.de
+
+- Translations added
+
+-------------------------------------------------------------------
+Thu Nov  2 17:15:19 CET 2006 - mt@suse.de
+
+- Fixed target/hal - removed dbus_connection_close calls, because
+  the connections are shared; unref the ref-counted handle only.
+  (#216035)
+- r4442
+
+-------------------------------------------------------------------
+Thu Nov  2 16:18:27 CET 2006 - dmacvicar@suse.de
+
+- skipping unreachable packages won't work
+  (#215445)
+- r4468
+
+-------------------------------------------------------------------
+Wed Oct 25 19:04:51 CEST 2006 - dmacvicar@suse.de
+
+- (#213793) Target store fails to recreate stored install-time (other
+  Date and ByteCount values as well)
+
+- skip comments and blank lines in multilists
+  fixes (#214877) - zen-updater is not installed by default
+
+- Automatically fix broken products when reading
+  the product database.
+  still pending: honour the read-only flag
+  Changes to make this possible include
+  using read_dir instead of boost directory
+  iterator.
+
+-------------------------------------------------------------------
+Wed Oct 25 17:09:06 CEST 2006 - schubi@suse.de
+
+- added new class for generating solver testcases:
+  Testcase
+
+-------------------------------------------------------------------
+Wed Oct 25 15:40:44 CEST 2006 - ma@suse.de
+
+- Finalized ui::PatternContents. (F301229)
+- version 2.7.0
+- revision 4413
+
+-------------------------------------------------------------------
+Wed Oct 25 08:21:52 CEST 2006 - mvidner@suse.cz
+
+- Moved zypper and zypp-checkpatches(-wrapper) to zypper.rpm
+- version 2.6.0
+
+-------------------------------------------------------------------
+Tue Oct 24 14:33:16 CEST 2006 - ma@suse.de
+
+- Added ui::PatternContents: Helper class that will compute a patterns
+  expanded install_packages set. (UI interface for F301229)
+- revision 4387
+
+-------------------------------------------------------------------
+Tue Oct 24 09:06:24 CEST 2006 - mvidner@suse.cz
+
+- removed the last reference to /usr/lib64 to fix the build
+
+-------------------------------------------------------------------
+Mon Oct 23 17:44:42 CEST 2006 - mvidner@suse.cz
+
+- added "zypper info" (jkupec)
+- version 2.5.2
+
+-------------------------------------------------------------------
+Mon Oct 23 11:28:01 CEST 2006 - dmacvicar@suse.de
+
+- fix rpm db timestamp
+- add extra urls and optional urls to product API
+- r4378
+
+-------------------------------------------------------------------
+Fri Oct 20 16:55:44 CEST 2006 - mvidner@suse.cz
+
+- zypper: nicer progress reports, with or without --verbose.
+
+-------------------------------------------------------------------
+Fri Oct 20 16:25:50 CEST 2006 - ma@suse.de
+
+- Fixed reloading of target data after commit. Broken since
+  rev 3880.
+- version 2.5.1
+- revision 4365
+
+-------------------------------------------------------------------
+Fri Oct 20 16:22:09 CEST 2006 - dmacvicar@suse.de
+
+- put query-pool in /usr/lib/zypp and not lib64
+- r4363
+
+-------------------------------------------------------------------
+Fri Oct 20 12:16:06 CEST 2006 - dmacvicar@suse.de
+
+- revert keyring changes, causes endless loop (obvious)
+- add dist-product information, adapt store
+- version 2.5.0
+- r4355
+
+-------------------------------------------------------------------
+Fri Oct 20 11:04:26 CEST 2006 - ma@suse.de
+
+- zypp-query-pool: For products show additionally distributionName
+  and distributionEdition. (required by #205392)
+- revision 4349
+
+-------------------------------------------------------------------
+Fri Oct 20 00:13:49 CEST 2006 - ma@suse.de
+
+- Add accessor for Product distributionName and distributionEdition.
+  (required by #205392)
+- revision 4347
+
+-------------------------------------------------------------------
+Thu Oct 19 18:40:36 CEST 2006 - dmacvicar@suse.de
+
+- added test case for KeyRing
+- fire trustedKeyAdded in all calls to import trusted key
+  not only in signature check workflow.
+- r4342
+
+-------------------------------------------------------------------
+Thu Oct 19 18:27:02 CEST 2006 - mvidner@suse.cz
+
+- zypper update: implemented minimal version (patches only)
+- zypper list-updates: changed default type from package to patch,
+  consider patches affecting the package manager separately
+
+-------------------------------------------------------------------
+Thu Oct 19 14:09:33 CEST 2006 - dmacvicar@suse.de
+
+- version 2.4.1
+- r4338
+
+-------------------------------------------------------------------
+Thu Oct 19 14:05:18 CEST 2006 - dmacvicar@suse.de
+
+- add zypp-query-pool, so registration doesn't depend on
+  libzypp-zmd-backend being installed. Will remove from
+  backend when suseregister gets updated.
+
+-------------------------------------------------------------------
+Thu Oct 19 12:31:08 CEST 2006 - schwab@suse.de
+
+- Make sure config.rpath is present.
+
+-------------------------------------------------------------------
+Wed Oct 18 21:40:25 CEST 2006 - mvidner@suse.cz
+
+- zypper search: fixed uninitialized members that made all searches
+  exact and case sensitve
+
+-------------------------------------------------------------------
+Wed Oct 18 13:13:16 CEST 2006 - dmacvicar@suse.de
+
+- zypp-checkpatches, write in the right file
+- r4328
+
+-------------------------------------------------------------------
+Wed Oct 18 12:45:49 CEST 2006 - schubi@suse.de
+
+- Install resolvables although they are unneeded
+  if they have NOT the kind patch/atoms
+  Bug 210538 - freshens/supplements does not work with patterns
+- r4326
+
+-------------------------------------------------------------------
+Tue Oct 17 19:17:39 CEST 2006 - mvidner@suse.cz
+
+- zypper service-add -r http://example.org/foo.repo (F#300641).
+
+-------------------------------------------------------------------
+Tue Oct 17 18:29:34 CEST 2006 - dmacvicar@suse.de
+
+- zypp-checkpatches:
+  save version of the generated xml to
+  regenerate it if it changes.
+  save a random token in case of error to
+  force recreating xml file
+- r4321
+
+-------------------------------------------------------------------
+Tue Oct 17 13:49:53 CEST 2006 - dmacvicar@suse.de
+
+- remove permissions for zypp checkpatches from spec
+- r4318
+
+-------------------------------------------------------------------
+Tue Oct 17 12:36:57 CEST 2006 - dmacvicar@suse.de
+
+- registration fails because of wrong product data
+  (#205392)
+  use DISTPRODUCT,DISTVERSION to create the product
+  resolvable. have this resolvable provide
+  PRODUCT = VERSION
+- r4312
+
+-------------------------------------------------------------------
+Mon Oct 16 18:05:56 CEST 2006 - mvidner@suse.cz
+
+- zypper service-add -r ./foo.repo (F#300641).
+
+-------------------------------------------------------------------
+Mon Oct 16 15:02:00 CEST 2006 - jkupec@suse.cz
+
+- zypper: added case-sensitive search, search in descriptions and
+  summaries, search by resolvable type, substring and word
+  matching, support for wildcards
+- Revision 4303
+
+-------------------------------------------------------------------
+Mon Oct 16 13:27:50 CEST 2006 - mvidner@suse.cz
+
+- Prevent the user from sending signals to zypp-checkpatches-wrapper
+  (#211286).
+
+-------------------------------------------------------------------
+Mon Oct 16 12:57:56 CEST 2006 - schubi@suse.de
+
+- Dont incomplete an uninstalled resolvable, even not when establishing.
+  Incomplete only makes sense for installed resolvables (when they have broken
+  deps), for patches (because they are needed) and for atoms (because they are
+  used during patch calculation)
+  Bug 198379
+
+- Do not branch for packages with the same NVE but different architectures.
+  Take the best architecture.
+
+-------------------------------------------------------------------
+Fri Oct 13 16:51:43 CEST 2006 - dmacvicar@suse.de
+
+- implement rename source in sourcemanager
+- r4286
+
+-------------------------------------------------------------------
+Fri Oct 13 14:32:44 CEST 2006 - dmacvicar@suse.de
+
+- YaST sources: set alias to product summary if empty
+- show alias on logs.
+- r4281
+
+-------------------------------------------------------------------
+Thu Oct 12 16:40:29 CEST 2006 - dmacvicar@suse.de
+
+- version 2.4.0 (bin incompat due to callback fixes)
+- r4272
+
+-------------------------------------------------------------------
+Thu Oct 12 16:30:55 CEST 2006 - ma@suse.de
+
+- Provide additional solver status information to the UI.
+  (#162164,F301272)
+- Fixed UI satus computation in presence of multiple available
+  candidates.
+- revision 4264
+- version 2.3.1
+
+-------------------------------------------------------------------
+Thu Oct 12 15:45:12 CEST 2006 - dmacvicar@suse.de
+
+- remove const in MediaChangeReport requestMedia that
+  broke cd changing.
+- r4262
+
+-------------------------------------------------------------------
+Wed Oct 11 08:35:48 CEST 2006 - mvidner@suse.cz
+
+- zypper: added search (jkupec)
+- removed the suid bit from zypp-checkpatches-wrapper so that the
+  build passes until permissions.rpm is updated (~#211286).
+- r4253
+
+-------------------------------------------------------------------
+Tue Oct 10 16:03:40 CEST 2006 - dmacvicar@suse.de
+
+- Log microseconds if ZYPP_PROFILING env var is enabled.
+- r4252
+
+-------------------------------------------------------------------
+Mon Oct  9 18:15:19 CEST 2006 - dmacvicar@suse.de
+
+- YaST sources:
+  Factory cannot be set with 'refresh' enabled
+  (#204957)
+- get rid of some const bool signatures in Source classes
+- r4247
+
+-------------------------------------------------------------------
+Mon Oct  9 15:11:16 CEST 2006 - mvidner@suse.cz
+
+- Added zypp-checkpatches and a suid-root zypp-checkpatches-wrapper.
+
+-------------------------------------------------------------------
+Fri Oct  6 13:23:32 CEST 2006 - schubi@suse.de
+
+- select the best solution: prefering the total amount of install/update
+  packages BEFORE source preferences. Bug 208784
+
+-------------------------------------------------------------------
+Fri Oct  6 11:55:11 CEST 2006 - dmacvicar@suse.de
+
+- Introduce a method to see if a source supports a
+  kind of resolvable at that time, so we can
+  init a YUM source like factory but avoid parsing
+  it if it contains no patches.
+- add TODO
+- zypp-checkpatches xml output
+- r4235
+
+-------------------------------------------------------------------
+Mon Oct  2 16:34:03 CEST 2006 - mvidner@suse.cz
+
+- added a CLI preview: zypper
+- revision 4214
+- version 2.2.3
+
+-------------------------------------------------------------------
+Mon Oct  2 15:11:26 CEST 2006 - dmacvicar@suse.de
+
+- FATE #100165:
+  Make Content File Aware of Different Architectures
+  expand %a in release notes with architecture
+
+-------------------------------------------------------------------
+Fri Sep 29 16:10:46 CEST 2006 - ma@suse.de
+
+- Extended pattern parser to support includes/extends tags as hint
+  for the IO. (F301229)
+- revision 4199
+- version 2.2.2
+
+-------------------------------------------------------------------
+Fri Sep 29 00:31:33 CEST 2006 - ma@suse.de
+
+- Enabled sending of ScriptResolvableReport.
+- Changed ScriptResolvableReport::start to send local path
+  of script to be executed.
+- revision 4190
+- version 2.2.1
+
+-------------------------------------------------------------------
+Wed Sep 27 14:14:33 CEST 2006 - ma@suse.de
+
+- Added ScriptResolvableReport. Callbacks triggered on script
+  execution during commit. (F100233)
+- revision 4187
+- version 2.2.0
+
+-------------------------------------------------------------------
+Mon Sep 25 13:52:55 CEST 2006 - mvidner@suse.cz
+
+- fix: Url::getRegisteredSchemes() would always return nothing
+
+-------------------------------------------------------------------
+Fri Sep 22 15:34:11 CEST 2006 - jkupec@suse.cz
+
+- Made the build dependency on gettext-devel explicit
+
+-------------------------------------------------------------------
+Tue Sep 19 10:59:24 CEST 2006 - jsrain@suse.cz
+
+- adapted multi-media YUM sources according to official YUM
+  specification (F300743)
+
+-------------------------------------------------------------------
+Mon Sep 18 17:42:26 CEST 2006 - lslezak@suse.cz
+
+- use RPM_OPT_FLAGS (meissner@suse.de)
+
+-------------------------------------------------------------------
+Mon Sep 18 15:19:53 CEST 2006 - lslezak@suse.cz
+
+- SourceFactory::createFrom() - don't loose url,...
+- r4160
+
+-------------------------------------------------------------------
+Mon Sep 18 12:00:46 CEST 2006 - kkaempf@suse.de
+
+- reduce logging in ResolvableImpl.cc
+- rev 4157
+
+-------------------------------------------------------------------
+Thu Sep 14 15:59:47 CEST 2006 - schubi@suse.de
+
+- Replaced requirementIsMet by requirementIsInstalledOrUnneeded
+  in QueueItemInstall and QueueItemRequire
+  Bug 192535/204913
+  removed fix:Thu Sep  7 18:31:46 CEST 2006 - schubi@suse.de
+
+-------------------------------------------------------------------
+Thu Sep 14 12:44:53 CEST 2006 - lslezak@suse.cz
+
+- fixed SourceFactory::createFrom() - don't loose alias,
+  cachedir,... parameters
+
+-------------------------------------------------------------------
+Thu Sep 14 10:21:02 CEST 2006 - mvidner@suse.cz
+
+- Use RPM Enhances only if detected at configure time, to allow
+  compilation with older rpm.
+- callback params: use const string & instead of string (dmacvicar)
+
+-------------------------------------------------------------------
+Thu Sep  7 18:31:46 CEST 2006 - schubi@suse.de
+
+- Do not regarding SATISFIED (regarding UNNEEDED) in isPresent if it is
+  a package/script/message
+  Bug: 192535
+
+-------------------------------------------------------------------
+Thu Sep  7 16:19:36 CEST 2006 - dmacvicar@suse.de
+
+- add Source_Ref::checksum() which in combination with
+  timestamp can give an idea of a source change.
+- r4106
+
+-------------------------------------------------------------------
+Thu Sep  7 14:32:38 CEST 2006 - mvidner@suse.cz
+
+- Implemented fgzstreambuf::compressed_tell and fXstream::getbuf to
+  enable progress reporting on compressed streams.
+
+-------------------------------------------------------------------
+Wed Sep  6 18:31:20 CEST 2006 - dmacvicar@suse.de
+
+- better error propagation
+- r4096
+
+-------------------------------------------------------------------
+Tue Sep  5 19:22:56 CEST 2006 - mt@suse.de
+
+- Removed libblkid dependency - the workaround using libblkid to
+  check filesystem on XEN vbd mapped devices is obsolete, because
+  the info is avaliable via /dev/disk/by-label link now. (#197107)
+- revision 4087
+
+-------------------------------------------------------------------
+Thu Aug 31 15:16:11 CEST 2006 - ma@suse.de
+
+- PackageProvider: Fixed broken retry. (#202163)
+- revision 4071
+
+-------------------------------------------------------------------
+Wed Aug 30 23:50:55 CEST 2006 - ma@suse.de
+
+- Fixed RpmDb::makePackageFromHeader: Catch NULL Header passed as argument and refuse
+  to create a Package from a source package header.
+- Added method Pathname::extension: Return all of the characters in name
+  after and including the last dot in the last element of name.
+- PlaindirImpl: Disable rpm signature verification when scaning a directory for
+  rpms. Otherwise we'd need access to the rpm database to get the keys.
+- revision 4069
+
+-------------------------------------------------------------------
+Wed Aug 30 17:42:40 CEST 2006 - schubi@suse.de
+
+- Do not regarding SATISFIED/UNNEEDED in isPresent if it is
+  a package/script/message
+  Bug: 192535
+
+-------------------------------------------------------------------
+Wed Aug 30 14:29:45 CEST 2006 - ma@suse.de
+
+- Speedup computation of number of rpm database entries.
+- revision 4058
+
+-------------------------------------------------------------------
+Tue Aug 29 16:58:20 CEST 2006 - schubi@suse.de
+
+- Fixed endless loop in transactResObject
+  Bug 198095 - YaST2 Installaler crashes when selecting Gnome pattern to a KDE installation
+
+-------------------------------------------------------------------
+Tue Aug 29 12:05:09 CEST 2006 - dmacvicar@suse.de
+
+- fix some testcases for tar file parser changes
+- r4045
+
+-------------------------------------------------------------------
+Tue Aug 29 11:19:53 CEST 2006 - dmacvicar@suse.de
+
+- missing includes
+- clean old callbacks
+- r4041
+
+-------------------------------------------------------------------
+Fri Aug 25 14:32:07 CEST 2006 - schubi@suse.de
+
+- zyppPattern->install_packages returns SUGGESTED package too.
+  Bug 201476
+  Revision 4036
+
+-------------------------------------------------------------------
+Fri Aug 25 13:05:33 CEST 2006 - schubi@suse.de
+
+- New behaviour in the solver: try with 'best' package first, try with 'all'
+  packages if this fails.
+  Bug :Bug 191983
+
+-------------------------------------------------------------------
+Fri Aug 25 11:35:48 CEST 2006 - dmacvicar@suse.de
+
+- libzypp 2.1.0
+- bump version due to incompatible callback changes in KeyRing
+  Sources
+
+-------------------------------------------------------------------
+Thu Aug 24 15:34:45 CEST 2006 - dmacvicar@suse.de
+
+- new keyring callbacks
+- separate trust key from import key
+- use PublicKey class instead of params, to be able
+  to add more info like photos later (pending #181682)
+- update zmart with those callbacks.
+- better error handling (Exception types)
+- make tmp file names more readable depending on the context
+- r4026
+
+-------------------------------------------------------------------
+Tue Aug 22 22:35:30 CEST 2006 - dmacvicar@suse.de
+
+- decouple probing from source creation, using the new
+  media test for existence functions.
+- r4019
+
+
+-------------------------------------------------------------------
+Tue Aug 22 17:56:53 CEST 2006 - mt@suse.de
+
+- Fixed getDoesFileExist to reset the transfer range
+- Added logging of curl debug messages to the zypp log.
+  The env var ZYPP_MEDIA_CURL_DEBUG=1 logs curl infos,
+  ZYPP_MEDIA_CURL_DEBUG=2 logs the in/out headers.
+- r4018
+
+-------------------------------------------------------------------
+Fri Aug 18 14:57:35 CEST 2006 - kkaempf@suse.de
+
+- remove the /etc/sysconfig/zypp:REWRITE_KERNEL_DEPS = yes check;
+  see rev 3810 below. (#190163)
+- rev 3998
+
+-------------------------------------------------------------------
+Thu Aug 17 18:15:14 CEST 2006 - dmacvicar@suse.de
+
+- fix uninstalling of atoms (noop)
+- r3995
+
+-------------------------------------------------------------------
+Wed Aug 16 17:41:40 CEST 2006 - dmacvicar@suse.de
+
+- Implement initial verson of Media
+  doesFileExist, for future source probing.
+- r3984
+
+-------------------------------------------------------------------
+Tue Aug 15 12:01:31 CEST 2006 - dmacvicar@suse.de
+
+- more dbus_connection_close fixes
+- r3974
+
+-------------------------------------------------------------------
+Tue Aug 15 11:41:41 CEST 2006 - kkaempf@suse.de
+
+- clean up 'incomplete' handling in QueueItemEstablish.
+- rev 3973.
+
+-------------------------------------------------------------------
+Tue Aug 15 11:30:50 CEST 2006 - kkaempf@suse.de
+
+- Don't set 'incomplete' for uninstalled patterns or products.
+  (#198379)
+- rev 3970.
+
+-------------------------------------------------------------------
+Mon Aug 14 16:50:53 CEST 2006 - schubi@suse.de
+
+- Added new API calls:
+  setAdditionalProvide
+  setAdditionalConflict
+  setAdditionalRequire
+
+-------------------------------------------------------------------
+Mon Aug 14 11:26:20 CEST 2006 - dmacvicar@suse.de
+
+- don't link examples to testsuite library.
+
+-------------------------------------------------------------------
+Sat Aug 12 17:30:30 CEST 2006 - schwab@suse.de
+
+- Disable profiling to work around compiler bug.
+
+-------------------------------------------------------------------
+Fri Aug 11 17:01:33 CEST 2006 - dmacvicar@suse.de
+
+- forward port 3924:3939
+- Add explicit finish callbacks for subtasks during ProvidePackage
+  to avoid UI confusion.
+- rev3957
+
+-------------------------------------------------------------------
+Fri Aug 11 11:48:44 CEST 2006 - dmacvicar@suse.de
+
+- Introduce examples/
+- fix some svn ignores
+- fix compilation. Use: dbus_connection_close
+- rev 3943
+
+-------------------------------------------------------------------
+Thu Aug 10 16:32:36 CEST 2006 - dmacvicar@suse.de
+
+- Initial support for plain directory with rpms as source
+- r3935
+
+-------------------------------------------------------------------
+Tue Aug  8 16:51:45 CEST 2006 - dmacvicar@suse.de
+
+- Move the target query by kind function to
+  a iterator, so we dont make a copy of the restore
+  the iterator works loading by demand too
+
+-------------------------------------------------------------------
+Mon Aug  7 17:57:26 CEST 2006 - dmacvicar@suse.de
+
+- rename the new initTarget to initializeTarget,
+  leave the old one as is, but deprecate it.
+- r3903
+
+-------------------------------------------------------------------
+Mon Aug  7 15:10:08 CEST 2006 - dmacvicar@suse.de
+
+- forward port from SLES10 branch , till 3888
+- Prefer to use available DeltaRpm or PatchRpm instead of downloading
+  full packages. (#168844)
+- rpmdb : Do not use the deprecated POSIX API, but boost::regex
+- version 2.0.0
+- rev 3893
+
+-------------------------------------------------------------------
+Fri Aug  4 15:20:13 CEST 2006 - dmacvicar@suse.de
+
+- Separate target init from adding resolvables, getting rid of the
+uggly bool flag.
+- dont clear the store each time Target::resolvables is called
+- Load target resolvables on demand by kind, keep them cached later
+- add Target::resolvablesByKind(kind) to allow query specific kind
+  without reading all kinds. Used to port TargetProduct, which
+  was reading the whole rpm database only to displayy base product
+  name in YaST help.
+- commit to pkg-bindings and packager will follow.
+- jsrain will port more yast stuff, especially inst_source which
+  startup time should by reduced by half afterwards.
+- rev 3880
+
+-------------------------------------------------------------------
+Tue Aug  1 13:37:29 CEST 2006 - dmacvicar@suse.de
+
+- forward port:
+  rev 3786 fix to stalle tmpfiles broke patches.
+  SLES was released with this broken. 10.1 has a blocked
+  zypp update because this.
+  Attempt to fix this. (#192535)
+- fix configure.ac sqlite-source build path
+- rev 3858
+
+-------------------------------------------------------------------
+Fri Jul 21 10:58:58 CEST 2006 - dmacvicar@suse.de
+
+- link correctly
+
+-------------------------------------------------------------------
+Wed Jul 19 13:50:57 CEST 2006 - dmacvicar@suse.de
+
+- dont link sqlite in the main lib.
+- rev 3826
+
+-------------------------------------------------------------------
+Tue Jul 18 17:42:45 CEST 2006 - dmacvicar@suse.de
+
+- susetags: parse product parser regexp only once
+- parse yum factory 5 sec. (from 30) faster using
+  another string find algorithm
+- rev 3824
+
+-------------------------------------------------------------------
+Tue Jul 18 12:56:17 CEST 2006 - dmacvicar@suse.de
+
+- Digest: Don't read the stream character wise but reading blocks,
+  as advised by matz profiling.
+- r3819
+
+-------------------------------------------------------------------
+Mon Jul 17 12:54:39 CEST 2006 - ma@suse.de
+
+- Add "openSUSE", "ATI Technologies Inc." and "Nvidia" to
+  trusted vendors. (#189573)
+- revision 3804
+
+-------------------------------------------------------------------
+Thu Jul 13 12:52:58 CEST 2006 - dmacvicar@suse.de
+
+- backport fix for stalle tmpfile (#191311)
+- rev 3788
+
+-------------------------------------------------------------------
+Wed Jun 28 13:22:22 CEST 2006 - mt@suse.de
+
+- deactivated media manager code that was disabling the
+  automounter (#172419)
+- rev 3724
+
+-------------------------------------------------------------------
+Mon Jun 26 17:14:53 CEST 2006 - dmacvicar@suse.de
+
+- fix autorefresh (#186115)
+- revision 3708
+-------------------------------------------------------------------
+Fri Jun 23 13:41:18 CEST 2006 - ma@suse.de
+
+- forward port from SLE branch
+- Set default permission for logfiles to 0640. (#187044)
+- revision 3696
+
+-------------------------------------------------------------------
+Thu Jun 22 16:01:15 CEST 2006 - ma@suse.de
+
+- forward port from SLE branch
+- Fixed installation of SP or Add-On product switching to media 2
+  too early. (#186607)
+- revision 3691
+
+-------------------------------------------------------------------
+Wed Jun 21 15:47:39 CEST 2006 - dmacvicar@suse.de
+
+- forward port from SLE branch
+- Strip self provides without edition in Resolvable ctor.
+  (#186079)
+- Source::provideResolvables not implemented in yum source type.
+  Product not set for packages that are available from update source
+  (#186920)
+- Hook modalias() supplements without package to "kernel" (#184840)
+- Allow on-demand SourceManager::restore() (#186678)
+- Hook modalias() supplements without package to "kernel" (#184840)
+- rev 3676
+
+-------------------------------------------------------------------
+Mon Jun 19 15:17:17 CEST 2006 - mt@suse.de
+
+- Fix adding resolving of path names for mount points (#181606)
+- rev 3658
+
+-------------------------------------------------------------------
+Mon Jun 19 13:52:14 CEST 2006 - dmacvicar@suse.de
+
+- merge download algorithm and refactoring from branch
+  (#181204)
+
+-------------------------------------------------------------------
+Thu Jun 15 17:53:40 CEST 2006 - mvidner@suse.cz
+
+- autodocs: use find+xargs to overcome command length limit,
+  do not call doxygen unnecessarily (#185334).
+- rev 3645
+
+-------------------------------------------------------------------
+Thu Jun 15 07:26:29 CEST 2006 - kkaempf@suse.de
+
+- Dont use getZYpp in static constructor (#185198)
+  Bugfix #178292 was wrong.
+- Only warn on incompleting installed resolvables (#185197)
+- rev 3644
+
+-------------------------------------------------------------------
+Wed Jun 14 22:43:40 CEST 2006 - kkaempf@suse.de
+
+- Atoms might only be installed via patches (#184714)
+- rev 3642
+
+-------------------------------------------------------------------
+Wed Jun 14 17:26:39 CEST 2006 - dmacvicar@suse.de
+
+- fix for the last stall tmpfile (#178292)
+- r3637
+
+-------------------------------------------------------------------
+Wed Jun 14 12:06:57 CEST 2006 - mt@suse.de
+
+- Implemented transfer timeout inside of the progress callback.
+  The timeout value can be set using timeout url parameter, the
+  default transfer timeout is 180 seconds. (#181602)
+- Added ssl_verify and ssl_capath url options used in https scheme,
+  allowing to change or disable the ssl verify options. (#171622)
+- Added fallback on read failures of /etc/mtab to /proc/mounts.
+  Improved verbosity in mount and mount check related failure cases,
+  incl. /etc/mtab dump. (#181606)
+- rev 3623
+
+-------------------------------------------------------------------
+Wed Jun 14 10:49:10 CEST 2006 - kkaempf@suse.de
+
+- combine knownAliases and knownUrls in a single function.
+- rev 3616
+
+-------------------------------------------------------------------
+Mon Jun 12 16:27:46 CEST 2006 - kkaempf@suse.de
+
+- honor parallel installs in resolver context (#181103)
+- rev 3592
+
+-------------------------------------------------------------------
+Mon Jun 12 15:37:10 CEST 2006 - dmacvicar@suse.de
+
+- right fix for tmpdir initialized in static constructor
+- catch around provideJustFile in providePackage
+-rev 3654
+
+-------------------------------------------------------------------
+Mon Jun 12 15:15:07 CEST 2006 - dmacvicar@suse.de
+
+- fix #182003 YUM packages without size
+- rev 3587
+
+-------------------------------------------------------------------
+Mon Jun 12 14:58:55 CEST 2006 - kkaempf@suse.de
+
+- Allow to restore and remove by Url
+- rev 3583
+
+-------------------------------------------------------------------
+Mon Jun 12 13:07:31 CEST 2006 - kkaempf@suse.de
+
+- make atoms parallel installable (#181103)
+- rev 3580
+
+-------------------------------------------------------------------
+Fri Jun  9 16:28:11 CEST 2006 - dmacvicar@suse.de
+
+- Allow to restore by alias
+- r3568
+
+-------------------------------------------------------------------
+Fri Jun  9 13:06:16 CEST 2006 - mvidner@suse.cz
+
+- Do not fork in a global destructor, perl dislikes it (#182672).
+  Fixes hanging ag_ldapserver and yast2-perl-bindings tests.
+
+-------------------------------------------------------------------
+Thu Jun  8 16:24:55 CEST 2006 - ma@suse.de
+
+- Installation: Assert product information is stored to libzypp
+  database before reboot. (#181198)
+- Version 1.2.0; revision 3553
+
+-------------------------------------------------------------------
+Wed Jun  7 13:55:23 CEST 2006 - visnov@suse.cz
+
+- Synchronize keys with rpm database before
+  closing access to it (#182338)
+- rev 3533
+
+-------------------------------------------------------------------
+Wed Jun  7 11:40:46 CEST 2006 - mt@suse.de
+
+- Changed to just prefer DVD drives in "dvd:" scheme, instead of
+  filter out the non-DVD drives completely. Allows a fallback to
+  drives without the dvd HAL property e.g. in VMWare. (#177457)
+- rev 3530
+
+-------------------------------------------------------------------
+Wed Jun  7 01:00:06 CEST 2006 - dmacvicar@suse.de
+
+- Merge fix for stalle tmpdir due to cyclic references, using a master
+ TmpDir for zypp. (#178292) . There is still 1 tmpdir to fix.
+- rev 3521
+
+-------------------------------------------------------------------
+Wed Jun  7 01:00:05 CEST 2006 - dmacvicar@suse.de
+
+- Fixes unneeded file download, and add download callbacks
+  (still need yast side) #181204 and #160206
+- Fix stalle tmpdir due to cyclic references, using a master
+  TmpDir for zypp. # 178292
+
+-------------------------------------------------------------------
+Wed Jun  7 00:02:18 CEST 2006 - ma@suse.de
+
+- fixed memory leak in PersistentStorage (#168690)
+- revision 3519
+
+-------------------------------------------------------------------
+Tue Jun  6 22:24:00 CEST 2006 - ma@suse.de
+
+- fixed memory leak in XMLSourceCacheParser (#168690)
+- revision 3517
+
+-------------------------------------------------------------------
+Fri Jun  2 16:09:03 CEST 2006 - schubi@suse.de
+
+-latest fi translation added
+ Revision 3502
+
+-------------------------------------------------------------------
+Thu Jun  1 15:11:47 CEST 2006 - schubi@suse.de
+
+-All installed resolvables has been set to "satisfied" in
+ ResolverContext::unneeded . BUT:
+ Patch concerning resolvables have to be set to
+ "unneeded" although they are installed. In order
+ getting the state "no longer applicable" (Bug 171590)
+- rev 3496
+
+-------------------------------------------------------------------
+Thu Jun  1 14:54:02 CEST 2006 - kkaempf@suse.de
+
+- compute status for scripts and messages so their freshens get
+  properly honored (aj with postgresql-server)
+- rev 3494
+
+-------------------------------------------------------------------
+Thu Jun  1 13:57:48 CEST 2006 - dmacvicar@suse.de
+
+- revert not-used-yet rpmdb timestamp, as
+  it broke rpmdb::init(). (#180040)
+- rev 3490
+
+-------------------------------------------------------------------
+Thu Jun  1 11:03:34 CEST 2006 - schubi@suse.de
+
+- updating gmo files, if po files has been changed; bug 164449
+
+-------------------------------------------------------------------
+Wed May 31 18:32:58 CEST 2006 - dmacvicar@suse.de
+
+- Dont download twice if starting from 1st time
+- fix typo
+- rev 3481
+
+-------------------------------------------------------------------
+Wed May 31 17:06:48 CEST 2006 - dmacvicar@suse.de
+
+- set cache dir only if storeMetadata is called as a public method.´
+- rev 3475
+
+-------------------------------------------------------------------
+Wed May 31 15:27:38 CEST 2006 - kkaempf@suse.de
+
+- schedule a package for installation if
+  - it freshens / supplements something
+  - it is not installed yet
+  (#178721)
+- rev 3473
+
+-------------------------------------------------------------------
+Wed May 31 15:22:43 CEST 2006 - dmacvicar@suse.de
+
+- make susetags also implement download and check first.
+- rev 3470
+
+-------------------------------------------------------------------
+Tue May 30 12:43:47 CEST 2006 - dmacvicar@suse.de
+
+- make yum more robust. Never parse from provideFile
+  but only from local disk. Make sure the cache
+  is consistent before recreating it.
+  the code is easier to follow and probably
+  faster. checksum and signatures are
+  checked on caching not on parsing.
+  Required to implement refresh for #154990
+- rev 3452
+
+-------------------------------------------------------------------
+Wed May 24 16:57:54 CEST 2006 - dmacvicar@suse.de
+
+- implement timestamp for YUM and SuseTags
+- actually use the license to confirm in yum patches
+- add prerequires tag in yum optonally to the bad designed
+  and nonintuitive pre=1
+- rev 3448
+
+-------------------------------------------------------------------
+Wed May 24 15:30:32 CEST 2006 - dmacvicar@suse.de
+
+- dont pass root on init but before.
+- implement rpm db modification timestamp
+  not used yet
+- move Helix source to testsuite out of the solver
+  so we can use it for target, storage tests
+- add Source_Ref::timestamp(), default to now()
+  in order to implement smart sync of sources by zmd
+- don't parse desc and summary twice
+- fix a segfault with tranlated text
+- fix broken size tag introduced in rev 3427
+- rev 3446
+
+-------------------------------------------------------------------
+Tue May 23 20:53:27 CEST 2006 - dmacvicar@suse.de
+
+- dont accept corrupt sources, improve logs
+
+-------------------------------------------------------------------
+Tue May 23 17:26:41 CEST 2006 - ma@suse.de
+
+- Added PoolItem_Ref::statusReset. Resets the PoolItem status without
+  loosing autoprotection (eg. for foreign vendor). (assists #177469)
+- rev 3431
+
+-------------------------------------------------------------------
+Tue May 23 17:04:04 CEST 2006 - jsrain@suse.cz
+
+- added mediaNr() to PatchRpm and DeltaRpm classes
+- rev 3430
+
+-------------------------------------------------------------------
+Tue May 23 15:58:04 CEST 2006 - dmacvicar@suse.de
+
+- enable YUM license to confirm.
+  needed for #174476
+- adapt store to serialize and read all new resobject fields
+- use install-time to now() when serializing (#174653)
+- rev 3427
+
+-------------------------------------------------------------------
+Mon May 22 20:51:59 CEST 2006 - ma@suse.de
+
+- Do not violate install order when restricting commit to a certain
+  mediaNumber. (#170079)
+- Version 1.1.0; rev 3423
+
+-------------------------------------------------------------------
+Mon May 22 19:03:20 CEST 2006 - mvidner@suse.cz
+
+- Added SourceManager::findSourceByUrl to overcome alias mismatches
+  (#177543).
+- rev 3420
+
+-------------------------------------------------------------------
+Mon May 22 17:19:25 CEST 2006 - ma@suse.de
+
+- Order all objects according to prerequirements, not just packages.
+  (#173690)
+- rev 3419
+
+-------------------------------------------------------------------
+Mon May 22 15:57:43 CEST 2006 - schubi@suse.de
+
+- Added new translation
+
+-------------------------------------------------------------------
+Fri May 19 12:53:44 CEST 2006 - dmacvicar@suse.de
+
+- fix missing homedir option for gpg (#171055)
+- rev 3415
+
+-------------------------------------------------------------------
+Thu May 18 19:08:52 CEST 2006 - ma@suse.de
+
+- Prevent against daemons launched in rpm %post, that do not close
+  their filedescriptors. (#174548)
+- Version 1.0.1; rev 3413
+
+-------------------------------------------------------------------
+Thu May 18 16:51:41 CEST 2006 - jsrain@suse.cz
+
+- fixed media number of package retrieved as a part of a patch
+  (#174841)
+- rev 3409
+
+-------------------------------------------------------------------
+Thu May 18 16:16:21 CEST 2006 - dmacvicar@suse.de
+
+- fix missing package descriptions due to filtered packages
+  by incompatible architectures. (#159109)
+- rev 3404
+
+-------------------------------------------------------------------
+Thu May 18 16:08:42 CEST 2006 - kkaempf@suse.de
+
+- decrease logging in DiskUsageCounter and Modalias (#163186)
+- rev 3406
+
+-------------------------------------------------------------------
+Thu May 18 10:43:47 CEST 2006 - ma@suse.de
+
+- Stay backward comapatible.
+
+-------------------------------------------------------------------
+Tue May 16 21:35:27 CEST 2006 - ma@suse.de
+
+- Make basic attributes available through ResObject.
+- Let ResObjects which do not require media access during
+  commit return ZERO sourceMediaNr (required for #173690)
+- Version 1.0.0
+- rev 3390
+
+-------------------------------------------------------------------
+Tue May 16 15:11:35 CEST 2006 - kkaempf@suse.de
+
+- reduce logging verbosity (#163186)
+- rev 3381
+
+-------------------------------------------------------------------
+Tue May 16 14:00:57 CEST 2006 - schubi@suse.de
+
+- setCandidate accept candidates with compatible architectures too. Not
+  only with the same architecture. Bug 172594 - If update package has
+  differet arch, UI display is wrong
+
+-------------------------------------------------------------------
+Tue May 16 09:30:45 CEST 2006 - mvidner@suse.cz
+
+- Added Source_Ref::resStoreInitialized.
+  If we know that noone has seen the resolvables yet, we can skip
+  them too, eg. when deleting a source. (#174840)
+- rev 3378
+
+-------------------------------------------------------------------
+Mon May 15 12:41:39 CEST 2006 - kkaempf@suse.de
+
+- Honor freshens as conditionals independant from the installed/
+  uninstalled status (#174797)
+- rev 3376
+
+-------------------------------------------------------------------
+Mon May 15 11:15:03 CEST 2006 - kkaempf@suse.de
+
+- State modifier "unneeded" is transitive for patches (#171590)
+- rev 3375
+
+-------------------------------------------------------------------
+Thu May 11 17:42:29 CEST 2006 - schubi@suse.de
+
+- Do not transact itself (update) in the transactResObject mechanism
+  Bug 174290
+
+-------------------------------------------------------------------
+Thu May 11 16:37:51 CEST 2006 - mt@suse.de
+
+- Reenabled improved large file support flags (unintentionally
+  removed in rev 1544). Fixes bug #173753.
+- Added large file support flags to libzypp.pc file allowing
+  consistence checks in the application using features variable
+- rev 3366
+
+-------------------------------------------------------------------
+Thu May 11 10:49:36 CEST 2006 - mvidner@suse.cz
+
+- SourceManager: moved source deletion before creation
+  so that we can recreate a deleted one (#174295)
+- removed dead code dealing with known_caches from SourceManager::store
+  (see r3195)
+- r3362
+
+-------------------------------------------------------------------
+Thu May 11 10:15:14 CEST 2006 - jsrain@suse.cz
+
+- fixed returning product short name and summary if product read
+  from target store (#148625)
+- rev 3360
+
+-------------------------------------------------------------------
+Wed May 10 11:39:06 CEST 2006 - jsrain@suse.cz
+
+- set media verifier on redirected medium (#172599)
+- rev 3359
+
+-------------------------------------------------------------------
+Mon May  8 17:28:42 CEST 2006 - kkaempf@suse.de
+
+- fix 'transactResKind' to collect best providers by capability
+  and to recursively transact items of same kind (#170114)
+- rev 3355
+
+-------------------------------------------------------------------
+Mon May  8 16:45:46 CEST 2006 - dmacvicar@suse.de
+
+- serialize the full URL to avoid missing password and other
+  url settings (#148108)
+- rev 3353
+
+-------------------------------------------------------------------
+Fri May  5 17:47:14 CEST 2006 - mt@suse.de
+
+- Added a 60 sec connect timeout to MediaCurl (#172860)
+- rev 3348
+
+-------------------------------------------------------------------
+Thu May  4 15:15:37 CEST 2006 - kkaempf@suse.de
+
+- re-fetch also .asc and .key files before checking signature
+  (#172597)
+- rev 3350 (3345-10.1)
+
+-------------------------------------------------------------------
+Wed May  3 17:40:45 CEST 2006 - dmacvicar@suse.de
+
+- Fix yum key verification, because a double variable declaration
+
+-------------------------------------------------------------------
+Wed May  3 15:34:00 CEST 2006 - dmacvicar@suse.de
+
+- use --no-default-keyring to avoid creating a
+  default gpg dir in / (#171055)
+- rev 3335
+
+-------------------------------------------------------------------
+Wed May  3 14:40:25 CEST 2006 - dmacvicar@suse.de
+
+- Fix YUM signature checking, we were passing the key instead of the
+  signature.
+- When the user trust a key, sync again. Bye to the session trusted
+  keys and user being asked all the time. (#171213)
+- r3332
+
+-------------------------------------------------------------------
+Wed May  3 14:22:02 CEST 2006 - ma@suse.de
+
+- Cleanup index tables when removing items from pool (#170564).
+
+-------------------------------------------------------------------
+Wed May  3 12:23:36 CEST 2006 - kkaempf@suse.de
+
+- backout rev 3246->3275 of TargetImpl.cc
+- add missing testsuite/utils/TestUtils.h
+- rev 3330
+
+-------------------------------------------------------------------
+Wed May  3 12:13:38 CEST 2006 - dmacvicar@suse.de
+
+- more fixes for #171062, there were some files still not being
+  read from cache.
+- r3327
+
+-------------------------------------------------------------------
+Tue May  2 18:41:44 CEST 2006 - dmacvicar@suse.de
+
+- cache keys and signature. Remove lot of duplicated code. (#171062)
+- r3320
+
+-------------------------------------------------------------------
+Tue May  2 18:02:46 CEST 2006 - mt@suse.de
+
+- Disabled isUseableAttachPoint check in MediaDIR -- we do not
+  mount here anything, so it is OK to use any dir (171351).
+- rev 3318
+
+-------------------------------------------------------------------
+Tue May  2 14:58:03 CEST 2006 - kkaempf@suse.de
+
+- parse "license-to-confirm" in primary.xml (#168437)
+- rev 3312
+
+-------------------------------------------------------------------
+Mon May  1 17:44:29 CEST 2006 - kkaempf@suse.de
+
+- Don't try to store 'Atom', not needed and the backend store
+  rejects them anyways (addtion to #168610)
+- rev 3306
+
+-------------------------------------------------------------------
+Mon May  1 04:43:01 CEST 2006 - mt@suse.de
+
+- Improved device check in MediaDISK using libblkid (Bug #158529)
+- Allow to provide sysfs path via $SYSFS_PATH in MediaCD.cc and
+  added a check if it is a directory
+- Added libcurl and libblkid checks to configure.ac
+- Added e2fsprogs(-devel) requires to the spec file
+- rev 3303
+
+-------------------------------------------------------------------
+Sat Apr 29 21:46:22 CEST 2006 - kkaempf@suse.de
+
+- dont download "other" during key check (#171041)
+- rev 3294
+
+-------------------------------------------------------------------
+Sat Apr 29 11:05:15 CEST 2006 - kkaempf@suse.de
+
+- allow parallel installs of atoms (used to fulfill patch require-
+  ments, atoms aren't installed anyways) (#170098)
+- some testsuite improvements.
+- rev 3288
+
+-------------------------------------------------------------------
+Fri Apr 28 20:15:06 CEST 2006 - dmacvicar@suse.de
+
+- get rid of autobuild check when throwing exceptions without throw
+  but with a macro, returning a null pointer at the end (never reached).
+
+-------------------------------------------------------------------
+Fri Apr 28 19:50:28 CEST 2006 - ma@suse.de
+
+- Do not violate install order when restricting commit to a certain
+  mediaNumber. (#170079)
+
+-------------------------------------------------------------------
+Fri Apr 28 18:12:26 CEST 2006 - dmacvicar@suse.de
+
+- Don't use throw directly!
+  use ZYPP_THROW with a Exception class, otherwise package bindings
+  will not catch them.
+  Should fix crashes when reading broken sources with yast.
+- rev 3272
+
+-------------------------------------------------------------------
+Fri Apr 28 15:29:32 CEST 2006 - schubi@suse.de
+
+- Bug 162064 - font packages are not installed for locale, e.g. khmer font not installed after CD1
+  revision 3269
+
+-------------------------------------------------------------------
+Fri Apr 28 14:55:04 CEST 2006 - kkaempf@suse.de
+
+- revert bugfix #168906 (fom rev 3219), it filters too many errors.
+- further improve on #168840 (from rev 3231), match on name-edition
+  when filtering by best arch. (#170098)
+- rev 3268
+
+-------------------------------------------------------------------
+Fri Apr 28 13:01:58 CEST 2006 - dmacvicar@suse.de
+
+- #170093 , lot of package descriptions missing
+- rev 3263
+
+-------------------------------------------------------------------
+Thu Apr 27 20:28:01 CEST 2006 - dmacvicar@suse.de
+
+- try to fix wrong permissions of /var/lib/zypp created
+  by old zypp, only when running as root they are fixed
+  (#169094)
+- YUM: Verify signatures on factoryInit.
+  Dont provide other.xml. Cleanups, better logging.
+  When refreshing signed soruces, don't refresh is source
+  has not changed.
+- YaST sources: don't refresh if media file has not changed.
+- Show full url of index files in sources for signature validation
+  (mentioned in #170139 comment #3)-
+
+-------------------------------------------------------------------
+Thu Apr 27 18:41:02 CEST 2006 - jsrain@suse.de
+
+- udpated media ID syntax for external scripts (to be consistent
+  with packages) (#170247)
+- rev 3256
+
+-------------------------------------------------------------------
+Thu Apr 27 18:33:48 CEST 2006 - jsrain@suse.de
+
+- set media ID to 1 if not specified in YUM metadata (#167452)
+- rev 3255
+
+-------------------------------------------------------------------
+Thu Apr 27 11:55:05 CEST 2006 - kkaempf@suse.de
+
+- If freshen and supplement are fulfilled, install any kind of
+  resolvable if not yet installed (#165746)
+- rev 3249
+
+-------------------------------------------------------------------
+Wed Apr 26 18:00:42 CEST 2006 - kkaempf@suse.de
+
+- make downloaded script executable. (#169191)
+- rev 3247
+
+-------------------------------------------------------------------
+Wed Apr 26 10:43:03 CEST 2006 - kkaempf@suse.de
+
+- Improve on last fix, compare only compatible archs.
+- rev 3233
+
+-------------------------------------------------------------------
+Tue Apr 25 19:36:13 CEST 2006 - kkaempf@suse.de
+
+- Only choose best arch of multiple package atoms with identical
+  name (#168840)
+- rev 3231
+
+-------------------------------------------------------------------
+Tue Apr 25 16:59:51 CEST 2006 - dmacvicar@suse.de
+
+- pass empty strings to UI as key properties if unknown key
+  (#169114)
+- rev 3228
+
+-------------------------------------------------------------------
+Tue Apr 25 16:47:35 CEST 2006 - kkaempf@suse.de
+
+- refrain from parsing 'other.xml' (#159316)
+- rev 3226
+
+-------------------------------------------------------------------
+Tue Apr 25 16:34:50 CEST 2006 - visnov@suse.cz
+
+- in source refresh, clean up the cache dir if fails
+- do not require repomd.xml.asc when creating a cache (#163765)
+- rev 3224
+
+-------------------------------------------------------------------
+Tue Apr 25 15:17:15 CEST 2006 - dmacvicar@suse.de
+
+- Check if a file exists before providing it, and just handling the
+  exception is not sufficient, because it can release the media.
+  it nows get all possible packages.X translations an then
+  it selects the candidate from the existing ones (#168654)
+- rev 3221
+
+-------------------------------------------------------------------
+Tue Apr 25 12:28:17 CEST 2006 - kkaempf@suse.de
+
+- dont report conflicts if item is neither installed
+  nor to-be-installed (#168906)
+- rev 3219
+
+-------------------------------------------------------------------
+Tue Apr 25 11:27:09 CEST 2006 - dmacvicar@suse.de
+
+- #168060 , propagate the file description or original
+  name to the UI and not the checked filename path,
+  which could be a tmp file.
+  Requires changes in pkg-manager, and probably zmd-helpers.
+- rev 3215
+
+-------------------------------------------------------------------
+Mon Apr 24 18:27:59 CEST 2006 - dmacvicar@suse.de
+
+- /var/lib/zypp/db/languages/* are empty files (##168355)
+- r3206
+
+-------------------------------------------------------------------
+Mon Apr 24 17:45:07 CEST 2006 - dmacvicar@suse.de
+
+- All resolvables must honor arch, so Arch_noarch in
+  target/store/XMLFilesBackend.cc is wrong (#160792)
+- Introduced code to honour shared package descriptions
+  (#159109)
+- r3204
+
+-------------------------------------------------------------------
+Mon Apr 24 16:43:48 CEST 2006 - jsrain@suse.de
+
+- replace '_' in YUM elements/attributes with '-' (#168762)
+- rev 3201
+
+-------------------------------------------------------------------
+Mon Apr 24 16:15:02 CEST 2006 - mt@suse.de
+
+- Fixed iseries workaround - interchanged variables for scsi
+  devices, added debug messages about the steps (#163971).
+- Added getenv NULL ptr check and verify of the $HOME dir's
+  and ~/.curlrc file's ownership (#163203).
+- rev 3199
+
+-------------------------------------------------------------------
+Mon Apr 24 15:44:18 CEST 2006 - ma@suse.de
+
+- Use filesystem::TmpDir to create unique and unused Source cache
+  directories. (#168051)
+
+-------------------------------------------------------------------
+Mon Apr 24 14:30:41 CEST 2006 - ma@suse.de
+
+- Enable signature checks per default. (#168525)
+
+-------------------------------------------------------------------
+Mon Apr 24 11:20:17 CEST 2006 - visnov@suse.cz
+
+- fix callbacks for providing a single file (#160206)
+
+-------------------------------------------------------------------
+Sun Apr 23 12:28:21 CEST 2006 - kkaempf@suse.de
+
+- If an installed package looses a dependency, the solver tries
+  to upgrade it. Limit the upgrade candidates to best arch, best
+  edition.
+- filter 'other' entries with incompatible arch in yum parser.
+- rev 3177
+
+-------------------------------------------------------------------
+Fri Apr 21 22:47:18 CEST 2006 - jsrain@suse.de
+
+- initialize the product category according to source (#168061)
+- rev 3172
+
+-------------------------------------------------------------------
+Fri Apr 21 18:32:00 CEST 2006 - mvidner@suse.cz
+
+- delete only one older version of a xml-store resolvable
+  (half-baked, but the previous attempt was charred)
+
+-------------------------------------------------------------------
+Fri Apr 21 17:49:18 CEST 2006 - jsrain@suse.de
+
+- moved license_to_confirm to primary.xml
+- rev 3170
+
+-------------------------------------------------------------------
+Fri Apr 21 16:36:14 CEST 2006 - ma@suse.de
+
+- Removed deprecated oldstyle commit methods.
+
+-------------------------------------------------------------------
+Fri Apr 21 15:58:14 CEST 2006 - mvidner@suse.cz
+
+- when installing a xml-store resolvable (all except package,
+  message, script), delete older versions (#160792).
+- read selection edition from the XML store
+- rev 3167
+
+-------------------------------------------------------------------
+Thu Apr 20 14:10:41 CEST 2006 - kkaempf@suse.de
+
+- properly clear transaction flag after successful commit
+  (see rev 3122, #164365, #167285)
+- rev 3157
+
+-------------------------------------------------------------------
+Thu Apr 20 13:14:25 CEST 2006 - kkaempf@suse.de
+
+- recursively soft-uninstall recommended package on real uninstall,
+  not on update (#167603)
+- rev 3155
+
+-------------------------------------------------------------------
+Thu Apr 20 13:01:28 CEST 2006 - dmacvicar@suse.de
+
+- fix #167605 (importing keys to rpm multiple times due to
+  wrong interpretation of rpm gpg versioning.
+- add support for reading the rpm keys, with full id and fingerprint
+- rev 3153
+
+-------------------------------------------------------------------
+Wed Apr 19 16:47:34 CEST 2006 - kkaempf@suse.de
+
+- don't add duplicate error infos to ResolverContext (#167309)
+- rev 3146
+
+-------------------------------------------------------------------
+Wed Apr 19 14:10:10 CEST 2006 - kkaempf@suse.de
+
+- fix Resolver::transactReset() (see rev 3122) (#167285)
+- rev 3140
+
+-------------------------------------------------------------------
+Wed Apr 19 13:48:26 CEST 2006 - ma@suse.de
+
+- Introduced $ZYPP_KEYRING_DEFAULT_ACCEPT_ALL. If this environment
+  variable is present, all signature checking callbacks will default
+  to 'accept', in case no recipient is present.
+
+-------------------------------------------------------------------
+Wed Apr 19 11:37:47 CEST 2006 - dmacvicar@suse.de
+
+- read content file on construction, and make
+  provideProduct only insert the already
+  read product object into the store (#165826)
+  (dmacvicar)
+- When the signature is not found, warn the
+  user about a unsigned source. When the
+  key is not found, do nothing, it can be in the
+  keyring already. (#166016) (dmacvicar)
+- enable key verification only if
+  ZYPP_CHECKSIG env var is set (dmacvicar)
+- r1529
+
+-------------------------------------------------------------------
+Tue Apr 18 20:41:48 CEST 2006 - kkaempf@suse.de
+
+- fix bugfix 164365, fix bug 167285
+  Actually clear the transcation state instead of locking it
+  to 'dont transact'
+- rev 3122
+
+-------------------------------------------------------------------
+Tue Apr 18 15:46:59 CEST 2006 - visnov@suse.cz
+
+- revert the signature/digest checking callbacks
+- rev 3115
+
+-------------------------------------------------------------------
+Tue Apr 18 15:14:35 CEST 2006 - kkaempf@suse.de
+
+- Bugfix #165670
+  - Honor keep requests.
+  - Dont flag "locked uninstall" as error if a keep request was
+    issued before.
+- rev 3114
+
+-------------------------------------------------------------------
+Tue Apr 18 12:04:35 CEST 2006 - kkaempf@suse.de
+
+- rule out locked items during distribution upgrade as early
+  as possible. (#165670)
+- rev 3110
+
+-------------------------------------------------------------------
+Mon Apr 17 16:04:35 CEST 2006 - kkaempf@suse.de
+
+- Bugfix #166212
+  - use APPL_LOW as 'ui initiated, by solver' in transactKind() and
+    transactResObject().
+  - resetTransaction(APPL_LOW) before resolving
+  - enhance transactCaps by using the same algorithm as in
+    QueueItemRequire
+    (before: transact all requires and recommends by name
+     now: transact best requires and recommends by provides)
+- rev 3107
+
+-------------------------------------------------------------------
+Fri Apr 14 16:04:35 CEST 2006 - visnov@suse.cz
+
+- new callbacks for failing digest
+- rev 3098
+
+-------------------------------------------------------------------
+Thu Apr 13 17:01:41 CEST 2006 - visnov@suse.cz
+
+- ask for file without a checksum (#165125)
+
+-------------------------------------------------------------------
+Thu Apr 13 16:59:38 CEST 2006 - kkaempf@suse.de
+
+- dont install satisfied resolvables (#165843)
+- rev 3095
+
+-------------------------------------------------------------------
+Thu Apr 13 16:00:21 CEST 2006 - kkaempf@suse.de
+
+- dont abort on failed "dry_run" (#164583)
+- rev 3091
+
+-------------------------------------------------------------------
+Thu Apr 13 15:19:20 CEST 2006 - visnov@suse.cz
+
+- Ask user if signature file does not exist (#163765)
+- handle repomd.xml.asc as optional file (#163765)
+- rev 3089
+
+-------------------------------------------------------------------
+Thu Apr 13 13:52:08 CEST 2006 - schubi@suse.de
+
+- Bug 164365 - build 906: Deselecting a selection, all packages are still selected
+- rev 3087
+
+-------------------------------------------------------------------
+Thu Apr 13 11:57:58 CEST 2006 - kkaempf@suse.de
+
+- Dont do transitive uninstalls on uninstalled or upgraded items.
+  (#165798)
+- rev 3083
+
+-------------------------------------------------------------------
+Wed Apr 12 17:57:21 CEST 2006 - ma@suse.de
+
+- Added 'rpmNoSignature' to ZYppCommitPolicy (#163862)
+
+-------------------------------------------------------------------
+Wed Apr 12 16:44:57 CEST 2006 - mvidner@suse.cz
+
+- Product::updateUrls: restore it from the XML store;
+  fixed content parsing (#163192).
+- restore product flags fro the XML store
+- rev 3074
+
+-------------------------------------------------------------------
+Wed Apr 12 13:48:25 CEST 2006 - kkaempf@suse.de
+
+- Only consider best arch/version (#165477)
+- rev 3069
+
+-------------------------------------------------------------------
+Wed Apr 12 10:57:50 CEST 2006 - ma@suse.de
+
+- No need to parse tags at all if there is no item to store values
+  (e.g. data for unwanted arch). Fixed segv trying to store data in
+  NULL item. (#165479)
+- rev3065
+
+-------------------------------------------------------------------
+Tue Apr 11 19:48:51 CEST 2006 - mt@suse.de
+
+- Improved Url path name "//" vs. "/%2f" handling; now if the
+  url has an authority, "/%2f" is used for ftp only (#163784)
+- rev 3062
+
+-------------------------------------------------------------------
+Tue Apr 11 16:41:02 CEST 2006 - jsrain@suse.de
+
+- fixed storing patch scripts to target store (#159928)
+- rev 3058
+
+-------------------------------------------------------------------
+Tue Apr 11 16:24:26 CEST 2006 - kkaempf@suse.de
+
+- if a patch is bad, only skip this patch, not everything
+  (#165200)
+- rev 3057
+
+-------------------------------------------------------------------
+Tue Apr 11 15:42:32 CEST 2006 - ma@suse.de
+
+- Susetags:Selections: Allow parsing older .sel file formats. (#159851)
+- Susetags:Pattern: Fixed parser.
+
+-------------------------------------------------------------------
+Tue Apr 11 15:21:48 CEST 2006 - kkaempf@suse.de
+
+- when uninstalling, only re-establish installed items
+  supplementing the to-be-uninstalled one. (variant of #165111)
+- rev 3054
+
+-------------------------------------------------------------------
+Tue Apr 11 14:17:00 CEST 2006 - ma@suse.de
+
+- Susetags:Package: Parse and provide ins/delnotify texts.
+
+-------------------------------------------------------------------
+Tue Apr 11 13:01:31 CEST 2006 - kkaempf@suse.de
+
+- when checking freshens/supplements at install, only consider
+  best architecture/edition (#164453)
+- rev 3051
+
+-------------------------------------------------------------------
+Tue Apr 11 10:31:41 CEST 2006 - kkaempf@suse.de
+
+- when checking for supplements, only consider best arch, best
+  edition for installation (#165111)
+- rev 3047
+
+-------------------------------------------------------------------
+Tue Apr 11 10:23:14 CEST 2006 - schubi@suse.de
+
+- Bug 165117: build 910: Update: Splitted packages are selected for
+  all archs
+
+-------------------------------------------------------------------
+Tue Apr 11 09:30:14 CEST 2006 - visnov@suse.cz
+
+- ask user if a file exists but does not have a checksum (#162797)
+- rev 3044
+
+-------------------------------------------------------------------
+Mon Apr 10 22:39:34 CEST 2006 - jsrain@suse.de
+
+- parse time and size elements from delta and patch RPM
+- rev 3043
+
+-------------------------------------------------------------------
+Mon Apr 10 18:55:03 CEST 2006 - mt@suse.de
+
+- Added detection of iSeries virtual CD (/dev/iseries/vcd[a-h])
+  devices - on powerpc only (#163971)
+- rev 3042
+
+-------------------------------------------------------------------
+Mon Apr 10 18:05:51 CEST 2006 - kkaempf@suse.de
+
+- fix endless loop in patches parsing.
+- rev 3039
+
+-------------------------------------------------------------------
+Mon Apr 10 17:00:05 CEST 2006 - jsrain@suse.de
+
+- fixed media handling in SuSEtags source (#164879)
+- rev 3037
+
+-------------------------------------------------------------------
+Mon Apr 10 16:30:54 CEST 2006 - kkaempf@suse.de
+
+- honor 'dry_run' on package remove (#164732)
+- rev 3036
+
+-------------------------------------------------------------------
+Mon Apr 10 13:12:49 CEST 2006 - kkaempf@suse.de
+
+- add files from yum filelist as provides to package (#164731)
+- rev 3032
+
+-------------------------------------------------------------------
+Mon Apr 10 11:10:37 CEST 2006 - kkaempf@suse.de
+
+- honor "+Enh:/-Enh:" in packages file (#156513)
+
+-------------------------------------------------------------------
+Mon Apr 10 10:56:24 CEST 2006 - visnov@suse.cz
+
+- fix callback receiver signature to match the callback for removing
+  package
+
+-------------------------------------------------------------------
+Mon Apr 10 10:32:22 CEST 2006 - mvidner@suse.cz
+
+- Added Product::updateUrls, from content/UPDATEURLS (#163192).
+- rev 3024
+
+-------------------------------------------------------------------
+Sat Apr  8 12:03:37 CEST 2006 - schubi@suse.de
+
+- Bug 164440; Taking wrong architecture while updating obsoletes
+  splitted packages
+- rev 3022
+
+-------------------------------------------------------------------
+Sat Apr  8 10:07:06 CEST 2006 - kkaempf@suse.de
+
+- allow relative paths with url file:
+- dont filter atoms from going into pool, multi-arch patch
+  requirements need them. Instead, treat atoms with incompatible
+  architecture as unneeded.
+- rev 3018
+
+-------------------------------------------------------------------
+Fri Apr  7 23:57:37 CEST 2006 - jsrain@suse.de
+
+- product now provides short name
+- rev 3013
+
+-------------------------------------------------------------------
+Fri Apr  7 20:42:09 CEST 2006 - jsrain@suse.de
+
+- read metadata for packages from correct tags in patches (#163220)
+- rev 3011
+
+-------------------------------------------------------------------
+Fri Apr  7 19:57:41 CEST 2006 - kkaempf@suse.de
+
+- more detailed resolver error reports (#162994)
+- rev 3010
+
+-------------------------------------------------------------------
+Fri Apr  7 17:35:07 CEST 2006 - visnov@suse.cz
+
+- report package download progress (#160966)
+- rev 3007
+
+-------------------------------------------------------------------
+Fri Apr  7 16:27:35 CEST 2006 - kkaempf@suse.de
+
+- transact also for languages (#163819)
+- rev 3004
+
+-------------------------------------------------------------------
+Fri Apr  7 15:08:06 CEST 2006 - kkaempf@suse.de
+
+- loop through all affected ResObjects in transactResKind (#163819)
+- rev 3002
+
+-------------------------------------------------------------------
+Fri Apr  7 12:42:35 CEST 2006 - kkaempf@suse.de
+
+- allow re-installation of non-packages (#162906)
+- rev 2998
+
+-------------------------------------------------------------------
+Fri Apr  7 11:50:22 CEST 2006 - mt@suse.de
+
+- Added loop checking for scsi cdroms (/sys/block/srX) in case
+  HAL does not provide any drives like on iSeries (#163971).
+- rev 2995
+
+-------------------------------------------------------------------
+Fri Apr  7 11:05:30 CEST 2006 - kkaempf@suse.de
+
+- add 'licenceToConfirm()' to Product. (#164375)
+
+-------------------------------------------------------------------
+Fri Apr  7 10:36:05 CEST 2006 - ma@suse.de
+
+- Avoid excessive CD hopping on commit. But still far from
+  being perfect. (#159679)
+- Fixed Target::commit: Despite dry_run set True, packages
+  were depeted.
+
+-------------------------------------------------------------------
+Fri Apr  7 08:32:32 CEST 2006 - visnov@suse.cz
+
+- honour if user decides to skip a package in commit (#156031)
+
+-------------------------------------------------------------------
+Thu Apr  6 18:14:30 CEST 2006 - jsrain@suse.de
+
+- fixed parsing external reference to script in patch (#163221)
+- r2981
+
+-------------------------------------------------------------------
+Thu Apr  6 17:07:10 CEST 2006 - dmacvicar@suse.de
+
+- cache and provide content.asc/key optionally. Dont show a
+  popup if they dont exists. (dmacvicar)
+- Actually abort when verify signature workflow is false. (dmacvicar)
+- r2978
+
+-------------------------------------------------------------------
+Thu Apr  6 16:37:49 CEST 2006 - jsrain@suse.de
+
+- fixed setting autorefresh flag for installation sources
+
+-------------------------------------------------------------------
+Thu Apr  6 15:53:02 CEST 2006 - kkaempf@suse.de
+
+- drop patches with incompatible architecture.
+- rev 2972
+
+-------------------------------------------------------------------
+Thu Apr  6 15:33:11 CEST 2006 - mt@suse.de
+
+- Added info method to media verifier base and more debug info
+- rev 2970
+
+-------------------------------------------------------------------
+Wed Apr  5 19:27:46 CEST 2006 - kkaempf@suse.de
+
+- Dont deny the "/" attach point in MediaDIR, since this is used
+  for all "file:" urls, esp. local packages.
+- rev 2962
+
+-------------------------------------------------------------------
+Wed Apr  5 18:21:58 CEST 2006 - schubi@suse.de
+
+- Bug 159673 - only one conflict solvable per page
+
+-------------------------------------------------------------------
+Wed Apr  5 18:01:37 CEST 2006 - kkaempf@suse.de
+
+- parse all dependencies of 'packages' file (#163773)
+- rev 2957
+
+-------------------------------------------------------------------
+Wed Apr  5 17:47:04 CEST 2006 - dmacvicar@suse.de
+
+- Use the original media descr_dir on refresh for
+  suse tags source (#163196)
+- r2952
+
+-------------------------------------------------------------------
+Wed Apr  5 16:44:08 CEST 2006 - kkaempf@suse.de
+
+- add Source::setUrl() for zmd backend helper.
+- rev 2946
+
+-------------------------------------------------------------------
+Wed Apr  5 16:44:01 CEST 2006 - dmacvicar@suse.de
+
+- implement rpm keyring / zypp tmp keyring two-way syncronization at rpm
+target init. (dmacvicar)
+- r2949
+
+-------------------------------------------------------------------
+Wed Apr  5 16:28:42 CEST 2006 - mt@suse.de
+
+- Fixed MediaDISK to use a mount -oro,bind id the disk
+  partition is already attached e.g. by the automounter.
+  Try to mount it a second time may fail (#163486).
+- rev 2944
+
+-------------------------------------------------------------------
+Wed Apr  5 15:04:31 CEST 2006 - kkaempf@suse.de
+
+- honor optional 3rd parameter to "=Loc:" key of packages (#154337)
+- rev 2940
+
+-------------------------------------------------------------------
+Wed Apr  5 12:48:19 CEST 2006 - visnov@suse.cz
+
+- only try to create a source of a given type when restoring
+  from the persistent store (#162111)
+
+-------------------------------------------------------------------
+Wed Apr  5 11:36:54 CEST 2006 - kkaempf@suse.de
+
+- parse all dependencies for patterns (.pat) files (#160602)
+- drop YOUPATH and YOUURL from content file.
+- rev 2924
+
+-------------------------------------------------------------------
+Wed Apr  5 09:27:08 CEST 2006 - visnov@suse.cz
+
+- properly initialize autorefresh for non-remote sources (#154990)
+- rev 2919
+
+-------------------------------------------------------------------
+Tue Apr  4 19:19:39 CEST 2006 - mt@suse.de
+
+- Added flag to MediaManager::isUseableAttachPoint, whether
+  to check against system mount entries or not.
+- Disallow to use the attachpoints of another media handlers
+  as source path in MediaDIR.
+- rev 2917
+
+-------------------------------------------------------------------
+Tue Apr  4 18:47:01 CEST 2006 - dmacvicar@suse.de
+
+-implement callbacks for when package verification (checksum)
+ fails, offer to retry or abort
+
+-------------------------------------------------------------------
+Tue Apr  4 16:57:51 CEST 2006 - dmacvicar@suse.de
+
+- Fix construction of checksum objects when using non-standard
+  checksum algorithms
+- Fix broken YUM cache
+- r2913
+
+-------------------------------------------------------------------
+Tue Apr  4 16:54:44 CEST 2006 - kkaempf@suse.de
+
+- Keep packages with no version upgrade installed during
+  distribution upgrade (#162972)
+- add 'transactReset()' helper function for UI.
+- rev 2908
+
+-------------------------------------------------------------------
+Tue Apr  4 14:47:14 CEST 2006 - dmacvicar@suse.de
+
+- r2906
+
+-------------------------------------------------------------------
+Tue Apr  4 14:43:15 CEST 2006 - ma@suse.de
+
+- Fixed candidate handling in ui::Selectable. (#156589)
+
+-------------------------------------------------------------------
+Tue Apr  4 14:06:23 CEST 2006 - dmacvicar@suse.de
+
+- fix #162984 , gpg hangs because the matching data file
+  for the key cannot be find. (dmacvicar)
+- Fix restore of YUM source using the same cache dir semantics as
+ susetags instead of assuming there is a cache if a cache_dir
+  was given. (dmacvicar)
+
+-------------------------------------------------------------------
+Tue Apr  4 12:37:51 CEST 2006 - kkaempf@suse.de
+
+- use DISTPRODUCT/DISTVERSION from content file to generate the
+  product name, version, and release.
+- rev 2902
+
+-------------------------------------------------------------------
+Mon Apr  3 20:45:55 CEST 2006 - mt@suse.de
+
+- Removed broken forcing of absolute ftp paths added in rev2705 to
+  MediaCurl, refined cleanupPathName/setPathName in url (#154197).
+- rev 2900
+
+-------------------------------------------------------------------
+Mon Apr  3 19:30:35 CEST 2006 - kkaempf@suse.de
+
+- add Resolver::freshenPool() (#156980)
+- rev 2893
+
+-------------------------------------------------------------------
+Mon Apr  3 08:33:12 CEST 2006 - kkaempf@suse.de
+
+- skip incompatible archs in filelist parsing.
+- restrict pathes to 'interesting' ones (/bin/, /sbin/, /lib/,
+  /lib64/, ...)
+- rev 2886
+
+-------------------------------------------------------------------
+Sun Apr  2 22:18:06 CEST 2006 - kkaempf@suse.de
+
+- skip incompatible archs in primary parsing.
+- rev 2883
+
+-------------------------------------------------------------------
+Sun Apr  2 11:17:56 CEST 2006 - kkaempf@suse.de
+
+- allow setting of source when parsing local .rpm (#147765)
+- rev 2880
+
+-------------------------------------------------------------------
+Fri Mar 31 18:53:55 CEST 2006 - schubi@suse.de
+
+- Do not update packages over other architectures
+
+-------------------------------------------------------------------
+Fri Mar 31 18:28:59 CEST 2006 - sh@suse.de
+
+- Added zypp/ui/UserWantedPackages to support the UI's
+  "automatic changes" dialog (bug #152700)
+
+-------------------------------------------------------------------
+Fri Mar 31 18:02:05 CEST 2006 - jsrain@suse.de
+
+- use KeyRing class to validate repomd.xml (#160909)
+
+-------------------------------------------------------------------
+Fri Mar 31 17:33:21 CEST 2006 - dmacvicar@suse.de
+
+- Product resolvables should be readable by normal users.
+  (#162474) (dmacvicar)
+- implemented keyring and metadata signature verification
+  in susetags source
+- dont delete the lock if we did not acquire it
+- r2847
+
+-------------------------------------------------------------------
+Fri Mar 31 16:34:51 CEST 2006 - mt@suse.de
+
+- Added disabling of the automounter while MediaManager
+  init and restoring of the old state on exit (#154326).
+- Implemented check if media (CD) is automounted or not
+- rev 2840
+
+-------------------------------------------------------------------
+Fri Mar 31 13:27:09 CEST 2006 - mt@suse.de
+
+- Implemented several hal get/set/removeDeviceProperty wrappers
+- Improved HalException to allow to fetch HAL/DBUS error componets
+- rev 2830
+
+-------------------------------------------------------------------
+Fri Mar 31 12:44:25 CEST 2006 - kkaempf@suse.de
+
+- honor subscription status of catalogs (#162350)
+- rev 2827
+
+-------------------------------------------------------------------
+Fri Mar 31 11:29:05 CEST 2006 - mt@suse.de
+
+- Enabled CD eject error reporting exceptions (#154326)
+- rev 2822
+
+-------------------------------------------------------------------
+Fri Mar 31 06:40:51 CEST 2006 - kkaempf@suse.de
+
+- support "dry run" (#159467)
+- implement "transactResKind" (#161400)
+- rev 2817
+
+-------------------------------------------------------------------
+Thu Mar 30 17:22:49 CEST 2006 - jsrain@suse.de
+
+- add checksum for external patches (#159928)
+
+-------------------------------------------------------------------
+Thu Mar 30 16:48:01 CEST 2006 - kkaempf@suse.de
+
+- calculate product architecture (#158198)
+
+-------------------------------------------------------------------
+Wed Mar 29 23:28:13 CEST 2006 - jsrain@suse.de
+
+- fixed checking checksum of YUM metadata, added sha1 vs. sha256
+  detection
+
+-------------------------------------------------------------------
+Wed Mar 29 23:12:30 CEST 2006 - ma@suse.de
+
+- Auto protect installed packages from unknown vendor. (#157446)
+
+-------------------------------------------------------------------
+Wed Mar 29 09:13:51 CEST 2006 - visnov@suse.de
+
+- added support for external scripts to metadata (#159928) (jsrain)
+- fixed handling of Language resolvables (ma)
+- fix leak in rpmdb (dmacvicar)
+- added softlock for autoyast (#159466) (ma)
+- Fixed exceptions in doGetFileCopy() to show full url
+  including the file instead of just the media base url. (mt)
+- Provide Language::summary (ma)
+- check patterns and selections file exist
+  before veryfing them (#161300) (dmacvicar)
+- added YUM metadata checksum computation (jsrain)
+- added interface to patch of a message (jsrain)
+- r2734
+
+-------------------------------------------------------------------
+Mon Mar 27 23:57:16 CEST 2006 - jsrain@suse.de
+
+- added support for external scripts to metadata (#159928)
+- r2709
+
+-------------------------------------------------------------------
+Sat Mar 25 22:08:26 CET 2006 - jsrain@suse.de
+
+- report separate exception when trying to start source cache again to
+  suppress incorrect error message in XEN installation
+- r2682
+
+-------------------------------------------------------------------
+Fri Mar 24 18:16:22 CET 2006 - schubi@suse.de
+
+- Implement inter process locking in zypp.
+- Added No medium found output
+- splitting modaliases in supplements TOO
+- parse also the available signing keys
+
+-------------------------------------------------------------------
+Fri Mar 24 10:44:10 CET 2006 - visnov@suse.cz
+
+- release all media when removing source (#159754) (visnov)
+- more testsuites (schubi)
+- updated translations (schubi)
+- added MediaNotEjectedException (mt)
+- rev 2652
+
+-------------------------------------------------------------------
+Thu Mar 23 14:10:54 CET 2006 - dmacvicar@suse.de
+
+- fix patches descriptions (dmacvicar)
+- fix source serialization (dmacvicar)
+- metadata for kernel test (schubi)
+- Arch tests updated (ma)
+- classify NULL Ptr as unique (ma)
+- Added host check, because file Url allows it now. (mt)
+- prepare modalias fix (#159766) (ma)
+- Provide iterator based access to SourceManager data. (ma)
+- Fixed "file:" Url scheme config to allow relative paths; (mt)
+  RFC1738 says, it may contain a hostname as well...
+- revision 2633
+
+-------------------------------------------------------------------
+Wed Mar 22 19:58:37 CET 2006 - visnov@suse.cz
+
+- pkg-config support (mvidner)
+- close all medias when destructing MediaSet (jsrain)
+- rev 2622
+
+-------------------------------------------------------------------
+Wed Mar 22 15:48:05 CET 2006 - dmacvicar@suse.de
+
+- Bug 159976 - build 804: Adding AddOn CD via ftp gives error (dmacvicar)
+- Message callback implemented to show patch messages (visnov)
+- Bug 159696 (schubi)
+- provide transform_iterators to iterate over a maps keys or values (ma)
+- added 'bool Arch::empty() const' test for an empty Arch string (ma)
+- added script and message installation (jsrain)
+- chooses the 'right' kernel now (kkaempf)
+- Use noarch if no arch is specified in patches (dmacvicar)
+- rev 2611
+
+-------------------------------------------------------------------
+Tue Mar 21 19:06:56 CET 2006 - mvidner@suse.cz
+
+- Added some debug output including the access id (mt)
+- Bug #154326: Enabled FORCE_RELEASE_FOREIGN flag causing
+  release with eject=true on attached media, to umount
+  other mounts as well. (mt)
+- 159483 - solver does not blame missing dependency (schubi)
+- Added a variant of MediaHandler::forceRelaseAllMedia (ma)
+- Fixed MediaCD::forceEject() to handle DELAYED_VERIFY
+  and use forceRelaseAllMedia if FORCE_RELEASE_FOREIGN=1 (ma)
+- fixed ZYPP_RETHROW (#156430) (ma)
+- patch for #156114 (visnov)
+- fixed container.erase loops (ma)
+- Fixed to reset desired (cached) flag before the action (mt)
+- Removed return in forceRelaseAllMedia (void function) (mt)
+- Parse nonexisting architecture to noarch so patches dont get
+  filtered by the pool (dmacvicar)
+- 159512 - yast2-qt does not show label of to be installed products
+  anymore (dmacvicar)
+- 159765 - Hidden patterns still visible (dmacvicar)
+- Use noarch if no arch is specified. (dmacvicar)
+- r2594
+
+-------------------------------------------------------------------
+Tue Mar 21 09:04:06 CET 2006 - visnov@suse.de
+
+- properly report error for media change callback
+- rev 2579
+
+-------------------------------------------------------------------
+Mon Mar 20 23:02:07 CET 2006 - ma@suse.de
+
+- fixed memory leak in XMLNodeIterator (#157474)
+- disabled storing filelist (YUMFileListParser) and changelog (YUMOtherParser)
+- Renamed private MediaManager::forceMediaRelease
+  function to forceReleaseShared (more exact name)
+- Implemented forceRelaseAllMedia() that can be
+  used to release also foreign (user) mounts.
+- Added use of forceRelaseAllMedia for CD/DVDs
+  if FORCE_RELEASE_FOREIGN is 1 (default 0)
+- little cleanup of the checkAttached function
+- r2578
+
+-------------------------------------------------------------------
+Mon Mar 20 17:04:28 CET 2006 - mvidner@suse.cz
+
+- don't try to attach without exception handling (#158620)
+- fix descriptions, as a new tag Des for selections exists now.
+- fix #157683: failure after adding add-on product to install
+  sources
+- added more files for translation
+- resolve-dependencies.cc: establish pool
+- parse-metadata.cc: catch bad URL
+- set zmdid for atoms
+- r2574
+
+-------------------------------------------------------------------
+Sun Mar 19 19:24:44 CET 2006 - kkaempf@suse.de
+
+- fix testsuite.
+- provide edition and architecture for all kinds of yum
+  resolvables.
+- fix ResStatus output.
+- establish atoms correctly.
+- treat requires to unneeded resolvables as fulfilled.
+- rev 2559
+
+-------------------------------------------------------------------
+Sun Mar 19 00:05:17 CET 2006 - kkaempf@suse.de
+
+- fix the build
+- only consider best architecture/version (#157594)
+- prefer providers which supplement/enhance installed or
+  to-be-installed packages (fixes the tpctl-kmp issue)
+- rev 2546
+
+-------------------------------------------------------------------
+Sat Mar 18 02:22:22 CET 2006 - kkaempf@suse.de
+
+- provide more filters for pkg-bindings (#158602)
+- add SystemResObject to provide system (modalias, hal, ...)
+  capabilities.
+- handle this during resolving.
+- make the modalias and hal capability match the SystemResObject
+  by default, thereyby triggering a modalias (resp. hal)
+  evaluation.
+- xmlstore: decouple target store from YUM schema.
+- clean up moving of hal() and modalias() from provides to
+  supplements in ResolvableImpl.
+- add PatchContents() for UI.
+- handle Edition::noedition as empty string.
+- r2537
+
+-------------------------------------------------------------------
+Tue Mar 14 23:32:44 CET 2006 - jsrain@suse.de
+
+- releasing all medias when asking for CD (#156981)
+- r2471
+
+-------------------------------------------------------------------
+Tue Mar 14 19:38:43 CET 2006 - mvidner@suse.cz
+
+- ResStatus::resetTransact must return a value.
+- Fixed random build failures in LanguageCode.cc.
+  (Rewrote the CodeMaps constructor so that gcc does not
+  optimize a 500-statement basic block.)
+- Fix constructions of patch objects. Actually insert atoms in atoms
+  list. Insert atoms for package even if the package does not exists
+  in the source. Fixes #157628 (dmacvicar).
+- Fixed license reading from susetags, #151834 (dmacvicar).
+- r2468
+
+-------------------------------------------------------------------
+Tue Mar 14 16:59:42 CET 2006 - mvidner@suse.cz
+
+- added ResStatus::resetTransact (ma)
+- bugfix for #156439 (schubi)
+- Added Source_Ref::setAlias (#154913).
+- Do not assume there is a product file when scanning for products
+  (visnov)
+- function to disable all sources in the persistent store (visnov)
+- dependency errors go to stdout, not stderr; output resolver info
+  directly to stderr (kkaempf)
+- rev 2464
+
+-------------------------------------------------------------------
+Tue Mar 14 01:34:38 CET 2006 - kkaempf@suse.de
+
+- fix merging of resolver info (needed for #157684).
+- errors are also important in ResolverInfo.
+- improve debug output in ResolverContext.
+- rev 2455
+
+-------------------------------------------------------------------
+Mon Mar 13 22:54:01 CET 2006 - jsrain@suse.de
+
+- delete RPMs downloaded via HTTP/FTP after installnig them
+  (#157011)
+- fixed product registration (reverted autorefresh patch) (#157566)
+
+-------------------------------------------------------------------
+Mon Mar 13 11:53:52 CET 2006 - kkaempf@suse.de
+
+- if root!="/", always prefer the upgrade candidate (#155472)
+- implement license confirmed api for UI.
+- prefer architecture over version in distribution upgrade
+  (#157501)
+- clean up media handling.
+- rev 2448
+
+-------------------------------------------------------------------
+Sun Mar 12 22:31:09 CET 2006 - kkaempf@suse.de
+
+- init Modalias properly.
+- fix warnings in testcases.
+- rev 2432
+
+-------------------------------------------------------------------
+Sat Mar 11 21:25:18 CET 2006 - kkaempf@suse.de
+
+- drop libjpeg-devel and sqlite-devel from build requires.
+
+-------------------------------------------------------------------
+Sat Mar 11 08:16:53 CET 2006 - kkaempf@suse.de
+
+- implement 'modalias()' capability (#157406)
+- make dependencies consistent, its 'freshens'.
+- cope with user umounts of devices.
+- add debug to SourceManager.
+- rev 2418
+
+-------------------------------------------------------------------
+Fri Mar 10 16:20:41 CET 2006 - kkaempf@suse.de
+
+- allow version downgrade during distribution upgrade if the
+  newer package is coming from a trusted vendor (#155472)
+- implement locale fallback
+- 'freshen' -> 'freshens' in schema definitions to make it
+  consistent with all other dependency definitions.
+- better error reporting for .pat and .sel files.
+- rule out packages from dependency resolutions which are
+  de-selected by user (#155368)
+- use locale fallbacks in package translations.
+- refresh source when re-enabling it.
+- rev 2406
+
+-------------------------------------------------------------------
+Tue Mar  7 21:18:19 CET 2006 - kkaempf@suse.de
+
+- split of libzypp-zmd-backend subpackage as a stand-alone
+  leaf package.
+- encapsulate bool test for Source_Ref better.
+- fixed stack overflow (ma).
+- make testsuite build again.
+- rev 2346
+
+-------------------------------------------------------------------
+Tue Mar  7 16:17:07 CET 2006 - kkaempf@suse.de
+
+- fixed URL rewriting for CD2 and following (#154762)
+- fixed ResPoolProxy diffState (for proper ok/cancel support
+  in UI)
+- added special exception class for aborting installation
+  (#154936)
+- only auto-change directories if they end in CDn or DVDn.
+- rev 2320.
+
+-------------------------------------------------------------------
+Tue Mar  7 15:37:51 CET 2006 - kkaempf@suse.de
+
+- silently ignore multiple installs of the same package.
+- fix disk usage for installs and uninstalls.
+- rev 2308
+
+-------------------------------------------------------------------
+Mon Mar  6 22:22:57 CET 2006 - kkaempf@suse.de
+
+- zmd-backend: filter out incompatible architectures from
+  repository.
+- rev 2298
+
+-------------------------------------------------------------------
+Mon Mar  6 21:35:24 CET 2006 - kkaempf@suse.de
+
+- sync libzypp media data with mtab.
+- improve resolver error and solution reports.
+- fix source cache reading (#155459).
+- default cached sources to enabled (#155459).
+- let each source provide public keys.
+- rev 2297
+
+-------------------------------------------------------------------
+Sun Mar  5 15:48:29 CET 2006 - kkaempf@suse.de
+
+- only write by-sovler transactions back (#154976)
+- rev 2278
+
+-------------------------------------------------------------------
+Sat Mar  4 12:36:40 CET 2006 - kkaempf@suse.de
+
+- release last used source at end of commit (#155002)
+- rev 2277
+
+-------------------------------------------------------------------
+Fri Mar  3 23:14:50 CET 2006 - kkaempf@suse.de
+
+- cope with NULL values in zmd catalogs table (#153584)
+- set YAST_IS_RUNNING in transact zmd helper (#154820)
+- run SuSEconfig after transact zmd helper (#154820)
+- add softTransact to honor user vs. soft requirements (#154650)
+- honor all build keys provided by a package source.
+- add source metadata refresh.
+- add progress callbacks to zmd helpers.
+- rev 2276
+
+-------------------------------------------------------------------
+Thu Mar  2 21:59:07 CET 2006 - kkaempf@suse.de
+
+- include .diffs into main source.
+- catch exception when ejecting media which was unmounted externally
+  (#154697).
+- init source in zmd-backend correctly (#154667)
+- implement disk usage info for YaST.
+- clean up XML schema files.
+- catch CPUs identifying as 'i686' but being 'i586'.
+- allow definition of preferred attach (mount) point for media.
+- make resolver results more readable.
+- use language fallbacks if none of multiple language providers
+  matches.
+- get rid of ignoring wrong arch in resolver, having the wrong
+  architecture is prevented by other means.
+- prepare for translations in exceptions.
+- fix 'abort does not abort'
+- implement 'flag' I/O in target cache backend.
+- skip incompatibles architectures in packages.<lang>
+- rev 2228
+
+-------------------------------------------------------------------
+Thu Mar  2 13:44:34 CET 2006 - kkaempf@suse.de
+
+- dont even provide src/nosrc from the source.
+- rev 2169 + diffs
+
+-------------------------------------------------------------------
+Wed Mar  1 17:23:23 CET 2006 - kkaempf@suse.de
+
+- Initialize commit result (#154409)
+- release media if its wrong (#154326)
+- dont copy src/nosrc packages to the pool (#154627)
+- reduce XML logging.
+- rev 2169 + diffs
+
+-------------------------------------------------------------------
+Tue Feb 28 16:10:14 CET 2006 - kkaempf@suse.de
+
+- fix path of .po files (#154074).
+- parse the correct package.<lang> file (kinda #154074).
+- complain about bad "=Sel:" or "=Pat:" lines (#153065).
+- reattach all released medias.
+- raise exception instead of abort() on XML errors (#154104).
+- update translations.
+- PathInfo: implemented a copy_dir_content (variant of copy_dir)
+  and is_empty_dir utility function
+- rev 2169
+
+-------------------------------------------------------------------
+Tue Feb 28 14:51:46 CET 2006 - kkaempf@suse.de
+
+- check freshens and supplements for packages (#154074).
+- only complain about incomplete installed resolvables,
+  if they are uninstalled, schedule them for installation.
+  (#154074)
+- add testcases for locale() provides.
+- add lang_country -> lang fallback.
+- have locale(parent:...) deps match any provides of 'parent'
+  also when uninstalling a package.
+- rev 2148
+
+-------------------------------------------------------------------
+Tue Feb 28 10:35:15 CET 2006 - kkaempf@suse.de
+
+- change the locale(...) separator to ";" (#153791)
+- complete "find-files" of zmd-backend.
+- rev 2140
+
+-------------------------------------------------------------------
+Tue Feb 28 10:28:06 CET 2006 - visnov@suse.de
+
+- avoid attaching media when initializing source
+- rev 2139
+
+-------------------------------------------------------------------
+Mon Feb 27 21:26:00 CET 2006 - kkaempf@suse.de
+
+- warn about misspelled 'locale(...)' provides
+- add testcases
+- rev 2134
+
+-------------------------------------------------------------------
+Mon Feb 27 20:19:40 CET 2006 - kkaempf@suse.de
+
+- fix the build
+- rev 2129
+
+-------------------------------------------------------------------
+Mon Feb 27 18:15:16 CET 2006 - kkaempf@suse.de
+
+- provide available locales to application (#153583)
+- honor 'requestedLocales' (language dependant packages)
+- honor release requests for all holders of a device.
+- silently re-attach after a forced release.
+- solver improvements.
+- handle source caches.
+- proper logging in zmd backend helpers.
+- rev 2127
+
+-------------------------------------------------------------------
+Mon Feb 27 13:44:39 CET 2006 - kkaempf@suse.de
+
+- upgrade always to best version and arch (#153577)
+- reset 'transact' state for obsoleted packages (#153578)
+- translation updates
+- rev 2113
+
+-------------------------------------------------------------------
+Mon Feb 27 10:42:33 CET 2006 - kkaempf@suse.de
+
+- add support for 'local' .rpm packages to zmd-backend.
+- rev 2101
+
+-------------------------------------------------------------------
+Sun Feb 26 20:24:10 CET 2006 - kkaempf@suse.de
+
+- fix build of zmd/backend.
+- actually fill 'files' table in package-files.
+- rev 2094
+
+-------------------------------------------------------------------
+Sun Feb 26 17:43:06 CET 2006 - kkaempf@suse.de
+
+- improve testcases.
+- add 'setPossibleLocales()' to ZYpp, this defines the set
+  of possible locales to choose from (#153583)
+- provide LanguageImpl and create 'Language' resolvables for
+  each 'possible' locale.
+- fix YUM parsing of patches, insert 'atoms' to link patches
+  with packages.
+- replace gzstream/ with own, existing implementation.
+- honor locks in solver (#150231)
+- sync pool with target after commit() properly (#150565, #153066)
+- new zmd helper 'package-files'
+- rev 2093
+
+-------------------------------------------------------------------
+Thu Feb 23 21:45:06 CET 2006 - kkaempf@suse.de
+
+- prevent multiple initializations of the target (#153124)
+- implement 'loopback mounted ISO images'
+- retain old package sources on upgrade.
+- support compressed .xml files in 'repodata' type repositories.
+- rev 2025
+
+-------------------------------------------------------------------
+Thu Feb 23 15:16:58 CET 2006 - kkaempf@suse.de
+
+- parse locale(...) provides and construct correct dependencies.
+
+-------------------------------------------------------------------
+Thu Feb 23 14:16:44 CET 2006 - kkaempf@suse.de
+
+- always upgrade to candidate (#152760).
+- fix typo in package sorting.
+- prepare handling of locale provides.
+- rev 1995
+
+-------------------------------------------------------------------
+Thu Feb 23 10:53:51 CET 2006 - kkaempf@suse.de
+
+- sort src/nosrc package to right list during commit.
+- revert installtime/buildtime in susetags parser (#152760)
+- rev 1990
+
+-------------------------------------------------------------------
+Thu Feb 23 10:22:08 CET 2006 - kkaempf@suse.de
+
+- reset state after successful commit (#153030)
+- run "rpm -e" always with "--nodeps" (#153026)
+- provide separate resolvable kind for src packages.
+- extend status field for LOCK and LICENSE.
+- add sameState()/diffState() for UI.
+- provide 'best' candidate for UI.
+- set 60 sec timeout for curl access.
+- don't cross-compare solver results, takes too much time.
+- provide sizes of installed packages.
+- extend REQUIRES semantics in content file.
+- add "parse-metadata" helper to zmd-backend.
+- rev 1987
+
+-------------------------------------------------------------------
+Wed Feb 22 14:51:46 CET 2006 - kkaempf@suse.de
+
+- provide complete disk usage data (#152761)
+- include upgrade flag when copying solver solution
+  back to pool (#152717)
+- rev 1959
+
+-------------------------------------------------------------------
+Wed Feb 22 13:16:48 CET 2006 - kkaempf@suse.de
+
+- don't insert incompatible architectures to the pool (#151933)
+- don't accept incompatible architectures from a repository
+  (#151933)
+- separate rpm log (#151431).
+- allow extended product requires.
+- rev 1954
+
+-------------------------------------------------------------------
+Tue Feb 21 22:02:59 CET 2006 - kkaempf@suse.de
+
+- provide the XML schema files in the main package. (#152593)
+
+-------------------------------------------------------------------
+Tue Feb 21 20:05:34 CET 2006 - kkaempf@suse.de
+
+- provide arch compat handling.
+- implement data upload to zmd.
+- fix source metadata caching on target.
+- add 'supplements' dependencies to 'yum' parser.
+- provide user agent identification to curl calls.
+- move resolver branches (multiple alternatives) back in queue
+  (resolve known things first, then the unknown ones).
+- clean up 'packages' parser.
+- rev 1947
+
+-------------------------------------------------------------------
+Tue Feb 21 09:18:53 CET 2006 - kkaempf@suse.de
+
+- improve media mount/umount interface
+- prepare class ArchCompat for proper architecture ordering
+  and compatibility handling.
+- add returns to dummy functions in DbAccess.
+- rev 1913
+
+-------------------------------------------------------------------
+Mon Feb 20 21:08:22 CET 2006 - kkaempf@suse.de
+
+- don't explictly delete to-be-upgraded packages.
+- finish query-system, resolve-dependencies, and transact for
+  libzypp-zmd-backend.
+- provide Pattern::category.
+- move system architecture to toplevel.
+- make target store pathname settable.
+- speed up rpmdb reading by properly filtering unwanted file
+  provides.
+- rev 1905
+
+-------------------------------------------------------------------
+Sun Feb 19 20:35:03 CET 2006 - kkaempf@suse.de
+
+- new translations.
+- proofread texts.
+- when comparing solutions, prefer higher versions.
+- provide generic 'SafeBool' for bool conversions.
+- add PtrTypes testsuites.
+- rev 1876
+
+-------------------------------------------------------------------
+Fri Feb 17 21:43:51 CET 2006 - kkaempf@suse.de
+
+- integrate all diffs
+- move Target::commit to toplevel API
+- generalize dependency iterators and hash dependency
+  information in pool (for speedup)
+- add 'supplements' as dependency
+- make more pattern attributes available
+- drop "smbfs" in favour of "cifs" (#151476)
+- add metadata cache to sources (Beta4 bug)
+- run "rpm -e"  with name-version-release
+- fix update conflicts
+- rev 1864
+
+-------------------------------------------------------------------
+Thu Feb 16 20:02:19 CET 2006 - kkaempf@suse.de
+
+- fix-mediachange.diff: dont skip CD but retry after media change
+- cd-eject-button.diff: fix CD url so YaST recognizes it and shows
+  'eject' button
+- release-forced-eject-no-ptrfix.diff: fix refcounting in ptrs
+  so media handle gets actually released and media unmounted.
+
+-------------------------------------------------------------------
+Thu Feb 16 14:40:31 CET 2006 - kkaempf@suse.de
+
+- implement arch scoring
+- prefer better arch (#151427)
+- transitive depedencies of weak requirements are non-weak
+  (#151446)
+- rev 1778 + diff
+
+-------------------------------------------------------------------
+Wed Feb 15 18:19:12 CET 2006 - kkaempf@suse.de
+
+- ignore self and to-be-updated conflicts (#150844)
+- fix enable of target store (for non-packages)
+- rev 1778
+
+-------------------------------------------------------------------
+Wed Feb 15 13:11:28 CET 2006 - kkaempf@suse.de
+
+- fix "cd:" url (#151121)
+- provide location() in public Package api
+- allow running distribution upgrade in testmode
+- extend HAL interface
+- rev 1762
+
+-------------------------------------------------------------------
+Wed Feb 15 10:10:48 CET 2006 - kkaempf@suse.de
+
+- pass normal and locale packages from selections correctly.
+- its "baseconf" for base selections.
+- Make 'ZYpp' an obvious singleton.
+- provide releasenotesUrl.
+- dont continue upgrade without target.
+- implement 'fake' hal for testing.
+- fix package sizes.
+- more solver testcases.
+- rev 1754
+
+-------------------------------------------------------------------
+Tue Feb 14 20:52:02 CET 2006 - kkaempf@suse.de
+
+- extend requires of libzypp-devel
+- provide package sizes for UI
+- provide more UI helpers
+- implement Product and related functions
+- fix split provides in distribution upgrade
+- provide locale information to system
+- ask HAL for available devices
+- reduce debug information in solver
+- filter architectures in source, not in solver
+- rev 1743
+
+-------------------------------------------------------------------
+Tue Feb 14 07:27:39 CET 2006 - visnov@suse.de
+
+- disable another testsuite for now
+- fetch the default locale from environment
+- support user-defined formatting of log
+- rev 1710
+
+-------------------------------------------------------------------
+Mon Feb 13 20:41:36 CET 2006 - visnov@suse.de
+
+- providing basic product information from susetags source
+- public API for preferred language
+- implemented redirect of logging (#149001)
+- report start/finish of source data parsing (#150211)
+- store/restore source aliases properly (#150256)
+- disable a lot of debug logging to speed up solver
+- properly rewrite URL for CDn directory layouts (#149870)
+- rev 1706
+
+-------------------------------------------------------------------
+Sun Feb 12 16:59:48 CET 2006 - kkaempf@suse.de
+
+- add save/restore state to facilitate UI 'cancel'
+- enable target/store
+- add 'forceResolve' call and flag to resolver to switch between
+  task-oriented ZMD and interactive YaST behaviour.
+- Fix resolver problem solution texts.
+- improve solver problem solution offerings.
+- fix media access handling to better support multiple
+  requestors to single media.
+- move the media number checking to the source (media requestor)
+  which knows how to verify the correct media.
+- Fix CD ordering (#149871), adding testcases.
+- Move 'PoolItemList' and 'PoolItemSet' typedefs inside classes.
+- Add selections to testcases.
+- rev 1673
+
+-------------------------------------------------------------------
+Sat Feb 11 10:17:15 CET 2006 - kukuk@suse.de
+
+- Fix missing return in Source.cc:124
+
+-------------------------------------------------------------------
+Fri Feb 10 18:41:29 CET 2006 - kkaempf@suse.de
+
+- cope with empty arch field in selections
+- enable dummy "enableStorage" function
+- rev 1610-branch
+
+-------------------------------------------------------------------
+Fri Feb 10 15:36:43 CET 2006 - kkaempf@suse.de
+
+- fix random data return in Source.cc
+- rev 1610
+
+-------------------------------------------------------------------
+Fri Feb 10 15:00:45 CET 2006 - kkaempf@suse.de
+
+- adapt zmd-backend to SourceImpl API change
+- rev 1608
+
+-------------------------------------------------------------------
+Fri Feb 10 13:54:43 CET 2006 - kkaempf@suse.de
+
+- fix the packages parser bug. Now all packages are parsed
+  including (english) translations.
+  source/susetags is back to svn head.
+- rev 1600
+
+-------------------------------------------------------------------
+Fri Feb 10 10:30:12 CET 2006 - kkaempf@suse.de
+
+- fix off-by-one bug in bitfield handling
+- revert source/susetags to rev 1411
+- rev 1586
+
+-------------------------------------------------------------------
+Thu Feb  9 22:21:43 CET 2006 - kkaempf@suse.de
+
+- dont prereq-sort non-packages
+- rev 1584
+
+-------------------------------------------------------------------
+Thu Feb  9 21:29:00 CET 2006 - kkaempf@suse.de
+
+- rev 1582
+
+-------------------------------------------------------------------
+Thu Feb  9 11:10:54 CET 2006 - kkaempf@suse.de
+
+- update to rev 1543
+
+-------------------------------------------------------------------
+Thu Feb  9 00:49:23 CET 2006 - ro@suse.de
+
+- require hal-devel in libzypp-devel
+- re-merge fixes (RPM_OPT_FLAGS)
+
+-------------------------------------------------------------------
+Wed Feb  8 23:53:58 CET 2006 - kkaempf@suse.de
+
+- make solver behaviour a bit more interactive
+- rev 1537
+
+-------------------------------------------------------------------
+Wed Feb  8 18:45:21 CET 2006 - schwab@suse.de
+
+- Fix syntax error in configure script.
+- Use RPM_OPT_FLAGS.
+
+-------------------------------------------------------------------
+Wed Feb  8 17:03:37 CET 2006 - kkaempf@suse.de
+
+- update for qt ui integration
+- rev 1504
+
+-------------------------------------------------------------------
+Tue Feb  7 23:18:09 CET 2006 - kkaempf@suse.de
+
+- split off libzypp-zmd-backend
+- rev 1466
+
+-------------------------------------------------------------------
+Tue Feb  7 23:10:59 CET 2006 - kkaempf@suse.de
+
+- another update to svn
+
+-------------------------------------------------------------------
+Mon Feb  6 20:42:22 CET 2006 - kkaempf@suse.de
+
+- finish rpm callbacks
+- finish UI API
+- fix state change resolver<->pool
+- zmd backend stuff
+- speed up tag file parsing
+- rev 1405
+
+-------------------------------------------------------------------
+Mon Feb  6 16:53:03 CET 2006 - schubi@suse.de
+
+- disabling failing tests of s390 and ppc
+
+-------------------------------------------------------------------
+Mon Feb  6 11:14:16 CET 2006 - schubi@suse.de
+
+- Snapshoot rev 1367
+
+-------------------------------------------------------------------
+Mon Feb  6 01:42:48 CET 2006 - kkaempf@suse.de
+
+- use hashes for pool
+- rev 1343
+
+-------------------------------------------------------------------
+Fri Feb  3 14:21:47 CET 2006 - schubi@suse.de
+
+- removed Obsoletes:    yast2-packagemanager
+
+-------------------------------------------------------------------
+Fri Feb  3 11:36:19 CET 2006 - schubi@suse.de
+
+- Snapshoot 3 Feb 2005 (11:30)
+
+-------------------------------------------------------------------
+Thu Feb  2 14:27:02 CET 2006 - schubi@suse.de
+
+- Snapshoot 2 Feb 2005 (14:00)
+
+-------------------------------------------------------------------
+Thu Feb  2 12:23:03 CET 2006 - schubi@suse.de
+
+- Snapshoot 2 Feb 2005 ( integrating YaST )
+
+-------------------------------------------------------------------
+Wed Jan 25 21:37:50 CET 2006 - mls@suse.de
+
+- converted neededforbuild to BuildRequires
+
+-------------------------------------------------------------------
+Sat Jan 14 08:51:55 CET 2006 - kkaempf@suse.de
+
+- Initial version
+
diff --git a/packaging/0001-Disable-proxy-only-if-_none_-is-set-in-repo-file.patch b/packaging/0001-Disable-proxy-only-if-_none_-is-set-in-repo-file.patch
new file mode 100644 (file)
index 0000000..197e70b
--- /dev/null
@@ -0,0 +1,39 @@
+diff -uNr libzypp-9.8.3/zypp/media/MediaCurl.cc libzypp-9.8.3.new/zypp/media/MediaCurl.cc
+--- libzypp-9.8.3/zypp/media/MediaCurl.cc      2011-07-07 14:49:33.876569258 +0800
++++ libzypp-9.8.3.new/zypp/media/MediaCurl.cc  2011-07-07 14:48:47.070681412 +0800
+@@ -553,6 +553,9 @@
+       ZYPP_RETHROW(e);
+   }
++  if ( _url.getQueryParam( "proxy" ) == "_none_" )
++      SET_OPTION(CURLOPT_NOPROXY, "*");
++
+   // if the proxy was not set by url, then look
+   if ( _settings.proxy().empty() )
+   {
+@@ -646,12 +649,6 @@
+         SET_OPTION(CURLOPT_PROXYUSERPWD, proxyuserpwd.c_str());
+     }
+   }
+-  else
+-  {
+-#if LIBCURL_VERSION_NUMBER >= 0x071904
+-      SET_OPTION(CURLOPT_NOPROXY, "*");
+-#endif
+-  }
+   /** Speed limits */
+   if ( _settings.minDownloadSpeed() != 0 )
+diff -uNr libzypp-9.8.3/zypp/parser/RepoFileReader.cc libzypp-9.8.3.new/zypp/parser/RepoFileReader.cc
+--- libzypp-9.8.3/zypp/parser/RepoFileReader.cc        2011-07-04 22:04:11.000000000 +0800
++++ libzypp-9.8.3.new/zypp/parser/RepoFileReader.cc    2011-07-07 14:49:16.916746628 +0800
+@@ -90,7 +90,8 @@
+                url.setQueryParam("proxy", what[1]);
+                url.setQueryParam("proxyport", what[2]);
+               }
+-            }
++            } else
++              url.setQueryParam("proxy", "_none_");
+           } else
+             ERR << "Unknown attribute in [" << *its << "]: " << it->second << " ignored" << endl;
+         }
diff --git a/packaging/MeeGo-Add-Rpm-Checker.patch b/packaging/MeeGo-Add-Rpm-Checker.patch
new file mode 100644 (file)
index 0000000..0bd6b87
--- /dev/null
@@ -0,0 +1,52 @@
+diff -uNr libzypp-8.10.2/zypp/target/TargetImpl.cc libzypp-8.10.2.new/zypp/target/TargetImpl.cc
+--- libzypp-8.10.2/zypp/target/TargetImpl.cc   2011-03-17 11:45:57.403456661 +0800
++++ libzypp-8.10.2.new/zypp/target/TargetImpl.cc       2011-03-17 11:45:03.891768999 +0800
+@@ -38,12 +38,14 @@
+ #include "zypp/ExternalProgram.h"
+ #include "zypp/Repository.h"
++#include "zypp/FileChecker.h"
+ #include "zypp/ResFilters.h"
+ #include "zypp/HistoryLog.h"
+ #include "zypp/target/TargetImpl.h"
+ #include "zypp/target/TargetCallbackReceiver.h"
+ #include "zypp/target/rpm/librpmDb.h"
+ #include "zypp/target/CommitPackageCache.h"
++#include "zypp/target/rpm/RpmDb.h"
+ #include "zypp/parser/ProductFileReader.h"
+@@ -1301,6 +1303,22 @@
+             try
+             {
++              if ( !policy_r.rpmNoSignature() ) 
++              {
++                target::rpm::RpmDb::checkPackageResult res;
++                Target_Ptr target( getZYpp()->getTarget() );
++                res = target->rpmDb().checkPackage(localfile);
++
++                if (res == target::rpm::RpmDb::CHK_OK)
++                  MIL << "Signature is OK" <<endl;
++                else if (res == target::rpm::RpmDb::CHK_NOTFOUND)
++                  ZYPP_THROW( FileCheckException( localfile.value().asString() + " Signature is unknown type" ) );
++                else if (res == target::rpm::RpmDb::CHK_FAIL)
++                  ZYPP_THROW( FileCheckException( localfile.value().asString() + " Signature does not verify" ) );
++                else if (res == target::rpm::RpmDb::CHK_NOKEY)
++                  ZYPP_THROW( FileCheckException( localfile.value().asString() + " Public key is unavailable" ) );
++              }
++
+               progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
+               rpm().installPackage( localfile, flags );
+               HistoryLog().install(*it);
+@@ -1319,6 +1337,10 @@
+                 success = true;
+               }
+             }
++            catch ( const FileCheckException &e )
++            {
++              ZYPP_THROW(e);
++            }
+             catch ( Exception & excpt_r )
+             {
+               ZYPP_CAUGHT(excpt_r);
diff --git a/packaging/MeeGo-dont-use-multcurl-by-default.patch b/packaging/MeeGo-dont-use-multcurl-by-default.patch
new file mode 100644 (file)
index 0000000..0c2e031
--- /dev/null
@@ -0,0 +1,12 @@
+diff -uNr libzypp-8.10.2/zypp/media/MediaAccess.cc libzypp-8.10.2.new/zypp/media/MediaAccess.cc
+--- libzypp-8.10.2/zypp/media/MediaAccess.cc   2010-12-11 01:09:04.000000000 +0800
++++ libzypp-8.10.2.new/zypp/media/MediaAccess.cc       2011-03-02 14:12:42.530519100 +0800
+@@ -137,7 +137,7 @@
+     {
+         // Another good idea would be activate MediaAria2c handler via external var
+         bool use_aria = false;
+-        bool use_multicurl = true;
++        bool use_multicurl = false;
+         string urlmediahandler ( url.getQueryParam("mediahandler") );
+         if ( urlmediahandler == "multicurl" )
+         {
diff --git a/packaging/MeeGo-patch-readd-thumb-arch-definitions.patch b/packaging/MeeGo-patch-readd-thumb-arch-definitions.patch
new file mode 100644 (file)
index 0000000..8713cfc
--- /dev/null
@@ -0,0 +1,87 @@
+From 2743cd0c6f2435e7fde1e4fe686aaf98e51edeb6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jan-Simon=20M=C3=B6ller?= <jsmoeller@linuxfoundation.org>
+Date: Tue, 7 Jun 2011 22:42:54 +0200
+Subject: [PATCH] [patch] readd thumb arch definitions
+
+---
+ zypp/Arch.cc                          |    6 ++++++
+ zypp/Arch.h                           |    4 ++++
+ zypp/parser/yum/schema/common-inc.rnc |    2 ++
+ zypp/parser/yum/schema/common-inc.rng |    2 ++
+ 4 files changed, 14 insertions(+), 0 deletions(-)
+
+diff --git a/zypp/Arch.cc b/zypp/Arch.cc
+index 6b5eeb0..8f735c9 100644
+--- a/zypp/Arch.cc
++++ b/zypp/Arch.cc
+@@ -187,6 +187,8 @@ namespace zypp
+     DEF_BUILTIN( sparcv8 );
+     DEF_BUILTIN( sparc );
++    DEF_BUILTIN( armv7tnhl );
++    DEF_BUILTIN( armv7thl );
+     DEF_BUILTIN( armv7nhl );
+     DEF_BUILTIN( armv7hl );
+     DEF_BUILTIN( armv7l );
+@@ -321,6 +323,8 @@ namespace zypp
+         defCompatibleWith( _armv7l,   _noarch,_armv3l,_armv4l,_armv4tl,_armv5l,_armv5tel,_armv5tejl,_armv6l );
+         defCompatibleWith( _armv7hl,    _noarch );
+         defCompatibleWith( _armv7nhl,   _noarch, _armv7hl );
++        defCompatibleWith( _armv7thl, _noarch,_armv7hl );
++        defCompatibleWith( _armv7tnhl,        _noarch,_armv7hl,_armv7thl,_armv7nhl );
+         //
+         defCompatibleWith( _sh3,      _noarch );
+         //
+@@ -426,6 +430,8 @@ namespace zypp
+   const Arch Arch_sparcv8( _sparcv8 );
+   const Arch Arch_sparc( _sparc );
++  const Arch Arch_armv7tnhl( _armv7tnhl );
++  const Arch Arch_armv7thl( _armv7thl );
+   const Arch Arch_armv7nhl ( _armv7nhl );
+   const Arch Arch_armv7hl ( _armv7hl );
+   const Arch Arch_armv7l( _armv7l );
+diff --git a/zypp/Arch.h b/zypp/Arch.h
+index 6b18a6e..c55aca0 100644
+--- a/zypp/Arch.h
++++ b/zypp/Arch.h
+@@ -222,6 +222,10 @@ namespace zypp
+   extern const Arch Arch_sparc;
+   /** \relates Arch */
++  extern const Arch Arch_armv7tnhl;
++  /** \relates Arch */
++  extern const Arch Arch_armv7thl;
++  /** \relates Arch */
+   extern const Arch Arch_armv7nhl;
+   /** \relates Arch */
+   extern const Arch Arch_armv7hl;
+diff --git a/zypp/parser/yum/schema/common-inc.rnc b/zypp/parser/yum/schema/common-inc.rnc
+index f12ac3b..fcf146f 100644
+--- a/zypp/parser/yum/schema/common-inc.rnc
++++ b/zypp/parser/yum/schema/common-inc.rnc
+@@ -33,6 +33,8 @@ private.archenum = "noarch"
+             | "armv7nhl"
+             | "armv7el"
+             | "armv7l"
++            | "armv7thl"
++            | "armv7tnhl"
+             | "athlon"
+             | "i386"
+             | "i486"
+diff --git a/zypp/parser/yum/schema/common-inc.rng b/zypp/parser/yum/schema/common-inc.rng
+index 60e5742..fe5aeed 100644
+--- a/zypp/parser/yum/schema/common-inc.rng
++++ b/zypp/parser/yum/schema/common-inc.rng
+@@ -96,6 +96,8 @@
+       <value>armv7nhl</value>
+       <value>armv7el</value>
+       <value>armv7l</value>
++      <value>armv7thl</value>
++      <value>armv7tnhl</value>
+       <value>athlon</value>
+       <value>i386</value>
+       <value>i486</value>
+-- 
+1.7.5.3
+
diff --git a/packaging/MeeGo-resume-download.patch b/packaging/MeeGo-resume-download.patch
new file mode 100644 (file)
index 0000000..f807418
--- /dev/null
@@ -0,0 +1,241 @@
+diff -uNr libzypp-8.10.2/zypp/Fetcher.cc libzypp-8.10.2.new/zypp/Fetcher.cc
+--- libzypp-8.10.2/zypp/Fetcher.cc     2011-03-02 14:25:34.650394373 +0800
++++ libzypp-8.10.2.new/zypp/Fetcher.cc 2011-02-26 15:41:36.414440092 +0800
+@@ -550,13 +550,19 @@
+       // try to get the file from the net
+       try
+       {
+-        Pathname tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, deltafile );
+-
+         Pathname dest_full_path = dest_dir + resource.filename();
++        Pathname tmp_file;
+         if ( assert_dir( dest_full_path.dirname() ) != 0 )
+               ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
+-        if ( filesystem::hardlinkCopy( tmp_file, dest_full_path ) != 0 )
++
++        if ( deltafile.asString() == "" )
++          tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, dest_full_path );
++        else 
++        {
++          tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, deltafile );
++        }
++        if ( ! PathInfo(dest_full_path).isExist() && filesystem::hardlinkCopy( tmp_file, dest_full_path ) != 0 )
+         {
+           if ( ! PathInfo(tmp_file).isExist() )
+               ERR << tmp_file << " does not exist" << endl;
+diff -uNr libzypp-8.10.2/zypp/media/MediaCurl.cc libzypp-8.10.2.new/zypp/media/MediaCurl.cc
+--- libzypp-8.10.2/zypp/media/MediaCurl.cc     2011-03-02 14:25:34.651393897 +0800
++++ libzypp-8.10.2.new/zypp/media/MediaCurl.cc 2011-03-02 14:24:29.578519071 +0800
+@@ -34,6 +34,7 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/mount.h>
++#include <sys/stat.h>
+ #include <errno.h>
+ #include <dirent.h>
+ #include <unistd.h>
+@@ -1188,37 +1189,63 @@
+ void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+ {
+     Pathname dest = target.absolutename();
++    string destNew;
++    FILE *file = NULL;
+     if( assert_dir( dest.dirname() ) )
+     {
+       DBG << "assert_dir " << dest.dirname() << " failed" << endl;
+       Url url(getFileUrl(filename));
+       ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
+     }
+-    string destNew = target.asString() + ".new.zypp.XXXXXX";
+-    char *buf = ::strdup( destNew.c_str());
+-    if( !buf)
++    if ( deltafile().asString() != "" )
+     {
+-      ERR << "out of memory for temp file name" << endl;
+-      Url url(getFileUrl(filename));
+-      ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
+-    }
++      destNew = deltafile().asString();
++      Pathname target_path(destNew.c_str());
++      struct stat file_info;
++      curl_off_t file_off = -1 ;
++
++      file = ::fopen( destNew.c_str(), "ab+" );
++      if ( !file ) {
++        filesystem::unlink( destNew );
++        ERR << "fopen failed for file '" << destNew << "'" << endl;
++        ZYPP_THROW(MediaWriteException(destNew));
++      }
++      if ( stat(destNew.c_str(), &file_info) == 0 )
++      {
++         file_off = file_info.st_size;
++         curl_easy_setopt(_curl, CURLOPT_RESUME_FROM_LARGE, file_off);
++      }
+-    int tmp_fd = ::mkstemp( buf );
+-    if( tmp_fd == -1)
++    }
++    else
+     {
++      destNew = target.asString() + ".new.zypp.XXXXXX";
++      char *buf = ::strdup( destNew.c_str());
++      if( !buf)
++      {
++        ERR << "out of memory for temp file name" << endl;
++        Url url(getFileUrl(filename));
++        ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
++      }
++
++  
++      int tmp_fd = ::mkstemp( buf );
++      if( tmp_fd == -1)
++      {
++        free( buf);
++        ERR << "mkstemp failed for file '" << destNew << "'" << endl;
++        ZYPP_THROW(MediaWriteException(destNew));
++      }
++      destNew = buf;
+       free( buf);
+-      ERR << "mkstemp failed for file '" << destNew << "'" << endl;
+-      ZYPP_THROW(MediaWriteException(destNew));
+-    }
+-    destNew = buf;
+-    free( buf);
+-    FILE *file = ::fdopen( tmp_fd, "w" );
+-    if ( !file ) {
+-      ::close( tmp_fd);
+-      filesystem::unlink( destNew );
+-      ERR << "fopen failed for file '" << destNew << "'" << endl;
+-      ZYPP_THROW(MediaWriteException(destNew));
++      file = ::fdopen( tmp_fd, "w" );
++      if ( !file ) {
++        ::close( tmp_fd);
++        filesystem::unlink( destNew );
++        ERR << "fopen failed for file '" << destNew << "'" << endl;
++        ZYPP_THROW(MediaWriteException(destNew));
++      }
+     }
+     DBG << "dest: " << dest << endl;
+@@ -1282,7 +1309,7 @@
+         ZYPP_THROW(MediaWriteException(destNew));
+       }
+       // move the temp file into dest
+-      if ( rename( destNew, dest ) != 0 ) {
++    if ( deltafile().asString() == "" && rename( destNew, dest ) != 0 ) {
+         ERR << "Rename failed" << endl;
+         ZYPP_THROW(MediaWriteException(dest));
+       }
+diff -uNr libzypp-8.10.2/zypp/media/MediaMultiCurl.cc libzypp-8.10.2.new/zypp/media/MediaMultiCurl.cc
+--- libzypp-8.10.2/zypp/media/MediaMultiCurl.cc        2011-03-02 14:25:34.652393815 +0800
++++ libzypp-8.10.2.new/zypp/media/MediaMultiCurl.cc    2011-02-26 15:41:36.432439678 +0800
+@@ -14,6 +14,7 @@
+ #include <sys/types.h>
+ #include <signal.h>
+ #include <sys/wait.h>
++#include <sys/stat.h>
+ #include <netdb.h>
+ #include <arpa/inet.h>
+@@ -1195,37 +1196,61 @@
+ void MediaMultiCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+ {
+   Pathname dest = target.absolutename();
++  string destNew;
++  FILE *file = NULL;
+   if( assert_dir( dest.dirname() ) )
+   {
+     DBG << "assert_dir " << dest.dirname() << " failed" << endl;
+     Url url(getFileUrl(filename));
+     ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
+   }
+-  string destNew = target.asString() + ".new.zypp.XXXXXX";
+-  char *buf = ::strdup( destNew.c_str());
+-  if( !buf)
++  if ( deltafile().asString() != "" )
+   {
+-    ERR << "out of memory for temp file name" << endl;
+-    Url url(getFileUrl(filename));
+-    ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
++    destNew = deltafile().asString();
++    Pathname target_path(destNew.c_str());
++    struct stat file_info;
++    curl_off_t file_off = -1 ;
++
++    file = ::fopen( destNew.c_str(), "ab+" );
++    if ( !file ) {
++      filesystem::unlink( destNew );
++      ERR << "fopen failed for file '" << destNew << "'" << endl;
++      ZYPP_THROW(MediaWriteException(destNew));
++    }
++    if ( stat(destNew.c_str(), &file_info) == 0 )
++    {
++       file_off = file_info.st_size;
++       curl_easy_setopt(_curl, CURLOPT_RESUME_FROM_LARGE, file_off);
++    }
+   }
+-
+-  int tmp_fd = ::mkstemp( buf );
+-  if( tmp_fd == -1)
++  else
+   {
++    destNew = target.asString() + ".new.zypp.XXXXXX";
++    char *buf = ::strdup( destNew.c_str());
++    if( !buf)
++    {
++      ERR << "out of memory for temp file name" << endl;
++      Url url(getFileUrl(filename));
++      ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
++    }
++
++    int tmp_fd = ::mkstemp( buf );
++    if( tmp_fd == -1)
++    {
++      free( buf);
++      ERR << "mkstemp failed for file '" << destNew << "'" << endl;
++      ZYPP_THROW(MediaWriteException(destNew));
++    }
++    destNew = buf;
+     free( buf);
+-    ERR << "mkstemp failed for file '" << destNew << "'" << endl;
+-    ZYPP_THROW(MediaWriteException(destNew));
+-  }
+-  destNew = buf;
+-  free( buf);
+-  FILE *file = ::fdopen( tmp_fd, "w" );
+-  if ( !file ) {
+-    ::close( tmp_fd);
+-    filesystem::unlink( destNew );
+-    ERR << "fopen failed for file '" << destNew << "'" << endl;
+-    ZYPP_THROW(MediaWriteException(destNew));
++    file = ::fdopen( tmp_fd, "w" );
++    if ( !file ) {
++      ::close( tmp_fd);
++      filesystem::unlink( destNew );
++      ERR << "fopen failed for file '" << destNew << "'" << endl;
++      ZYPP_THROW(MediaWriteException(destNew));
++    }
+   }
+   DBG << "dest: " << dest << endl;
+   DBG << "temp: " << destNew << endl;
+@@ -1241,8 +1266,6 @@
+     curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+     curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+   }
+-  // change header to include Accept: metalink
+-  curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _customHeadersMetalink);
+   try
+     {
+       MediaCurl::doGetFileCopyFile(filename, dest, file, report, options);
+@@ -1358,7 +1381,7 @@
+       ERR << "Fclose failed for file '" << destNew << "'" << endl;
+       ZYPP_THROW(MediaWriteException(destNew));
+     }
+-  if ( rename( destNew, dest ) != 0 )
++  if ( deltafile().asString() == "" && rename( destNew, dest ) != 0 )
+     {
+       ERR << "Rename failed" << endl;
+       ZYPP_THROW(MediaWriteException(dest));
diff --git a/packaging/MeeGo-use-fullname-in-search_deltafile.patch b/packaging/MeeGo-use-fullname-in-search_deltafile.patch
new file mode 100644 (file)
index 0000000..71ece84
--- /dev/null
@@ -0,0 +1,31 @@
+diff -uNr libzypp-8.12.1/zypp/repo/yum/Downloader.cc libzypp-8.12.1.new/zypp/repo/yum/Downloader.cc
+--- libzypp-8.12.1/zypp/repo/yum/Downloader.cc 2011-02-14 21:47:33.000000000 +0800
++++ libzypp-8.12.1.new/zypp/repo/yum/Downloader.cc     2011-04-29 11:42:03.477581524 +0800
+@@ -64,9 +64,6 @@
+   if (!PathInfo(dir).isDir())
+     return deltafile;
+   string base = file.basename();
+-  size_t hypoff = base.find("-");
+-  if (hypoff != string::npos)
+-    base.replace(0, hypoff + 1, "");
+   size_t basesize = base.size();
+   std::list<Pathname> retlist;
+   if (!filesystem::readdir(retlist, dir, false))
+@@ -86,7 +83,7 @@
+ {
+   OnMediaLocation loc_with_path(loc_with_path_prefix(loc, repoInfo().path()));
+   MIL << id << " : " << loc_with_path << endl;
+-  this->enqueueDigested(loc_with_path,  FileChecker(), search_deltafile(_delta_dir + "repodata", loc.filename()));
++  this->enqueueDigested(loc_with_path,  FileChecker());
+   return true;
+ }
+@@ -110,7 +107,7 @@
+     return true;
+   }
+-  this->enqueueDigested(loc_with_path, FileChecker(), search_deltafile(_delta_dir + "repodata", loc.filename()));
++  this->enqueueDigested(loc_with_path, FileChecker());
+   // We got a patches file we need to read, to add patches listed
+   // there, so we transfer what we have in the queue, and
diff --git a/packaging/docs.patch b/packaging/docs.patch
new file mode 100644 (file)
index 0000000..564807a
--- /dev/null
@@ -0,0 +1,27 @@
+Index: libzypp-9.8.3/CMakeLists.txt
+===================================================================
+--- libzypp-9.8.3.orig/CMakeLists.txt
++++ libzypp-9.8.3/CMakeLists.txt
+@@ -160,12 +160,6 @@ ELSE ( NOT LIBPROXY_FOUND )
+   ADD_DEFINITIONS(-D_WITH_LIBPROXY_SUPPORT_)
+ ENDIF( NOT LIBPROXY_FOUND )
+-FIND_PROGRAM( DOXYGEN doxygen )
+-IF ( NOT DOXYGEN )
+-  MESSAGE( FATAL_ERROR "doxygen not found: install doxygen to build the documentation." )
+-ELSE ( NOT DOXYGEN )
+-  MESSAGE( STATUS "doxygen found: ${DOXYGEN}" )
+-ENDIF ( NOT DOXYGEN )
+ MESSAGE(STATUS "soname: ${LIBZYPP_VERSION_INFO}")
+ MESSAGE(STATUS "version: ${VERSION}")
+@@ -204,9 +198,5 @@ ADD_SUBDIRECTORY( devel EXCLUDE_FROM_ALL
+ ADD_SUBDIRECTORY( tools )
+ ADD_SUBDIRECTORY( examples )
+ ADD_SUBDIRECTORY( po EXCLUDE_FROM_ALL )
+-ADD_SUBDIRECTORY( doc )
+ ADD_SUBDIRECTORY( vendor )
+-ADD_SUBDIRECTORY( tests EXCLUDE_FROM_ALL )
+-INCLUDE(CTest)
+-ENABLE_TESTING()
diff --git a/packaging/libzypp-6.29.2-meego.patch b/packaging/libzypp-6.29.2-meego.patch
new file mode 100644 (file)
index 0000000..a656c90
--- /dev/null
@@ -0,0 +1,37 @@
+--- a/tools/CMakeLists.txt
++++ b/tools/CMakeLists.txt
+@@ -1,5 +1,5 @@
+-ADD_SUBDIRECTORY( package-manager )
++##ADD_SUBDIRECTORY( package-manager )
+ INSTALL( FILES notify-message DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/zypp" )
+--- a/zypp.conf
++++ b/zypp.conf
+@@ -217,6 +217,7 @@
+ ##                    some sane default.
+ ##
+ ## commit.downloadMode =
++commit.downloadMode = DownloadInAdvance
+ ##
+ ## Defining directory which contains vendor description files.
+@@ -326,6 +327,7 @@
+ ##    empty
+ ##
+ # multiversion = provides:multiversion(kernel)
++multiversion = kernel,kernel-adaptation-connext,kernel-adaptation-intel-automotive,kernel-adaptation-medfield,kernel-adaptation-mrst,kernel-adaptation-oaktrail
+ ##
+ ## Path to locks file. If not exist then is create.
+--- a/zypp/CMakeLists.txt
++++ b/zypp/CMakeLists.txt
+@@ -3,6 +3,7 @@
+ ####################################################################
+ ADD_DEFINITIONS(-DLOCALEDIR="${CMAKE_INSTALL_PREFIX}/share/locale" -DTEXTDOMAIN="zypp" )
++ADD_DEFINITIONS(-DNO_HAL)
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+ #FILE(WRITE filename "message to write"... )
diff --git a/packaging/libzypp-log-issue-bug704.patch b/packaging/libzypp-log-issue-bug704.patch
new file mode 100644 (file)
index 0000000..d60cb9c
--- /dev/null
@@ -0,0 +1,15 @@
+diff --git a/zypp/KeyRing.cc b/zypp/KeyRing.cc
+index 44d253a..cafd1e4 100644
+--- a/zypp/KeyRing.cc
++++ b/zypp/KeyRing.cc
+@@ -613,7 +613,9 @@ namespace zypp
+     str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
+     string id;
+-    for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
++    for(line = prog.receiveLine(), count=0;
++      !line.empty() && id.empty() && count < 100;
++      line = prog.receiveLine(), count++ )
+     {
+       //MIL << "[" << line << "]" << endl;
+       str::smatch what;
diff --git a/packaging/libzypp-meego-release.patch b/packaging/libzypp-meego-release.patch
new file mode 100644 (file)
index 0000000..6b5f613
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/zypp/ZConfig.cc
++++ b/zypp/ZConfig.cc
+@@ -818,7 +818,7 @@ namespace zypp
+   ///////////////////////////////////////////////////////////////////
+   std::string ZConfig::distroverpkg() const
+-  { return "redhat-release"; }
++  { return "meego-release"; }
+   ///////////////////////////////////////////////////////////////////
diff --git a/packaging/libzypp-rpmlintrc b/packaging/libzypp-rpmlintrc
new file mode 100644 (file)
index 0000000..4aa496b
--- /dev/null
@@ -0,0 +1 @@
+addFilter("libzypp.* shlib-policy-name-error")
diff --git a/packaging/libzypp.changes b/packaging/libzypp.changes
new file mode 100644 (file)
index 0000000..9754eb7
--- /dev/null
@@ -0,0 +1,146 @@
+* Fri Jul 22 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 9.8.3
+- Add meego-try-again-while-downloading-fails.patch to fix BMC#21712 
+
+* Thu Jul  7 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 9.8.3
+- no_proxy support for individual repo
+
+* Wed Jul 06 2011 Anas Nashif <anas.nashif@intel.com> - 9.8.3
+- Temporary fix for bmc #19659: Architecture is not being set correctly
+
+* Tue Jul 05 2011 Anas Nashif <anas.nashif@intel.com> - 9.8.3
+- Update to 9.8.3
+
+* Tue Jun 07 2011 Jan-Simon Möller <jsmoeller@linuxfoundation.org> - 9.1.2
+- Readd thumb arch names
+
+* Mon May 23 2011 Anas Nashif <anas.nashif@intel.com> - 9.1.2
+- Update to 9.1.2 with support for rpm 4.9.0
+
+* Wed May 18 2011 Jan-Simon Möller <jsmoeller@linuxfoundation.org> - 8.12.1
+- Add missing const Arch Arch_armv* entries. BMC#12930 .
+
+* Fri Apr 29 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 8.12.1
+- Update MeeGo-use-fullname-in-search_deltafile.patch to fix BMC#14928
+- Add 0001-Fix-build-with-alternative-libproxy-implementations-.patch
+  to building with libproxy support, fix BMC#16869
+
+* Tue Apr 19 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 8.12.1
+- Update to 8.12.1, and drop patches already in upstream.
+
+* Mon Mar 21 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 8.10.2
+- Update libzypp-6.29.2-meego.patch to update the multiversion
+  package name with the current kernel names, fix BMC#14746
+
+* Wed Mar 16 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 8.10.2
+- Add MeeGo-use-fullname-in-search_deltafile.patch to use full name
+  while search delta file. Fix BMC#14209.
+- Add MeeGo-Add-Rpm-Checker.patch to check signature of rpm packages
+  before installing packages, and FileCheckerException is throw and 
+  details is controlled by PackageKit. This patch is necessary to fix
+  BMC#3622.
+
+* Wed Mar  2 2011 Zhang Qiang <qiang.z.zhang@intel.com> - 8.10.2
+- Update MeeGo-resume-download.patch to let single curl support resume.
+- MeeGo-dont-use-multcurl-by-default.patch to use single curl download
+  by default, as percentage progress is not supported well in multcurl
+  fix BMC#13980
+
+* Wed Feb 02 2011 Marko Saukko <marko.saukko@cybercom.com> - 8.10.2
+- Added patch for armv7tnhl and armv7thl support. BMC#12624
+
+* Thu Dec 30 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.10.2
+- Update MeeGo-resume-download.patch to resolve issue when installing 
+  local rpm, fix BMC#11827.
+
+* Wed Dec 22 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.10.2
+- Update to 8.10.2, and drop patches already in upstream. BMC#11604
+- Add MeeGo-support-none-proxy-from-repo.patch to fix a bug in 
+  using none proxy, without this patch, zypper cant use env proxy.
+
+* Tue Dec 21 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.1.0
+- Update MeeGo-Add-armv7hl-and-armv7nhl-architecture.patch to fix
+  the compatible issue, fix BMC#11484.
+
+* Mon Dec 20 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.1.0
+- Add MeeGo-resume-download.patch to support resume/re-start 
+  download packages, this patch can support to implement FEA#9355
+- Add MeeGo-Add-armv7hl-and-armv7nhl-architecture.patch to support 
+  two arm architecture, fix BMC#11484
+
+* Sat Nov  6 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.1.0
+- Add a patch meego-check-products-dir-while-using-rpmdb2solv.patch
+  to check the products dir (/etc/products.d) while using rpmdb2solv
+  to create rpmdb solv file, fix BMC#9099
+- Update meego-enable-proxy.patch, to add removeQueryParam() in class
+  Url, which can used to remove proxy Param while it's useless. 
+  also use regex to parse the proxy info in repo file
+
+* Mon Sep 13 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.1.0
+- Add a patch to implement enable/disable proxy for special repo
+  just as we used in yum repo file, fix BMC#6614.
+
+* Mon Sep 06 2010 Anas Nashif <nashif@linux.intel.com> - 8.1.0
+- Fixed bug #5599: Use gpg2 instead of gpg to verify keys
+
+* Sat Sep 04 2010 Anas Nashif <nashif@linux.intel.com> - 8.1.0
+- Depend on gnupg2
+- Do not depend on aria2 anymore, we use curl
+
+* Mon Aug 23 2010 Zhang Qiang <qiang.z.zhang@intel.com> - 8.1.0
+- Update to 8.1.0
+
+* Tue Aug 17 2010 Peter J Zhu <peter.j.zhu@intel.com> - 7.7.2
+- revert to depend on gnupg again, workround for BMC#5283
+
+* Mon Aug  9 2010 Yan Li <yan.i.li@intel.com> - 7.7.2
+- Replace gnupg with gnupg2
+
+* Thu Jun 17 2010 Anas Nashif <anas.nashif@intel.com> - 7.7.2
+- Update to version 7.7.2
+
+* Sun Apr 28 2010 Michael Meek <michael.meeks@novell.com> - 6.31.0
+- Add patch to workaround an infinite sized log blow-out.
+
+* Sun Mar 21 2010 Anas Nashif <anas.nashif@intel.com> - 6.31.0
+- Update to 6.31.0
+
+* Tue Mar 16 2010 michael.meeks@novell.com - 6.30.4
+- remove bogus C++ workaround, was a prelink bug - cf. prelink patch.
+
+* Tue Mar 16 2010 Anas Nashif <anas.nashif@intel.com> - 6.30.4
+- Exclude from prelink
+
+* Sat Mar 13 2010 michael.meeks@novell.com - 6.30.4
+- Work around a C++ compiler bug - bogus non-functioning 'private'
+
+* Sat Mar 06 2010 Anas Nashif <anas.nashif@intel.com> - 6.30.4
+- Update to 6.30.4
+- Remove upstreamed patches
+
+* Fri Feb 26 2010 Anas Nashif <anas.nashif@intel.com> - 6.29.4
+- Update tp 6.29.4
+- libzypp-do_not_rebuild_db.patch: Do not rebuild database
+
+* Wed Feb 17 2010 Anas Nashif <anas.nashif@intel.com> - 6.29.2
+- Update to 6.29.2
+
+* Fri Jan 22 2010 Anas Nashif <anas.nashif@intel.com> - 6.21.2
+- Do not install package-manager app
+
+* Tue Jan 19 2010 Peter Zhu <peter.j.zhu@intel.com> - 6.21.2
+- Update to 6.21.2
+
+* Tue Nov 17 2009 Peter Zhu <peter.j.zhu@intel.com> - 6.17.0
+- disable hal of libzypp we don't need support DVD/CD repo in Moblin
+
+* Mon Oct 05 2009 Anas Nashif <anas.nashif@intel.com> - 6.17.0
+- Cleanup spec file
+
+* Wed Sep 30 2009 Anas Nashif <anas.nashif@intel.com> - 6.17.0
+- Remove suse specific macro
+
+* Wed Sep 30 2009 Anas Nashif <anas.nashif@intel.com> - 6.17.0
+- Moblin specific config
+
+* Tue Sep 29 2009 Anas Nashif <anas.nashif@intel.com> - 6.17.0
+- Update to 6.17.0
diff --git a/packaging/libzypp.conf b/packaging/libzypp.conf
new file mode 100644 (file)
index 0000000..dca50f0
--- /dev/null
@@ -0,0 +1 @@
+-b /usr/lib/libzypp.so.*
diff --git a/packaging/libzypp.spec b/packaging/libzypp.spec
new file mode 100644 (file)
index 0000000..18a7028
--- /dev/null
@@ -0,0 +1,229 @@
+%define run_testsuite 0
+Name:           libzypp
+License:        GPL v2 or later
+Group:          System/Packages
+AutoReqProv:    on
+Summary:        Package, Patch, Pattern, and Product Management
+Version:        9.8.3
+Release:        1
+Source:         %{name}-%{version}.tar.bz2
+Source1:        %{name}-rpmlintrc
+Source2:        libzypp.conf
+BuildRequires:  cmake
+BuildRequires:  libudev-devel
+BuildRequires:  libsatsolver-devel >= 0.14.9
+BuildRequires:  openssl-devel
+BuildRequires:  boost-devel curl-devel 
+#BuildRequires:  dejagnu doxygen  graphviz
+BuildRequires:  gcc-c++ gettext-devel libxml2-devel
+# required for testsuite, webrick
+#BuildRequires:  ruby
+BuildRequires:  expat-devel
+BuildRequires:  glib2-devel popt-devel rpm-devel
+BuildRequires:  pkgconfig(dbus-glib-1)
+#BuildRequires:  pkgconfig(libproxy-1.0)
+#Requires:       gnupg2
+Requires:       satsolver-tools
+
+Patch0:        libzypp-6.29.2-meego.patch
+Patch1:        libzypp-log-issue-bug704.patch
+Patch2:         libzypp-meego-release.patch
+Patch3:                use_gpg2.patch
+Patch5:         meego-check-products-dir-while-using-rpmdb2solv.patch
+Patch6:         MeeGo-resume-download.patch
+Patch10:        MeeGo-dont-use-multcurl-by-default.patch
+Patch11:        MeeGo-Add-Rpm-Checker.patch
+Patch12:        MeeGo-use-fullname-in-search_deltafile.patch
+Patch13:        MeeGo-patch-readd-thumb-arch-definitions.patch
+Patch14:        linker.patch
+Patch15:       0001-Disable-proxy-only-if-_none_-is-set-in-repo-file.patch 
+Patch16:        meego-try-again-while-downloading-fails.patch
+Patch17:       docs.patch
+
+%description
+Package, Patch, Pattern, and Product Management
+
+Authors:
+--------
+    Michael Andres <ma@suse.de>
+    Jiri Srain <jsrain@suse.cz>
+    Stefan Schubert <schubi@suse.de>
+    Duncan Mac-Vicar <dmacvicar@suse.de>
+    Klaus Kaempf <kkaempf@suse.de>
+    Marius Tomaschewski <mt@suse.de>
+    Stanislav Visnovsky <visnov@suse.cz>
+    Ladislav Slezak <lslezak@suse.cz>
+
+%package devel
+License:        GPL v2 or later
+Requires:       libzypp == %{version}
+Requires:       libxml2-devel curl-devel openssl-devel rpm-devel glibc-devel zlib-devel
+Requires:       bzip2 popt-devel glib2-devel boost-devel libstdc++-devel
+Requires:       pkgconfig(dbus-1)
+Requires:       cmake libsatsolver-devel >= 0.13.0
+Summary:        Package, Patch, Pattern, and Product Management - developers files
+Group:          System/Packages
+Provides:       yast2-packagemanager-devel
+Obsoletes:      yast2-packagemanager-devel
+
+%description -n libzypp-devel
+Package, Patch, Pattern, and Product Management - developers files
+
+Authors:
+--------
+    Michael Andres <ma@suse.de>
+    Jiri Srain <jsrain@suse.cz>
+    Stefan Schubert <schubi@suse.de>
+    Duncan Mac-Vicar <dmacvicar@suse.de>
+    Klaus Kaempf <kkaempf@suse.de>
+    Marius Tomaschewski <mt@suse.de>
+    Stanislav Visnovsky <visnov@suse.cz>
+    Ladislav Slezak <lslezak@suse.cz>
+
+%prep
+%setup -q
+%patch0 -p1 -b .meego
+%patch1 -p1 -b .log-issue
+%patch2 -p1 -b .meego-release
+%patch3 -p1 
+%patch5 -p1
+%patch6 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+
+%build
+mkdir build
+cd build
+export CFLAGS="$RPM_OPT_FLAGS"
+export CXXFLAGS="$CFLAGS"
+
+cmake -DCMAKE_INSTALL_PREFIX=/usr \
+      -DDOC_INSTALL_DIR=%{_docdir} \
+      -DLIB=%{_lib} \
+      -DCMAKE_BUILD_TYPE=Release \
+      -DCMAKE_SKIP_RPATH=1 \
+      ..
+make %{?jobs:-j %jobs} VERBOSE=1
+make -C po %{?jobs:-j %jobs} translations
+%if 0%{?run_testsuite}
+  make -C tests %{?jobs:-j %jobs}
+  pushd tests
+  LD_LIBRARY_PATH=$PWD/../zypp:$LD_LIBRARY_PATH ctest .
+  popd
+%endif
+
+%install
+rm -rf "$RPM_BUILD_ROOT"
+cd build
+make install DESTDIR=$RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/zypp/repos.d
+mkdir -p $RPM_BUILD_ROOT/etc/zypp/services.d
+mkdir -p $RPM_BUILD_ROOT/%{_usr}/lib/zypp
+mkdir -p $RPM_BUILD_ROOT/%{_var}/lib/zypp
+mkdir -p $RPM_BUILD_ROOT/%{_var}/log/zypp
+mkdir -p $RPM_BUILD_ROOT/%{_var}/cache/zypp
+make -C po install DESTDIR=$RPM_BUILD_ROOT
+# Create filelist with translations
+cd ..
+
+install -d %{buildroot}/etc/prelink.conf.d/
+install -m 644 %{SOURCE2} %{buildroot}/etc/prelink.conf.d/
+
+
+%{find_lang} zypp
+
+%post
+/sbin/ldconfig
+if [ -f /var/cache/zypp/zypp.db ]; then rm /var/cache/zypp/zypp.db; fi
+# convert old lock file to new
+# TODO make this a separate file?
+# TODO run the sript only when updating form pre-11.0 libzypp versions
+LOCKSFILE=/etc/zypp/locks
+OLDLOCKSFILE=/etc/zypp/locks.old
+is_old(){
+  # if no such file, exit with false (1 in bash)
+  test -f ${LOCKSFILE} || return 1
+  TEMP_FILE=`mktemp`
+  cat ${LOCKSFILE} | sed '/^\#.*/ d;/.*:.*/d;/^[^[a-zA-Z\*?.0-9]*$/d' > ${TEMP_FILE}
+  if [ -s ${TEMP_FILE} ]
+  then
+    RES=0
+  else
+    RES=1
+  fi
+  rm -f ${TEMP_FILE}
+  return ${RES}
+}
+append_new_lock(){
+  case "$#" in
+    1 )
+  echo "
+solvable_name: $1
+match_type: glob
+" >> ${LOCKSFILE}
+;;
+    2 ) #TODO version
+  echo "
+solvable_name: $1
+match_type: glob
+version: $2
+" >> ${LOCKSFILE}
+;;
+    3 ) #TODO version
+  echo "
+solvable_name: $1
+match_type: glob
+version: $2 $3
+" >> ${LOCKSFILE}
+  ;;
+esac
+}
+die() {
+  echo $1
+  exit 1
+}
+if is_old ${LOCKSFILE}
+  then
+  mv -f ${LOCKSFILE} ${OLDLOCKSFILE} || die "cannot backup old locks"
+  cat ${OLDLOCKSFILE}| sed "/^\#.*/d"| while read line
+  do
+    append_new_lock $line
+  done
+fi
+
+%postun -p /sbin/ldconfig
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+%files -f zypp.lang
+%defattr(-,root,root,-)
+%dir /etc/zypp
+%dir /etc/zypp/repos.d
+%dir /etc/zypp/services.d
+%config(noreplace) /etc/zypp/zypp.conf
+%config(noreplace) /etc/zypp/systemCheck
+%config(noreplace) %{_sysconfdir}/logrotate.d/zypp-history.lr
+%config(noreplace) /etc/prelink.conf.d/*
+%dir %{_libdir}/zypp
+%{_libdir}/zypp
+%dir %{_var}/lib/zypp
+%dir %{_var}/log/zypp
+%dir %{_var}/cache/zypp
+/usr/share/zypp
+/usr/bin/*
+%{_libdir}/libzypp*so.*
+
+%files devel
+%defattr(-,root,root,-)
+%{_libdir}/libzypp.so
+%dir /usr/include/zypp
+/usr/include/zypp/*
+/usr/share/cmake/Modules/*
+%{_libdir}/pkgconfig/libzypp.pc
\ No newline at end of file
diff --git a/packaging/linker.patch b/packaging/linker.patch
new file mode 100644 (file)
index 0000000..3f32a33
--- /dev/null
@@ -0,0 +1,12 @@
+Index: libzypp-9.8.3/zypp/CMakeLists.txt
+===================================================================
+--- libzypp-9.8.3.orig/zypp/CMakeLists.txt
++++ libzypp-9.8.3/zypp/CMakeLists.txt
+@@ -917,6 +917,7 @@ SET_LOGGROUP( "satsolver" ${zypp_sat_SRC
+ #SET_LOGGROUP( "group" ${zypp_pool_SRCS} )
++list( REVERSE zypp_lib_SRCS )
+ ADD_LIBRARY(zypp SHARED ${zypp_lib_SRCS})
+ SET_TARGET_PROPERTIES( zypp PROPERTIES VERSION "${LIBZYPP_VERSION_INFO}" )
+ SET_TARGET_PROPERTIES( zypp PROPERTIES SOVERSION "${LIBZYPP_SOVERSION_INFO}" )
diff --git a/packaging/meego-check-products-dir-while-using-rpmdb2solv.patch b/packaging/meego-check-products-dir-while-using-rpmdb2solv.patch
new file mode 100644 (file)
index 0000000..800ab0b
--- /dev/null
@@ -0,0 +1,14 @@
+diff -uNr libzypp-8.1.0/zypp/target/TargetImpl.cc libzypp-8.1.0.new/zypp/target/TargetImpl.cc
+--- libzypp-8.1.0/zypp/target/TargetImpl.cc    2010-08-06 00:32:16.000000000 +0800
++++ libzypp-8.1.0.new/zypp/target/TargetImpl.cc        2010-11-06 17:52:02.356580841 +0800
+@@ -785,7 +785,9 @@
+         if ( ! _root.empty() )
+           cmd << " -r '" << _root << "'";
+-        cmd << " -p '" << Pathname::assertprefix( _root, "/etc/products.d" ) << "'";
++        PathInfo productsdir( Pathname::assertprefix( _root, "/etc/products.d" ) );
++        if ( productsdir.isExist() )
++          cmd << " -p '" << productsdir.path() << "'";
+         if ( ! oldSolvFile.empty() )
+           cmd << " '" << oldSolvFile << "'";
diff --git a/packaging/meego-try-again-while-downloading-fails.patch b/packaging/meego-try-again-while-downloading-fails.patch
new file mode 100644 (file)
index 0000000..000dc05
--- /dev/null
@@ -0,0 +1,23 @@
+diff -uNr libzypp-9.8.3/zypp/repo/RepoProvideFile.cc libzypp-9.8.3.new/zypp/repo/RepoProvideFile.cc
+--- libzypp-9.8.3/zypp/repo/RepoProvideFile.cc 2011-07-04 22:04:11.000000000 +0800
++++ libzypp-9.8.3.new/zypp/repo/RepoProvideFile.cc     2011-07-22 16:37:51.059506789 +0800
+@@ -249,6 +249,7 @@
+                                               const ProvideFilePolicy & policy_r )
+     {
+       MIL << loc_r << endl;
++      int count = 0;
+       // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
+       // and redirect download progress triggers to call the ProvideFilePolicy
+       // callback.
+@@ -346,6 +347,11 @@
+           repo_excpt.remember(e);
++          if (count < 3)
++          {
++            count++;
++            --it;
++          }
+           WAR << "Trying next url" << endl;
+           continue;
+         }
diff --git a/packaging/use_gpg2.patch b/packaging/use_gpg2.patch
new file mode 100644 (file)
index 0000000..ac31795
--- /dev/null
@@ -0,0 +1,13 @@
+Index: libzypp-8.1.0/zypp/PublicKey.cc
+===================================================================
+--- libzypp-8.1.0.orig/zypp/PublicKey.cc
++++ libzypp-8.1.0/zypp/PublicKey.cc
+@@ -157,7 +157,7 @@ namespace zypp
+         static filesystem::TmpDir dir;
+         const char* argv[] =
+         {
+-          "gpg",
++          "gpg2",
+           "-v",
+           "--no-default-keyring",
+           "--fixed-list-mode",
diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt
new file mode 100644 (file)
index 0000000..977fdc0
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Creating the .pot file to be translated.
+#
+ADD_CUSTOM_TARGET( zypp.pot
+       COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/createPot ${LIBZYPP_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/zypp.pot
+)
+SET_DIRECTORY_PROPERTIES( PROPERTIES
+       ADDITIONAL_MAKE_CLEAN_FILES zypp.pot
+)
+
+#
+# Provides the 'translations' target that creates the .gmo files
+# out of the pofiles provided by zypp-po.tar.bz2.
+# Use USE_TRANSLATION_SET to use an alternate set provided by
+# ${USE_TRANSLATION_SET}-po.tar.bz2
+#
+GETTEXT_CREATE_TARBALL_TRANSLATIONS( "zypp" )
+
diff --git a/po/createPot b/po/createPot
new file mode 100755 (executable)
index 0000000..1fb1485
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+SOURCE_DIR="${1:-..}"
+POTFILE="${2:-po/zypp.pot}"
+# search for sourcecode-files
+cd "$SOURCE_DIR"
+SRCFILES=`find examples tools zypp \
+                           -type f -name "*.h" \
+                                -o -name "*.pm"  \
+                                -o -name "*.c"   \
+                                -o -name "*.cc"  \
+                                -o -name "*.cpp"`
+#calling xgettext with the sourcefiles
+xgettext -s --no-wrap --add-comments --add-location --keyword=_ --keyword=_:1,2 --keyword=__ --keyword=N_ --foreign-user --copyright-holder="SuSE Linux Products GmbH, Nuernberg" --default-domain=libzypp --output="$POTFILE" $SRCFILES
diff --git a/po/zypp-po.tar.bz2 b/po/zypp-po.tar.bz2
new file mode 100644 (file)
index 0000000..4e37d5e
Binary files /dev/null and b/po/zypp-po.tar.bz2 differ
diff --git a/systemCheck b/systemCheck
new file mode 100644 (file)
index 0000000..4cb60e2
--- /dev/null
@@ -0,0 +1,13 @@
+##
+## This file contains requirements/conflicts which fulfill the 
+## needs of a running system.
+## For example the system would be broken if not glibc or kernel is
+## installed. 
+## So the user will be informed if these packages will be deleted.
+## 
+## format: Each line represents one dependency:
+## e.g.
+## requires:kernel
+## requires:glibc
+
+requires:glibc
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6ba335d
--- /dev/null
@@ -0,0 +1,17 @@
+
+ADD_SUBDIRECTORY(lib)
+
+ENABLE_TESTING()
+INCLUDE_DIRECTORIES( ${LIBZYPP_SOURCE_DIR}/tests/lib )
+
+ADD_DEFINITIONS( -DTESTS_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}" -DTESTS_BUILD_DIR="${CMAKE_CURRENT_BINARY_DIR}" )
+
+ADD_SUBDIRECTORY( media )
+ADD_SUBDIRECTORY( zypp )
+ADD_SUBDIRECTORY( parser )
+ADD_SUBDIRECTORY( repo )
+ADD_SUBDIRECTORY( sat )
+
+ADD_CUSTOM_TARGET( ctest
+   COMMAND ctest -a
+)
diff --git a/tests/README b/tests/README
new file mode 100644 (file)
index 0000000..38a246b
--- /dev/null
@@ -0,0 +1,29 @@
+
+You can find here unit tests for lot of zypp classes.
+
+run
+ctest .
+
+or the binary itself to run just one test
+
+
+Note on libboost_unit_test_framework.so.1.38.0
+----------------------------------------------
+
+When you run the tests manually, some of the testcases here may fail
+with an error message like:
+
+  unknown location(0):            \
+  fatal error in "keyring_test":  \
+  child has exited; pid: 7222; uid: 216; exit value: 2
+
+This happens because the boost test framework we use monitors the
+return code of child processes. This monitoring should be turned off.
+
+You can do this either via a commandfline option:
+
+        ./KeyRing_test --catch_system_errors=no
+
+Or via an environment variable:
+
+        BOOST_TEST_CATCH_SYSTEM_ERRORS=no ./KeyRing_test
diff --git a/tests/data/11.0-update/repodata/deltainfo.xml.gz b/tests/data/11.0-update/repodata/deltainfo.xml.gz
new file mode 100644 (file)
index 0000000..61e6b40
Binary files /dev/null and b/tests/data/11.0-update/repodata/deltainfo.xml.gz differ
diff --git a/tests/data/11.0-update/repodata/primary.xml.gz b/tests/data/11.0-update/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..cb6ecda
Binary files /dev/null and b/tests/data/11.0-update/repodata/primary.xml.gz differ
diff --git a/tests/data/11.0-update/repodata/repomd.xml b/tests/data/11.0-update/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..af651f7
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">c1d8b4a70e4e2ed3cf5d68d27319974cc023cf03</checksum>
+    <timestamp>1224755528</timestamp>
+    <open-checksum type="sha">5c8a77e4e4f57070709f995d2d2ebee13b052ded</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">630e1f5bc7d12bc26b571e35380191835bf9e5e7</checksum>
+    <timestamp>1224755531</timestamp>
+    <open-checksum type="sha">726ac5ccf337abfa42bd6a76d032c3ed7a923ea8</open-checksum>
+  </data>
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">958ea074b3c3ff7c57112e292ff1bceb07c6d682</checksum>
+    <timestamp>1224755535</timestamp>
+    <open-checksum type="sha">4a44c8ef91cefaaaf31070d5556ca94b27525dc9</open-checksum>
+  </data>
+  <data type="deltainfo">
+    <location href="repodata/deltainfo.xml.gz"/>
+    <checksum type="sha">7949794d363b7a84c2e9168649613bdcdf9b2b91</checksum>
+    <timestamp>1224755535</timestamp>
+    <open-checksum type="sha">f26bbdd272a1f51f750b76b67bee6f178e9645e0</open-checksum>
+  </data>
+  <data type="updateinfo">
+    <location href="repodata/updateinfo.xml.gz"/>
+    <checksum type="sha">da21499c94aef90694c8e9d94eec39ab3891cbc8</checksum>
+    <timestamp>1224755535</timestamp>
+    <open-checksum type="sha">7314b359cc9b5c68f22cbe7df2e8cfb964224593</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/data/11.0-update/repodata/repomd.xml.asc b/tests/data/11.0-update/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..768a149
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBJAElRqE7a6JyACsoRAugdAKCImJX9Aop0Z5ngfpYz1Z6ec4637QCg
+kDTnvGTdR3QFlYLXDEnK2UpM3hs=
+=QwmG
+-----END PGP SIGNATURE-----
diff --git a/tests/data/11.0-update/repodata/repomd.xml.key b/tests/data/11.0-update/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..daeef8a
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiAhsDBAsHAwID
+FQIDAxYCAQIeAQIXgAUCSB80OgUJEfKmuQAKCRCoTtronIAKyuJlAJ0cWZifmBO6
+Eh71jattipdMhUYBTwCfSXbJJtuF3c96JPmpmT8be2LDo86IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkgfNGYFCRHyptQACgkQqE7a6JyACsrv3ACbBLhafFXmTjH3
+JJWFJGWuIOaZUosAniPs4feEyN46gjXGgcZc2Ai8nkm6
+=mY6G
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/11.0-update/repodata/updateinfo.xml.gz b/tests/data/11.0-update/repodata/updateinfo.xml.gz
new file mode 100644 (file)
index 0000000..11608e4
Binary files /dev/null and b/tests/data/11.0-update/repodata/updateinfo.xml.gz differ
diff --git a/tests/data/Mirrorlist/remote-site/metalink.xml b/tests/data/Mirrorlist/remote-site/metalink.xml
new file mode 100644 (file)
index 0000000..5df588b
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<metalink version="3.0" xmlns="http://www.metalinker.org/" type="dynamic" pubdate="Wed, 03 Nov 2010 13:30:11 GMT" generator="mirrormanager" xmlns:mm0="http://fedorahosted.org/mirrormanager">
+  <files>
+    <file name="repomd.xml">
+      <mm0:timestamp>1288734483</mm0:timestamp>
+      <size>4834</size>
+      <verification>
+        <hash type="md5">8fd7745c38277ac8b5618107edb72b7e</hash>
+        <hash type="sha1">de06c2b34f5b13fe6029da59475359675931eb9d</hash>
+        <hash type="sha256">d6f8135c9d5ac370fafd258c89cfb574989cd8557044d9551eefd1aaf2c54c48</hash>
+        <hash type="sha512">f43d747fa0134e9990297f98771f74a7a449f1b5a53a6936949af5e0b51f3b3834b69af3e25ae4dd7c97415d5570abb5c0a4588ddfae673228f46a83f200b5cd</hash>
+      </verification>
+      <mm0:alternates>
+        <mm0:alternate>
+            <mm0:timestamp>1288642986</mm0:timestamp>
+            <size>4834</size>
+            <verification>
+              <hash type="md5">bd32127a8b2ae7a370be3dbf876afbc7</hash>
+              <hash type="sha1">b4b9ea2494aefd82ff08915c28f6548a31625744</hash>
+              <hash type="sha256">b0d8ae9c43bfd06829092273b409cb0f6e2b581fafd370f11752842c0cd0c2c6</hash>
+              <hash type="sha512">78d2eff26e6a12bb1b1bbc78956f548734e921335943afabcfbf4f3ba6d164e8820eb2e685fad78088d2b42b7ec75fe4bde8ad6f5d7e9cf44e70837ce23fb758</hash>
+            </verification>
+        </mm0:alternate>
+      </mm0:alternates>
+      <resources maxconnections="1">
+        <url protocol="http" type="http" location="DE" preference="100" >http://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/repodata/repomd.xml</url>
+        <url protocol="ftp" type="ftp" location="DE" preference="100" >ftp://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/repodata/repomd.xml</url>
+        <url protocol="rsync" type="rsync" location="DE" preference="100" >rsync://ftp-stud.hs-esslingen.de/fedora/linux/updates/13/x86_64/repodata/repomd.xml</url>
+        <url protocol="http" type="http" location="BE" preference="45" >http://mirror.eurid.eu/fedora/linux/updates/13/x86_64/repodata/repomd.xml</url>
+        <url protocol="ftp" type="ftp" location="BE" preference="45" >ftp://mirror.eurid.eu/fedora/linux/updates/13/x86_64/repodata/repomd.xml</url>
+      </resources>
+    </file>
+  </files>
+</metalink>
diff --git a/tests/data/Mirrorlist/remote-site/mirrors.txt b/tests/data/Mirrorlist/remote-site/mirrors.txt
new file mode 100644 (file)
index 0000000..20a2168
--- /dev/null
@@ -0,0 +1,5 @@
+http://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/repodata/repomd.xml
+ftp://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/repodata/repomd.xml
+rsync://ftp-stud.hs-esslingen.de/fedora/linux/updates/13/x86_64/repodata/repomd.xml
+http://mirror.eurid.eu/fedora/linux/updates/13/x86_64/repodata/repomd.xml
+ftp://mirror.eurid.eu/fedora/linux/updates/13/x86_64/repodata/repomd.xml
diff --git a/tests/data/OBS_zypp_svn-11.1/repodata/primary.xml.gz b/tests/data/OBS_zypp_svn-11.1/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..ce990f8
Binary files /dev/null and b/tests/data/OBS_zypp_svn-11.1/repodata/primary.xml.gz differ
diff --git a/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml b/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..1bde3d3
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">734543b9cb748334925d6fac1a1f2f3f5b5dffd1</checksum>
+    <timestamp>1223586019</timestamp>
+    <open-checksum type="sha">97bd7432a1c6b1b44b9ca1f52b9a18979c72a9ba</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">722956718221845366d4572fe5649a268cb93920</checksum>
+    <timestamp>1223586019</timestamp>
+    <open-checksum type="sha">e9edadbe3063f4ae8c97667f67d31a192eadc427</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">f586852ceffd230cd6e7b15a880494a9e2f7a8a5</checksum>
+    <timestamp>1223586019</timestamp>
+    <open-checksum type="sha">c9e4113751d71ea413fe4564f1f0e94e2cda9bec</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.asc b/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..96a9d37
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBI7nDkyW8T9xlZ+8sRAolAAKCgHtH/tQVLIIM1k3SAo8EMMSe+7QCg
+ylO1hOqgWrRGrF+JovkE7akc4XI=
+=XnSD
+-----END PGP SIGNATURE-----
diff --git a/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.key b/tests/data/OBS_zypp_svn-11.1/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..620e3a1
--- /dev/null
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQGiBEeWW2sRBAC6p0M82A5kapMTPpn9MQ0w++cEHC4cKRGKe4ysJiGEOKcl8wmE
+buiiYOZKLvuFoyPDyyB/KPQ56nE/sEwJAsssXpMJ3EeSF5Px2qresY7Ralowozc5
+8WF2Btq90RNqIhfjAEpTmiDpn9g7fIn9YAzwh6md2i7BemKBEfNkVrVYNwCgy5bg
+9fSyOa4mr7HldiIAdNsPFLsD/0e8GhiSxD8xV3o7dchf8jMEgAv7gAbP1lGsbZQG
+dSsyODycr0PabGY3XVkNBb6NPdEcm63crsKeVltG455NL4WgkULaJDnDNtU1SGui
+AlAqEjXXR9dOCgVMs7lfpaF55cerUwGjGGMnZpGbzVClHZubwZrWV9h7O+9ln7Lu
+/W3mBACKfr68onKxCxmFEadAYEXtnJ/9Yk58rsD2V05rV+1SYCrybOTM/Hhjt5PZ
+WwXyfCwla43ljVGdIO2Rde8o6UIzbM49F04OC97HMzyGyUd2EiXfDtF4j5y+caoq
+9mZaXl2hiu4YSjNGEFGBelOS1YwBefnytgFV9nyWU+9E1iqDjLQyenlwcDpzdm4g
+T0JTIFByb2plY3QgPHp5cHA6c3ZuQGJ1aWxkLm9wZW5zdXNlLm9yZz6IZgQTEQIA
+JgUCR5ZbawIbAwUJBB6wAAYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEMlvE/cZ
+WfvLxR0AnRRinTmsd2B7Db6eruYX40smjwnrAJ4iIFZQXD/qMEGsSr0GfCgvhmGe
+94hGBBMRAgAGBQJHlltrAAoJEDswEbdrnWUjQqAAn1QYRSp5S0NrCknsvvWbGmhI
+jR0SAKCwuY8a+dKLOdrhV+520EhUsYU6+Q==
+=7eFk
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/TCSelectable/RepoHIGH.xml b/tests/data/TCSelectable/RepoHIGH.xml
new file mode 100644 (file)
index 0000000..924fff9
--- /dev/null
@@ -0,0 +1,57 @@
+<channel><subchannel>
+<package>
+       <name>candidate</name>
+       <vendor>unkown</vendor>
+       <history><update>
+               <arch>x86_64</arch>
+               <version>4</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidate</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>4</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+<package>
+       <name>candidatenoarch</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>noarch</arch>
+               <version>5</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>x86_64</arch>
+               <version>4</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>4</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>noarch</arch>
+               <version>4</version>
+               <release>1</release>
+       </update></history>
+</package>
+</subchannel></channel>
diff --git a/tests/data/TCSelectable/RepoLOW.xml b/tests/data/TCSelectable/RepoLOW.xml
new file mode 100644 (file)
index 0000000..2248672
--- /dev/null
@@ -0,0 +1,71 @@
+<channel><subchannel>
+<package>
+       <name>candidate</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>x86_64</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidate</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+<package>
+       <name>installed_and_available</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>installed_and_available</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>installed_and_available</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>3</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+<package>
+       <name>available_only</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>available_only</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>available_only</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>3</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+</subchannel></channel>
diff --git a/tests/data/TCSelectable/RepoMID.xml b/tests/data/TCSelectable/RepoMID.xml
new file mode 100644 (file)
index 0000000..3537414
--- /dev/null
@@ -0,0 +1,57 @@
+<channel><subchannel>
+<package>
+       <name>candidate</name>
+       <vendor>SUSE</vendor>
+       <history><update>
+               <arch>x86_64</arch>
+               <version>0</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidate</name>
+       <vendor>SUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>0</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+<package>
+       <name>candidatenoarch</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>noarch</arch>
+               <version>0</version>
+               <release>2</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>x86_64</arch>
+               <version>0</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>0</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>noarch</arch>
+               <version>0</version>
+               <release>1</release>
+       </update></history>
+</package>
+</subchannel></channel>
diff --git a/tests/data/TCSelectable/solver-system.xml b/tests/data/TCSelectable/solver-system.xml
new file mode 100644 (file)
index 0000000..9cac186
--- /dev/null
@@ -0,0 +1,55 @@
+<channel><subchannel>
+<package>
+       <name>candidate</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>candidatenoarch</name>
+       <vendor>unknown</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+
+<package>
+       <name>installed_only</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>installed_only</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>0</release>
+       </update></history>
+</package>
+
+<package>
+       <name>installed_and_available</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>installed_and_available</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>0</release>
+       </update></history>
+</package>
+
+</subchannel></channel>
diff --git a/tests/data/TCSelectable/solver-test.xml b/tests/data/TCSelectable/solver-test.xml
new file mode 100644 (file)
index 0000000..64a28d4
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="x86_64">
+       <system file="solver-system.xml"/>
+       <!--
+       - alias       : RepoHIGH
+       - url         : http://foo.org/distribution/HIGH
+       -->
+       <channel file="RepoHIGH.xml" name="RepoHIGH" priority="10" />
+       <!--
+       - alias       : RepoMID
+       - url         : http://foo.org/distribution/MID
+       -->
+       <channel file="RepoMID.xml" name="RepoMID" priority="50" />
+       <!--
+       - alias       : RepoLOW
+       - url         : http://foo.org/distribution/LOW
+       -->
+       <channel file="RepoLOW.xml" name="RepoLOW" priority="99" />
+       <locale name="en_US" />
+       <locale name="de" />
+</setup>
+</test>
diff --git a/tests/data/TCWhatObsoletes/solver-system.xml b/tests/data/TCWhatObsoletes/solver-system.xml
new file mode 100644 (file)
index 0000000..ce95a06
--- /dev/null
@@ -0,0 +1,55 @@
+<channel><subchannel>
+<package>
+       <name>test1</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version><release>1</release>
+       </update></history>
+       <provides>
+               <dep name='test1' op='==' version='1' release='1' />
+       </provides>
+       <obsoletes>
+               <dep name='goaway' op='&lt;' version='2' release='1' />
+       </obsoletes>
+</package>
+<package>
+       <name>goaway</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version><release>1</release>
+       </update></history>
+       <provides>
+               <dep name='goaway' op='==' version='1' release='1' />
+       </provides>
+</package>
+<package>
+       <name>goaway</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version><release>1</release>
+       </update></history>
+       <provides>
+               <dep name='goaway' op='==' version='2' release='1' />
+       </provides>
+</package>
+<package>
+       <name>meetoo</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version><release>1</release>
+       </update></history>
+       <provides>
+               <dep name='goaway' op='==' version='1' release='1' />
+       </provides>
+</package>
+<package>
+       <name>meetoo</name>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version><release>1</release>
+       </update></history>
+       <provides>
+               <dep name='goaway' op='==' version='2' release='1' />
+       </provides>
+</package>
+</subchannel></channel>
diff --git a/tests/data/TCWhatObsoletes/solver-test.xml b/tests/data/TCWhatObsoletes/solver-test.xml
new file mode 100644 (file)
index 0000000..9a2143d
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="i686">
+       <system file="solver-system.xml.gz"/>
+       <locale name="en_US" />
+</setup>
+</test>
diff --git a/tests/data/TCdup/solver-system.xml b/tests/data/TCdup/solver-system.xml
new file mode 100644 (file)
index 0000000..2e60954
--- /dev/null
@@ -0,0 +1,55 @@
+<channel><subchannel>
+<product>
+       <name>Product</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</product>
+<package>
+       <name>release-package</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+        <provides>
+                <dep name='product()'/>
+                <dep name='product(Product)' op='==' version='1' release='1' />
+        </provides>
+</package>
+
+<package>
+       <name>glibc</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+        <requires>
+                <dep name='dropped_required'/>
+        </requires>
+</package>
+<package>
+       <name>dropped_required</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+<package>
+       <name>dropped</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>1</version>
+               <release>1</release>
+       </update></history>
+</package>
+</subchannel></channel>
diff --git a/tests/data/TCdup/solver-test.xml b/tests/data/TCdup/solver-test.xml
new file mode 100644 (file)
index 0000000..08c701d
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="x86_64">
+       <system file="solver-system.xml"/>
+       <!--
+       - alias       : update
+       - url         : http://foo.org/distribution/update
+       -->
+       <channel file="update.xml" name="update" priority="10" />
+       <locale name="en_US" />
+       <locale name="de" />
+</setup>
+</test>
diff --git a/tests/data/TCdup/update.xml b/tests/data/TCdup/update.xml
new file mode 100644 (file)
index 0000000..e8e0e0c
--- /dev/null
@@ -0,0 +1,36 @@
+<channel><subchannel>
+<product>
+       <name>Product</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</product>
+<package>
+       <name>release-package</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+        <provides>
+                <dep name='product()'/>
+                <dep name='product(Product)' op='==' version='2' release='1' />
+                <dep name='weakremover(dropped_required)'/>
+                <dep name='weakremover(dropped)'/>
+        </provides>
+</package>
+
+<package>
+       <name>package</name>
+       <vendor>openSUSE</vendor>
+       <history><update>
+               <arch>i586</arch>
+               <version>2</version>
+               <release>1</release>
+       </update></history>
+</package>
+</subchannel></channel>
diff --git a/tests/data/obs_virtualbox_11_1/repodata/primary.xml.gz b/tests/data/obs_virtualbox_11_1/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..855e00a
Binary files /dev/null and b/tests/data/obs_virtualbox_11_1/repodata/primary.xml.gz differ
diff --git a/tests/data/obs_virtualbox_11_1/repodata/repomd.xml b/tests/data/obs_virtualbox_11_1/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..7f971ad
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">580103a6b945e1830565121199acbdad45437c7f</checksum>
+    <timestamp>1223588080</timestamp>
+    <open-checksum type="sha">2d2604f11a697bc443b3c9999d630897138168ad</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">e8696a3badb8419500de7e1618585d6272912a99</checksum>
+    <timestamp>1223588080</timestamp>
+    <open-checksum type="sha">91d3e517317bcd686a5ec1e88abd63b6fb7619da</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">fc0f951db57c5b767887190bf99e479692b1c407</checksum>
+    <timestamp>1223588080</timestamp>
+    <open-checksum type="sha">a0015ff42e9822c16990b23a9cb08e026b4afdf9</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/data/obs_virtualbox_11_1/repodata/repomd.xml.asc b/tests/data/obs_virtualbox_11_1/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..857cd5e
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBI7njy+FLjwVdyGmYRAh6pAJ4zinB1Qa04keOTZET1/KTWRQcdqACf
+WulP6OHLt3n1fuHPRQRYUmUgeaU=
+=GveQ
+-----END PGP SIGNATURE-----
diff --git a/tests/data/obs_virtualbox_11_1/repodata/repomd.xml.key b/tests/data/obs_virtualbox_11_1/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..a79e313
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQGiBEeWVjIRBACUzcPOeJfTB0/AbBdlmZqfGca/YwPdg7PXB1zBkfuH/YSDDgTo
+4BesZigfcgIyO+3KCyPZ0P+5pO3RIdmIyMGndhKsSTb+sX0utPYvsJlMDmo2lHm3
+i26st2DZLESKOtfpeyeXrZHTL1loAyUy0IHpTD6pmtgnBLr7SNkcGb2oJwCgxMvG
+ArBVVG5+R6r8gXJ5NxAH+rUEAIOYuwl8RwmFHvVMg/swDl+YTXqhM7BOyieY4HzR
+hhm+OKOkMFqSMudReHvX+jwtpUduN8GiTZq0QaWTF1WUZLQhn+Q+4oRjOWnIKiwl
+RRQisrn4cVJtzLQojCh6JBcIobNCexAUCMMoC0dGB5LGxfx8eKKuytzPooMYpfzV
+78QmA/49WMDc5wLyobhchFzm4cQmk7SYGaTFbAnkF6IBgIEOfQGkQCSMcKlLNYfB
+kgJr8FMeakkeExrXKsvmL5U8stwtLgRqYhBYgEE/1ZS2MGDLB0nzaEVfovvTYn6B
+n4o34KE/l6RaXUsxcyL0sbZ6BOG1MgKWLjlKkn9xI77m9jR5HbRUVmlydHVhbGl6
+YXRpb246VmlydHVhbEJveCBPQlMgUHJvamVjdCA8VmlydHVhbGl6YXRpb246Vmly
+dHVhbEJveEBidWlsZC5vcGVuc3VzZS5vcmc+iGYEExECACYFAkeWVjICGwMFCQQe
+sAAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD4UuPBV3IaZrrMAJ0aJsQOUt+s
+efyWCGh3MefGjBEUFACdEixxBrtbVss3yILYjJbzlUwhp1GIRgQTEQIABgUCR5ZW
+MgAKCRA7MBG3a51lI3O8AJ9vv441rs2ZR8pHL2EEmvegbAMPTwCeJJUbs14J5Wlr
+zqyZAhXnXWJnldY=
+=elkz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/content b/tests/data/openSUSE-11.1/content
new file mode 100644 (file)
index 0000000..6987b12
--- /dev/null
@@ -0,0 +1,122 @@
+CONTENTSTYLE 11
+NAME openSUSE
+LABEL The openSUSE Distribution
+VENDOR SUSE LINUX Products GmbH, Nuernberg, Germany
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/11.0.42/release-notes-openSUSE.rpm
+DESCRDIR suse/setup/descr
+REFERENCES openSUSE-release EQ 11.1
+LINGUAS cs da de en en_GB en_US es fi fr hu it ja nb nl pl pt pt_BR ru sv zh zh_CN zh_TW 
+BASEARCHS x86_64 
+FLAVOR dvd
+DISTRIBUTION openSUSE
+DATADIR suse
+VERSION 11.1
+META SHA1 02f22523c8302dbd4838618b746cf8a348e39a53  dvd-11.1-46.1.x86_64.pat.gz
+META SHA1 cc02920ad0f2270663a04861bc44c7fd480f1e9f  non_oss-11.1-46.1.x86_64.pat.gz
+META SHA1 d8e90da75261b09757c10d90055aba27c36c2571  packages.cs.gz
+META SHA1 1bfeb67fbb2e77f1309bd105d209549370d63184  packages.DU.gz
+META SHA1 2db9160217ca686f78a3e86de3de38955ad9fe3f  packages.en.gz
+META SHA1 f4671fc99244c27e2a467a9bd11aa43a5fbe96b8  packages.gz
+META SHA1 73615d8e511ea4ad14bce617bfe18ea5de65395e  patterns
+HASH SHA1 cd4e142d292e299488a482144f38f9f8d4d61f01  license.tar.gz
+HASH SHA1 7bd21d938f9fec0ec764ac4d46d80417baf5c94e  control.xml
+HASH SHA1 e1499f54fba10d11ce6b6f0fc9c57c6a2ad49f83  media.1/info.txt
+HASH SHA1 d423ad41e93a51195a6264961e4a074c6d89359d  boot/x86_64/bind
+HASH SHA1 635492857403d315c613579760694919d252fa68  boot/x86_64/branding
+HASH SHA1 6c661d6cf1db68da6498bcf179fb52dbec907563  boot/x86_64/common
+HASH SHA1 7cb8e2739b4751daed7c6f6e7cc20b5021346885  boot/x86_64/config
+HASH SHA1 7eebc04d8999af9a5b2fdcb75c6ad2e9cd46ef24  boot/x86_64/cracklib-dict-full.rpm
+HASH SHA1 50067c10711281342e1b73bdb02b49a95ea626d0  boot/x86_64/directory.yast
+HASH SHA1 93d86eaadeedbf29cf4028f6b965cd513fb4b392  boot/x86_64/efi
+HASH SHA1 c9216fb7114ce0f23a0c0650b91d4efd5785dd57  boot/x86_64/fonts-arabic.rpm
+HASH SHA1 d4cde75eaf078dcf5a9be04f5cb6427fc5c0a4d2  boot/x86_64/fonts-thai.rpm
+HASH SHA1 c82ef5b36dda514d8239a0c234f50a5201cf2f1c  boot/x86_64/gdb
+HASH SHA1 44bed8ec07474e7703fa7915909eccf76a624b68  boot/x86_64/indic-fonts.rpm
+HASH SHA1 e3005c6ffc8600dc1e5c42b3a1d94ce3e32562d5  boot/x86_64/KhmerOS-fonts.rpm
+HASH SHA1 e9c779539f1b3f56a96337f262b2bc548e6b223e  boot/x86_64/LIESMICH
+HASH SHA1 abdf17a0246be709efbcc7c0e0a68f8ad797a754  boot/x86_64/LIESMICH.DOS
+HASH SHA1 b5a21e780bdad707a989754f0fda867b60bf398e  boot/x86_64/lklug.rpm
+HASH SHA1 c4dca65aa97554bc45885b22ef4af24ad5177c75  boot/x86_64/mkbootdisk
+HASH SHA1 635492857403d315c613579760694919d252fa68  boot/x86_64/openSUSE
+HASH SHA1 ae44557c57285f0866be41334fdaa20312ccdeab  boot/x86_64/README
+HASH SHA1 4a71ee6114eccfb88582dc5422020cbc5a2639dd  boot/x86_64/README.DOS
+HASH SHA1 b82adaf3be49972e7241ade027a6a26139cc632c  boot/x86_64/rescue
+HASH SHA1 3810d9544827f1d42708f39427e0a48e9e086ea1  boot/x86_64/root
+HASH SHA1 415f125a4ee1e5a42dc65399b31526110bce3dc1  boot/x86_64/rpmlist
+HASH SHA1 bd61c88b73894e8106bda37d8190427a910d3ca2  boot/x86_64/sax2
+HASH SHA1 df10e84db8c57c00aac85e2e6a6cc5e359c27d8f  boot/x86_64/unfonts.rpm
+HASH SHA1 1239df7c815fa8e121aed8c0dcef1afe45579611  boot/x86_64/yast2-trans-af.rpm
+HASH SHA1 b12865177ac3be4741dfb4aae42b39398128314f  boot/x86_64/yast2-trans-ar.rpm
+HASH SHA1 a35e306a95f4dd292baab41c280cb25f49e9a90f  boot/x86_64/yast2-trans-bg.rpm
+HASH SHA1 b6eeb78c8462f7d5a26e863f1e4b60740b3379eb  boot/x86_64/yast2-trans-bn.rpm
+HASH SHA1 b3f6d1ff28da9b19e42a0376a44eaa047d538ebc  boot/x86_64/yast2-trans-bs.rpm
+HASH SHA1 e95f0a401ac1b94f463d3c47bb1fbde091644dbb  boot/x86_64/yast2-trans-ca.rpm
+HASH SHA1 6a2c8a3bad0ecc2d18c000d6c1ce0b3af2e0e208  boot/x86_64/yast2-trans-cs.rpm
+HASH SHA1 11486ac6c36f4d5a4272bc1c712c30dc8f2c7828  boot/x86_64/yast2-trans-cy.rpm
+HASH SHA1 9f15697ae01461959861b33ca46a04587b8f428e  boot/x86_64/yast2-trans-da.rpm
+HASH SHA1 4a15f3271782059ad77d0b2027685e86d26f0b00  boot/x86_64/yast2-trans-de.rpm
+HASH SHA1 1667a7ab28bcc171234c960dab46fef1871d4d69  boot/x86_64/yast2-trans-el.rpm
+HASH SHA1 70dab8364af972d42cb21879eac56eaf9b1b2b19  boot/x86_64/yast2-trans-en_GB.rpm
+HASH SHA1 29b416da00c34a2bced9438f0ce4e4eeb92b94e6  boot/x86_64/yast2-trans-en_US.rpm
+HASH SHA1 46c97123053954623de74f2f88eaabe9bc7a330d  boot/x86_64/yast2-trans-es.rpm
+HASH SHA1 ab897d8d470485f1ede2ed378502faa5ddc699cc  boot/x86_64/yast2-trans-et.rpm
+HASH SHA1 f0fba8ba6a39e4ff54e55d0f5afe52102d7f6cb3  boot/x86_64/yast2-trans-fa.rpm
+HASH SHA1 83c25237432beb7fcb31ef4242d692ab8683cf2a  boot/x86_64/yast2-trans-fi.rpm
+HASH SHA1 54d0a51ca313a293afa71837f06570933a811123  boot/x86_64/yast2-trans-fr.rpm
+HASH SHA1 199ce417e36c36384709f5870a9227bdbeb69300  boot/x86_64/yast2-trans-gl.rpm
+HASH SHA1 3adf1af8f9a806101ec2ec30fd30e3b6f36c5754  boot/x86_64/yast2-trans-gu.rpm
+HASH SHA1 4b7f87ba2f9c0305b50ccd05993223319bd98699  boot/x86_64/yast2-trans-hi.rpm
+HASH SHA1 64573b85a3635dd438ea3a20948b50e7feff1e1f  boot/x86_64/yast2-trans-hr.rpm
+HASH SHA1 6450a7cd2061f5399816b521248ad4f96d1401c0  boot/x86_64/yast2-trans-hu.rpm
+HASH SHA1 cd7a63bda7472183da33cb778e3ad403152663e2  boot/x86_64/yast2-trans-id.rpm
+HASH SHA1 8db427cb2eb69348042ea94e2523c0e626431894  boot/x86_64/yast2-trans-it.rpm
+HASH SHA1 3c273d19b4c6ac6d3209fe35109595034fb2bbf5  boot/x86_64/yast2-trans-ja.rpm
+HASH SHA1 4a14bf1c880b63ebc4ae0746c89519f7b8a41cd5  boot/x86_64/yast2-trans-jv.rpm
+HASH SHA1 f47f6a81e05c38fc77a37226dabe038d64b3b4cd  boot/x86_64/yast2-trans-ka.rpm
+HASH SHA1 397dcfbe3dc3f54b0ede510105d474360d986930  boot/x86_64/yast2-trans-km.rpm
+HASH SHA1 666f733888966efd0490fbb3e41f1ede9f14f1ca  boot/x86_64/yast2-trans-ko.rpm
+HASH SHA1 417c70401e18d2f948e5f4e87db1396dd6d886c9  boot/x86_64/yast2-trans-lo.rpm
+HASH SHA1 f7856142994f69c5d6fe3c7a78fc1ad708fecb55  boot/x86_64/yast2-trans-lt.rpm
+HASH SHA1 58beaecdacfda372a34454f738a0f8ec80aecfb2  boot/x86_64/yast2-trans-mk.rpm
+HASH SHA1 c48d3d055928c8bd2fc1ec3040f102fe0f387990  boot/x86_64/yast2-trans-mr.rpm
+HASH SHA1 890a914b0ad6a29143e61deb161eff3556b72a44  boot/x86_64/yast2-trans-nb.rpm
+HASH SHA1 11a8297890053f5cca3674953805284d88b1ad79  boot/x86_64/yast2-trans-nl.rpm
+HASH SHA1 fa28c6c056018f95e008e8dc586f0e0b370428c1  boot/x86_64/yast2-trans-pa.rpm
+HASH SHA1 7751c37d3d772ff1b85a3a890d95779703980419  boot/x86_64/yast2-trans-pl.rpm
+HASH SHA1 b8462077cb347654ad110e7a5eceb5510b785ba1  boot/x86_64/yast2-trans-pt_BR.rpm
+HASH SHA1 97f44a1d7e04f9d08d2ce6fdd83398d7aa0643ba  boot/x86_64/yast2-trans-pt.rpm
+HASH SHA1 570ccf8610302ee61731f50604a0d74405c36640  boot/x86_64/yast2-trans-ro.rpm
+HASH SHA1 74822dc08976c91840e90624d369da07c035b69a  boot/x86_64/yast2-trans-ru.rpm
+HASH SHA1 133235b1cb582ecb94c2f1da2b42f2bf7ad555dd  boot/x86_64/yast2-trans-si.rpm
+HASH SHA1 a49abc3f07422c8b7356633d4293de4f477d2d8a  boot/x86_64/yast2-trans-sk.rpm
+HASH SHA1 2cc14382b7bda0846d529580fc168e76d02eade2  boot/x86_64/yast2-trans-sl.rpm
+HASH SHA1 6728cda67fd1c92d4b34f8a75752e98145916e4f  boot/x86_64/yast2-trans-sr.rpm
+HASH SHA1 fc9387bfec45138cbcc10d112a4160e93548dce9  boot/x86_64/yast2-trans-sv.rpm
+HASH SHA1 3a6f6bf53ab8dfe8c268dbf25f7aae869dfa8623  boot/x86_64/yast2-trans-ta.rpm
+HASH SHA1 755b0d4289a35a84bdc643112c09f102e5aecee6  boot/x86_64/yast2-trans-th.rpm
+HASH SHA1 c4e460be4ead26938872f5ee56f05fd527af31c8  boot/x86_64/yast2-trans-tr.rpm
+HASH SHA1 5df0e280b9d3f071cf72158d4a482e443fc98c51  boot/x86_64/yast2-trans-uk.rpm
+HASH SHA1 e4c77981b4ab02c963536ab6afd3cfe226fd6a3d  boot/x86_64/yast2-trans-vi.rpm
+HASH SHA1 dcd414589be7e8d1d837d49d35614845f986a59e  boot/x86_64/yast2-trans-wa.rpm
+HASH SHA1 2f76618d24c00b3fd4cbf8fdd4a56d34dc19867e  boot/x86_64/yast2-trans-xh.rpm
+HASH SHA1 3e6ac86cc533dae2053c06dca92918444e4c9361  boot/x86_64/yast2-trans-zh_CN.rpm
+HASH SHA1 ad9e32afe651712cd384c8e20d17de18d6a86ae4  boot/x86_64/yast2-trans-zh_TW.rpm
+HASH SHA1 a1aaa5991b25744683a761b936067ba279be608e  boot/x86_64/yast2-trans-zu.rpm
+HASH SHA1 219014df667b51f44d62b22979993c6914aab3ce  boot/x86_64/loader/linux
+HASH SHA1 266979798cb8924da5078fce8f1c41db54e21032  boot/x86_64/loader/initrd
+HASH SHA1 1ee4dd576e7aef8b10e01cf64b70e548175f9f40  boot/x86_64/loader/08000600.spl
+HASH SHA1 f93b778b469797d3547ce21dd67d31aeeefa412b  boot/x86_64/loader/10240600.spl
+HASH SHA1 8a33d7e0f2f1c1933261e1747ea9592991b6d305  boot/x86_64/loader/10240768.spl
+HASH SHA1 8f51f64ddfcbe6b3261bd329a65bd8f75ddbd779  boot/x86_64/loader/12800800.spl
+HASH SHA1 de76d38637bf6a520972ba22125023fb5e9005d2  boot/x86_64/loader/12801024.spl
+HASH SHA1 db7c22e91260da6c5c86587a477b277a2921fcc3  boot/x86_64/loader/14001050.spl
+HASH SHA1 a5985dc49d457ef58127b288cf3e20c4fbdb5a55  boot/x86_64/loader/16001200.spl
+HASH SHA1 495cee4703eaea74c31fca705a4cdbb8825c9f8d  boot/x86_64/loader/16801050.spl
+HASH SHA1 01aaf0d2aa576c4bdd0fd2638baeeb6e788e902b  boot/x86_64/loader/19201200.spl
+KEY SHA1  17162a96933229a9771ee10c0976bdc047a2f53d  gpg-pubkey-0dfb3188-41ed929b.asc
+KEY SHA1  f6accbb18d705bfc104c893cf7dfca1247a33f3c  gpg-pubkey-307e3d54-481f30aa.asc
+KEY SHA1  47f6492d127ae9f6aac353a2dd23752fc0ed4f8d  gpg-pubkey-3d25d3d9-36e12d04.asc
+KEY SHA1  2288e5849740566e4fb65b7c9dc0c7e4f43b1039  gpg-pubkey-56b4177a-47965b33.asc
+KEY SHA1  89d4bcd20a281553fd1d4ec1708603ebf88f1a59  gpg-pubkey-7e2e3b05-4816488f.asc
+KEY SHA1  06ff5171362496c0db84beeccd29967f580350b2  gpg-pubkey-9c800aca-481f343a.asc
+KEY SHA1  04544096c5c3b0ed7b01a83d79e048307c2af919  gpg-pubkey-a1912208-446a0899.asc
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-0dfb3188-41ed929b.asc b/tests/data/openSUSE-11.1/gpg-pubkey-0dfb3188-41ed929b.asc
new file mode 100644 (file)
index 0000000..ac75d62
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEHtkpsRBACRHiXh3olS++6/Mp9N7ByGMmjaaE+Y8cJQLUPG1myrbW5aogIP
+0WenayhGbbgOHNWgd5dQ8KQpYYFoQuUHjFYzj5MvgrdOENOvD7ZNJ6+EmbkNh5cV
+zUYfNG9jdiGweZkyA1sh8DYS0JiUmQ4CzaBD/DotB/dCmDcyuNQFiw4qKwCglQah
+ATyueBRsOiXl0NIs1uB6dkkD/1A2YmQ6te1q38a1J+a8os6bDlMZhVnkZdhJdw6x
+eBwUb9XS0n7hyt/AKCcBnrDEUQJuhBMNgzctJvbuMv27yRMANAXZDQkp0ip/yHLJ
+PhUdSNTTRHOL9bV3t+JuZ9xmuclprwyrrJYUkEESXNc0tkuczHBP2c/RqA3OxYHt
+hrHLA/9Pqe2gEleeo8l26u/uFXs2dtwjh8EZmdhHoqGcOlpYR4DyAg2D+jYfh3RI
+oPzIwRlHVUR1ii5h8iPi98BVuEvukwfbbQ1K22Jwzxt6w3ihCXBKWKbeC3ElIMfA
+hVMchLFUbTAw+yodO/u3NHxKQ34+ginid9dVyxV5T0gpDEEHObQrT3BlbiBFbnRl
+cnByaXNlIFNlcnZlciA8c3VwcG9ydEBub3ZlbGwuY29tPoheBBMRAgAeBQJB7ZKb
+AhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEHPSXWMN+zGID4oAoJPTGZbZApW+
+tuU422mHYGwoqgjrAJ9fhzRhRbV3YsOxKUomNeuIfmWGXA==
+=Qv5+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-307e3d54-481f30aa.asc b/tests/data/openSUSE-11.1/gpg-pubkey-307e3d54-481f30aa.asc
new file mode 100644 (file)
index 0000000..57a9078
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mIsERCAdXQEEAL7MrBTz+3SBWpCm2ae2yaDqV3ezQcs2JlvqidJVhsZqQe9/jkxi
+KTEQW5+TXF/+BlQSiebunRI7oo3+9U8GyRCgs1sf+yRQWMLzZqRaarzRhw9w+Ihl
+edtqYl6/U2JZCb8Adp6d7RzlRliJdJ/VtsfXj2ef7Dwu7elOVSsmaBdtAAYptChT
+dVNFIFBhY2thZ2UgU2lnbmluZyBLZXkgPGJ1aWxkQHN1c2UuZGU+iLgEEwECACIC
+GwMECwcDAgMVAgMDFgIBAh4BAheABQJIHzCqBQkHwXpNAAoJEOOlw2Awfj1UvWgE
+AIRoxE8S6jQB7S43SVcX06FHJeUJ/m+1ErIj9LwJTYrR/8qsDjTgrttgb+nBHkIj
+NhCCLAuR8sWj3CxsUMH2fayryNnwZEWGqnzo7Jtt4R1Ur3h5pHYonFjfoJyFUZjJ
+7Mhw7/TuOWx20FrzqBi8tbHx8pd7Fa5lCUgopVtMh6GR
+=R56j
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-3d25d3d9-36e12d04.asc b/tests/data/openSUSE-11.1/gpg-pubkey-3d25d3d9-36e12d04.asc
new file mode 100644 (file)
index 0000000..80380d2
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQENAzbhLQQAAAEIAKAkXHe0lWRBXLpn38hMHy03F0I4Sszmoc8aaKJrhfhyMlOA
+BqvklPLE2f9UrI4Xc860gH79ZREwAgPt0pi6+SleNFLNcNFAuuHMLQOOsaMFatbz
+JR9i4m/lf6q929YROu5zB48rBAlcfTm+IBbijaEdnqpwGib45wE/Cfy6FAttBHQh
+1Kp+r/jPbf1mYAvljUfHKuvbg8t2EIQz/5yGp+n5trn9pElfQO2cRBq8LFpf1l+U
+P7EKjFmlOq+Gs/fF98/dP3DfniSd78LQPq5vp8RL8nr/o2i7jkAQ33m4f1wOBWd+
+cZovrKXYlXiR+Bf7m2hpZo+/sAzhd7LmAD0l09kABRG0JVN1U0UgU2VjdXJpdHkg
+VGVhbSA8c2VjdXJpdHlAc3VzZS5kZT6JARUDBRA24S1H5Fiyh7HKPEUBAVcOB/9b
+yHYji1/+4Xc2GhvXK0FSJN0MGgeXgW47yxDL7gmR4mNgjlIOUHZj0PEpVjWepOJ7
+tQS3L9oP6cpj1Fj/XxuLbkp5VCQ61hpt54coQAvYrnT9rtWEGN+xmwejT1WmYmDJ
+xG+EGBXKr+XP69oIUl1E2JO3rXeklulgjqRKos4cdXKgyjWZ7CP9V9daRXDtje63
+Om8gwSdU/nCvhdRIWp/Vwbf7Ia8iZr9OJ5YuQl0DBG4qmGDDrvImgPAFkYFzwlqo
+choXFQ9y0YVCV41DnR+GYhwl2qBd81T8aXhihEGPIgaw3g8gd8B5o6mPVgl+nJqI
+BkEYGBusiag2pS6qwznZiQEVAwUQNuEtBHey5gA9JdPZAQFtOAf+KVh939b0J94u
+v/kpg4xs1LthlhquhbHcKNoVTNspugiC3qMPyvSX4XcBr2PC0cVkS4Z9PY9iCfT+
+x9WM96g39dAF+le2CCx7XISk9XXJ4ApEy5g4AuK7NYgAJd39PPbERgWnxjxir9g0
+Ix30dS30bW39D+3NPU5Ho9TD/B7UDFvYT5AWHl3MGwo3a1RhTs6sfgL7yQ3U+mvq
+MkTExZb5mfN1FeaYKMopoI4VpzNVeGxQWIz67VjJHVyUlF20ekOz4kWVgsxkc8G2
+saqZd6yv2EwqYTi8BDAduweP33KrQc4KDDommQNDOXxaKOeCoESIdM4p7Esdjq1o
+L0oixF12CohGBBARAgAGBQI7HmHDAAoJEJ5A4xAACqukTlQAoI4QzP9yjPohY7OU
+F7J3eKBTzp25AJ42BmtSd3pvm5ldmognWF3Trhp+GYkAlQMFEDe3O8IWkDf+zvyS
+FQEBAfkD/3GG5UgJj18UhYmh1gfjIlDcPAeqMwSytEHDENmHC+vlZQ/p0mT9tPiW
+tp34io54mwr+bLPN8l6B5GJNkbGvH6M+mO7R8Lj4nHL6pyAv3PQr83WyLHcaX7It
+Klj371/4yzKV6qpz43SGRK4MacLo2rNZ/dNej7lwPCtzCcFYwqkiiEYEEBECAAYF
+AjoaQqQACgkQx1KqMrDf94ArewCfWnTUDG5gNYkmHG4bYL8fQcizyA4An2eVo/n+
+3J2KRWSOhpAMsnMxtPbB
+=Ay23
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-56b4177a-47965b33.asc b/tests/data/openSUSE-11.1/gpg-pubkey-56b4177a-47965b33.asc
new file mode 100644 (file)
index 0000000..f0bb55a
--- /dev/null
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEeWWzMRBADU8l2IckSzgdUS1dn0WMM7wXK4seoFsHHQw/0unHCQCxpyDMnn
+TKV86p5KTbR1FDdeaZlY0yCV+IGsiIxLyuUdJn8vuA5gA5ZkUr89/HtWaeZVl77J
+HIQxvhDRBWCOO4QNtrZYWvGbvl83wl/zOfdLEs8IGElt0LgfohyTA1qfrwCg/Hac
+tDDscXsPlo5Jek/+3RHVeD0D/30riCpfpLJOmhraLg1EbWsE0mN9IQsl+WDPdoYo
+bB76z6eH3e38618WzP/LTG4WuVbwpSSqmXyfdVpXxWzESfT8q0B8CGpHf/Sa/T/L
+emohmRnLvkf/tAfxFmDMm1jOewJIE9S35jANGHVJcxmfRNpPWC7uHnqjopnsmDkL
+kMEdBAC6YcpDOcMJZ9sJbt/JNZBaoT5CltgMDlSN50t2v/J5em8qMLqCSNF5UJyd
+LFnePHTHy6gVjWbqcC0ncFzOqM1y644Up7BoKSAr1hRTl6Mw9S3UfZZZ0al3JtWt
+8y0eFIW3QP66w1AL0LO2bZMBuOvhb63DXv5iHorcxk0yIFbbybRCb3BlblNVU0U6
+RmFjdG9yeSBPQlMgUHJvamVjdCA8b3BlblNVU0U6RmFjdG9yeUBidWlsZC5vcGVu
+c3VzZS5vcmc+iGYEExECACYFAkeWWzMCGwMFCQQesAAGCwkIBwMCBBUCCAMEFgID
+AQIeAQIXgAAKCRAcchwkVrQXevBsAKCOeScnlH2fWVBJGHTOVJ3M4yBqDACbBeNk
+PuWo05AOQ3M1dLE1hkN36G+IRgQTEQIABgUCR5ZbMwAKCRA7MBG3a51lI7PfAKCc
+9ZtKfI5G/g66V7pSMXh9gi+ykgCgivPfGMDh9HIROwBIudo2qGImOqI=
+=htdw
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-7e2e3b05-4816488f.asc b/tests/data/openSUSE-11.1/gpg-pubkey-7e2e3b05-4816488f.asc
new file mode 100644 (file)
index 0000000..74ff59e
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBER0iroRBADfqUeJmPCXqPJFnf3CVKy40dL1F+gfvP+JHN7/uu4c9+oCYjI1
+uAE8iGTm/Twb/Zzbs4dt0iWjhNFXbRi42rMww4d/8QcPYZ21WSehh+fv8nCjt2sw
+LeC87ar2SR8OTpJBK0fQlcd4e6H5GMntfI6SYEUOPd8m/eQ+4+1AxpUUpwCgswaF
+13fePZGI//pDn5tGjbvmaP8D/R3qum/I+oDS8lbFeeDS10GkEkwTYec13gdfsq6I
+yzIj7VBsC+rGfbipv+VGR61Q4d19pOHKLDekr9OG+3G4ZcYM4NQvQZR+QIlp3xWu
+nBmYD1LRkHLVj+Z4DGQhjjOffkPSuacKPymMaZ/aRiLgTIAo97W2YPhutscXrLSG
+2Y+BA/4jsyaDb7kbW4wc8RtPIcuFEheVqgBeRakP9Uj47kBMBEpPtI/mIdY5liKk
+ztKnuQG6ROYLNV/PW0ZbE1uT64C710weh4cB3PnZLV5P10deDLBjHk8MJQGCTSDD
+JYvhutUzQfshAU6j2kErGvKdZxWGezab34vFyMP2oLGqswPAJrRQTm92ZWxsIFBy
+b3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20pIDxub3ZlbGwt
+cHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6IZgQTEQIAJgIbAwYLCQgHAwIEFQIIAwQW
+AgMBAh4BAheABQJIFkiPBQkHhmvVAAoJEBTCi8l+LjsFn4QAn2wgOHudNubNZvTz
+NdaYJKJ0m2qnAJ9hd0nQBhn28H4Ii4a4h7kpGWRxN4hGBBMRAgAGBQJEexD/AAoJ
+EKhO2uicgArKFLwAn0B+g2mJ5n8LrBziTQ5SjnSPyDBXAJwJoYTta5Sfw/3vVGpU
+fJAKVDoB9w==
+=MWDN
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-9c800aca-481f343a.asc b/tests/data/openSUSE-11.1/gpg-pubkey-9c800aca-481f343a.asc
new file mode 100644 (file)
index 0000000..daeef8a
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiAhsDBAsHAwID
+FQIDAxYCAQIeAQIXgAUCSB80OgUJEfKmuQAKCRCoTtronIAKyuJlAJ0cWZifmBO6
+Eh71jattipdMhUYBTwCfSXbJJtuF3c96JPmpmT8be2LDo86IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkgfNGYFCRHyptQACgkQqE7a6JyACsrv3ACbBLhafFXmTjH3
+JJWFJGWuIOaZUosAniPs4feEyN46gjXGgcZc2Ai8nkm6
+=mY6G
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/gpg-pubkey-a1912208-446a0899.asc b/tests/data/openSUSE-11.1/gpg-pubkey-a1912208-446a0899.asc
new file mode 100644 (file)
index 0000000..8467c19
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQILBERqCJkBEACdqhZWdAbUHLIumXMEgv+GFjr1ZzVHgynnFOzztU/8sxZNa9cm
+YV4HZpVfjMr7fos3ArzyiPPt/336cf7w9p79/ZS4rHSNPDMlPCtXYvFxUbvU0/GY
+q4jwcBsrJ0xaJ9CP5bWyAgVKOb7Y6k0ktaLjRR+tDfMsHA4H0ClMoRr6ATw8NL0e
+VCfAHuzqCKvX1If8ng+wTivtAhKvz/WwQiELNELmPfc5tZHOw8NgP/r0Pze18Hn2
+dlAHu0WpC7uoR00vscsMIJiJJPcsxbL1F1eADKnk+wEy8Go+EJeJ5i0WoFbqD52q
+Lv/C/oY6NVtVY0MBwtn+oQNSnQ4JBsB/Akdt53LAi0ZtNQxMyUW+76R8FCOmVCV8
+WGiF5CPRP0yvG80AMBjBjKjHb/v8ov5MnIyFimzAHS1gQcUNxTEYA/5eFwoYcGcK
+weGq9FUjPTzLQAgvp7XmOzHpSAfJ7qysxFTepNsSZZhgizJyInrdQldr+GYcUNqB
+krD9MWmFop975OxhCTEnNv/HcE79r8WD26HzDFYxTiTJbr0pU/ivBzo+rjq+YG2V
+stJk+udVYmZTnC4LmXus8JiNuqBXbxNscwCBpcJ8YcfCV6uh+7E0XfXZsgVUFLp1
+NF+ylYRGTycOlWoZODrnJevZW7N9O3bWRx/G2P4bJD07LsDLe4i5hymf5QAGKbRQ
+Tm92ZWxsIFByb3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20p
+IDxub3ZlbGwtcHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6JAjMEEwECAB0FAkRqCJkG
+CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRBHijLooZEiCKSFD/93vZHCAMLEfksU
+KnvXl08bv1rfuamuyJnE3ANRE5RDyypriHMCnkVxazvQ2WI4W4UEjluL9+SzZwtV
+ZvKVoAr31614nSyWwv2YnJTHfjMG+xRlkolZMnuIiB9PcCBo9+GPU0ABuzo4pEJW
+NIRoSS1NFbAZBhtUnY0cN+trM5QObLl7xXTavLyGk//blkk57fov7GXsQJlZUig0
+l2yt5XNyGpLUnTMDumHh8b389quF+0+ZfdwOy7A768xjipAZiTvIujBrEv51wrxh
+0HBT0VGA0MhD9t0B+Ce4BM9P/iVMO00naaOp6PqMfPPKxQQqer8qy1i6UWBx95SY
+mKZBIvOm2d9PezDxkckCu61r6krx1iKnT1wdprCAkIYwALK118SpbxuyGW0bhRHc
+wsc/akzWH72fS0Xu49mvL4k4A2U9asdeQid3dMgbtm5mSWof0yiU/G4YNn0yeXoY
+oG1VbCAqQbFX1Rvd6GITJVqI+ekW/uMA9BP78dF8wBeG0+QmpQnSf+eOsxB/RT8o
+Kb4hHY+29MUlg+i9ceVt7hoKr03J/uIG5TXFXRYLaI0iAFVlKfWxpqDfS2XA4+dD
+VYt+5RDgBcnxDaTB4FE9GqcYScNfe7+NFtL0p0wOPftbmgZzGjucTmrD8mDUNdqA
+xGK7vlk4GATSfOQlq7G6LXW6RYnInohGBBMRAgAGBQJEazMlAAoJEKhO2uicgArK
+2vMAn0TbVDESEVKVuFZStrfIzOvJQrR9AJsH733Ju1kE99GFrdfCeGqpckmNhg==
+=E+qN
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/license.tar.gz b/tests/data/openSUSE-11.1/license.tar.gz
new file mode 100644 (file)
index 0000000..87c8208
Binary files /dev/null and b/tests/data/openSUSE-11.1/license.tar.gz differ
diff --git a/tests/data/openSUSE-11.1/media.1/media b/tests/data/openSUSE-11.1/media.1/media
new file mode 100644 (file)
index 0000000..e9c8128
--- /dev/null
@@ -0,0 +1,3 @@
+SuSE Linux Products GmbH
+20080918201559
+1
diff --git a/tests/data/openSUSE-11.1/media.1/products b/tests/data/openSUSE-11.1/media.1/products
new file mode 100644 (file)
index 0000000..ec71eef
--- /dev/null
@@ -0,0 +1 @@
+/ openSUSE-dvd-11.1
diff --git a/tests/data/openSUSE-11.1/media.1/products.asc b/tests/data/openSUSE-11.1/media.1/products.asc
new file mode 100644 (file)
index 0000000..0de16cd
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBI0qRLHHIcJFa0F3oRAm+dAKCLULReuAU6ZgUPJaYRwKuU9tqFfgCg
+mqQniBKmBYN23YM/PTIUhH7yP1g=
+=2Ub2
+-----END PGP SIGNATURE-----
diff --git a/tests/data/openSUSE-11.1/media.1/products.key b/tests/data/openSUSE-11.1/media.1/products.key
new file mode 100644 (file)
index 0000000..f0bb55a
--- /dev/null
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEeWWzMRBADU8l2IckSzgdUS1dn0WMM7wXK4seoFsHHQw/0unHCQCxpyDMnn
+TKV86p5KTbR1FDdeaZlY0yCV+IGsiIxLyuUdJn8vuA5gA5ZkUr89/HtWaeZVl77J
+HIQxvhDRBWCOO4QNtrZYWvGbvl83wl/zOfdLEs8IGElt0LgfohyTA1qfrwCg/Hac
+tDDscXsPlo5Jek/+3RHVeD0D/30riCpfpLJOmhraLg1EbWsE0mN9IQsl+WDPdoYo
+bB76z6eH3e38618WzP/LTG4WuVbwpSSqmXyfdVpXxWzESfT8q0B8CGpHf/Sa/T/L
+emohmRnLvkf/tAfxFmDMm1jOewJIE9S35jANGHVJcxmfRNpPWC7uHnqjopnsmDkL
+kMEdBAC6YcpDOcMJZ9sJbt/JNZBaoT5CltgMDlSN50t2v/J5em8qMLqCSNF5UJyd
+LFnePHTHy6gVjWbqcC0ncFzOqM1y644Up7BoKSAr1hRTl6Mw9S3UfZZZ0al3JtWt
+8y0eFIW3QP66w1AL0LO2bZMBuOvhb63DXv5iHorcxk0yIFbbybRCb3BlblNVU0U6
+RmFjdG9yeSBPQlMgUHJvamVjdCA8b3BlblNVU0U6RmFjdG9yeUBidWlsZC5vcGVu
+c3VzZS5vcmc+iGYEExECACYFAkeWWzMCGwMFCQQesAAGCwkIBwMCBBUCCAMEFgID
+AQIeAQIXgAAKCRAcchwkVrQXevBsAKCOeScnlH2fWVBJGHTOVJ3M4yBqDACbBeNk
+PuWo05AOQ3M1dLE1hkN36G+IRgQTEQIABgUCR5ZbMwAKCRA7MBG3a51lI7PfAKCc
+9ZtKfI5G/g66V7pSMXh9gi+ykgCgivPfGMDh9HIROwBIudo2qGImOqI=
+=htdw
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/dvd-11.1-46.1.x86_64.pat.gz b/tests/data/openSUSE-11.1/suse/setup/descr/dvd-11.1-46.1.x86_64.pat.gz
new file mode 100644 (file)
index 0000000..5082f14
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/dvd-11.1-46.1.x86_64.pat.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/non_oss-11.1-46.1.x86_64.pat.gz b/tests/data/openSUSE-11.1/suse/setup/descr/non_oss-11.1-46.1.x86_64.pat.gz
new file mode 100644 (file)
index 0000000..3885acd
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/non_oss-11.1-46.1.x86_64.pat.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/packages.DU.gz b/tests/data/openSUSE-11.1/suse/setup/descr/packages.DU.gz
new file mode 100644 (file)
index 0000000..7e1c240
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/packages.DU.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/packages.cs.gz b/tests/data/openSUSE-11.1/suse/setup/descr/packages.cs.gz
new file mode 100644 (file)
index 0000000..47f641b
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/packages.cs.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/packages.en.gz b/tests/data/openSUSE-11.1/suse/setup/descr/packages.en.gz
new file mode 100644 (file)
index 0000000..8cf954f
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/packages.en.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/packages.gz b/tests/data/openSUSE-11.1/suse/setup/descr/packages.gz
new file mode 100644 (file)
index 0000000..d070c7e
Binary files /dev/null and b/tests/data/openSUSE-11.1/suse/setup/descr/packages.gz differ
diff --git a/tests/data/openSUSE-11.1/suse/setup/descr/patterns b/tests/data/openSUSE-11.1/suse/setup/descr/patterns
new file mode 100644 (file)
index 0000000..2630225
--- /dev/null
@@ -0,0 +1,2 @@
+dvd-11.1-46.1.x86_64.pat.gz
+non_oss-11.1-46.1.x86_64.pat.gz
diff --git a/tests/lib/CMakeLists.txt b/tests/lib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5ff5932
--- /dev/null
@@ -0,0 +1,10 @@
+
+INCLUDE_DIRECTORIES( ${LIBZYPP_SOURCE_DIR}/vendor/mongoose )
+
+ADD_LIBRARY(zypp_test_utils
+ TestSetup.h
+ WebServer.h
+ WebServer.cc
+)
+
+TARGET_LINK_LIBRARIES(zypp_test_utils mongoose zypp boost_thread-mt)
diff --git a/tests/lib/TestSetup.h b/tests/lib/TestSetup.h
new file mode 100644 (file)
index 0000000..3015ddd
--- /dev/null
@@ -0,0 +1,412 @@
+#ifndef INCLUDE_TESTSETUP
+#define INCLUDE_TESTSETUP
+#include <iostream>
+
+#ifndef INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include <boost/test/auto_unit_test.hpp>
+using boost::unit_test::test_case;
+#endif
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/Flags.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZYpp.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Glob.h"
+#include "zypp/PathInfo.h"
+#include "zypp/RepoManager.h"
+#include "zypp/Target.h"
+#include "zypp/ResPool.h"
+
+using std::cin;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::flush;
+using namespace zypp;
+
+#ifndef BOOST_CHECK_NE
+#define BOOST_CHECK_NE( L, R ) BOOST_CHECK( (L) != (R) )
+#endif
+
+#define LABELED(V) #V << ":\t" << V
+
+inline std::string getXmlNodeVal( const std::string & line_r, const std::string & node_r )
+{
+  std::string::size_type pos = line_r.find( node_r + "=\"" );
+  if ( pos != std::string::npos )
+  {
+    pos += node_r.size() + 2;
+    std::string::size_type epos = line_r.find( "\"", pos );
+    return line_r.substr( pos, epos-pos );
+  }
+  return std::string();
+}
+
+enum TestSetupOptionBits
+{
+  TSO_CLEANROOT = (1 <<  0)
+};
+ZYPP_DECLARE_FLAGS_AND_OPERATORS( TestSetupOptions, TestSetupOptionBits );
+
+/** Build a test environment below a temp. root directory.
+ * If a \c rootdir_r was provided to the ctor, this directory
+ * will be used and it will \b not be removed.
+ *
+ * \note The lifetime of this objects is the lifetime of the temp. root directory.
+ *
+ * \code
+ * #include "TestSetup.h"
+ *
+ * BOOST_AUTO_TEST_CASE(WhatProvides)
+ * {
+ *   // enabls loging fot the scope of this block:
+ *   // base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
+ *
+ *   TestSetup test( Arch_x86_64 );
+ *   // test.loadTarget(); // initialize and load target
+ *   test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
+ *
+ *   // Here the pool is ready to be used.
+ *
+ * }
+ * \endcode
+*/
+class TestSetup
+{
+  public:
+    typedef TestSetupOptions Options;
+
+  public:
+    TestSetup( const Arch & sysarch_r = Arch_empty )
+    { _ctor( Pathname(), sysarch_r, Options() ); }
+
+    TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
+    { _ctor( rootdir_r, sysarch_r, options_r ); }
+
+    TestSetup( const Pathname & rootdir_r, const Options & options_r )
+    { _ctor( rootdir_r, Arch_empty, options_r ); }
+
+    ~TestSetup()
+    { USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl; }
+
+  public:
+    /** Whether directory \a path_r contains a solver testcase. */
+    static bool isTestcase( const Pathname & path_r )
+    {
+      return filesystem::PathInfo( path_r / "solver-test.xml" ).isFile();
+    }
+
+    /** Whether directory \a path_r contains a testsetup. */
+    static bool isTestSetup( const Pathname & path_r )
+    {
+      return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
+    }
+
+  public:
+    const Pathname & root() const { return _rootdir; }
+
+    Target &     target()      { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _rootdir ); return *getZYpp()->getTarget(); }
+    RepoManager  repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _rootdir ) ); }
+    ResPool      pool()        { return ResPool::instance(); }
+    ResPoolProxy poolProxy()   { return pool().proxy(); }
+    sat::Pool    satpool()     { return sat::Pool::instance(); }
+    Resolver &   resolver()    { return *getZYpp()->resolver(); }
+
+  public:
+    /** Load target repo. */
+    void loadTarget()
+    { target().load(); }
+    /** Fake @System repo from url. */
+    void loadTargetRepo( const Url & url_r )
+    { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
+    /** Fake @System repo from Path. */
+    void loadTargetRepo( const Pathname & path_r )
+    { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
+    /** Fake @System repo from helix repo. */
+    void loadTargetHelix( const Pathname & path_r )
+    { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
+
+  public:
+    /** Directly load repoinfo to pool. */
+    void loadRepo( RepoInfo nrepo )
+    {
+      RepoManager rmanager( repomanager() );
+      if ( rmanager.hasRepo( nrepo ) )
+        nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
+      rmanager.addRepository( nrepo );
+      rmanager.buildCache( nrepo );
+      rmanager.loadFromCache( nrepo );
+    }
+    /** Directly load repo from url to pool. */
+    void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
+    {
+      RepoInfo nrepo;
+      nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
+      nrepo.addBaseUrl( url_r );
+      nrepo.setGpgCheck( false );
+      loadRepo( nrepo );
+    }
+    /** Directly load repo from metadata(dir) or solvfile(file) to pool.
+     * An empty alias is guessed.
+    */
+    void loadRepo( const Pathname & path_r, const std::string & alias_r = std::string() )
+    {
+      if ( filesystem::PathInfo( path_r ).isDir() )
+      {
+        loadRepo( path_r.asUrl(), alias_r );
+        return;
+      }
+      // .solv file is loaded directly using a faked RepoInfo
+      RepoInfo nrepo;
+      nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
+      satpool().addRepoSolv( path_r, nrepo );
+    }
+    /** Directly load repo from some location (url or absolute(!)path).
+     * An empty alias is guessed.
+    */
+    void loadRepo( const std::string & loc_r, const std::string & alias_r = std::string() )
+    {
+      if ( *loc_r.c_str() == '/' )
+      {
+        loadRepo( Pathname( loc_r ), alias_r );
+      }
+      else
+      {
+        loadRepo( Url( loc_r ), alias_r );
+      }
+    }
+    /** Directly load repo from some location (url or absolute(!)path).
+     * An empty alias is guessed.
+    */
+    void loadRepo( const char * loc_r, const std::string & alias_r = std::string() )
+    { loadRepo( std::string( loc_r ? loc_r : "" ), alias_r ); }
+
+  private:
+    // repo data from solver-test.xml
+    struct RepoD {
+      DefaultIntegral<unsigned,0> priority;
+      std::string alias;
+      Url url;
+    };
+
+  public:
+    /** Directly load a helix repo from some testcase.
+     * An empty alias is guessed.
+     */
+    void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
+    {
+      // .solv file is loaded directly using a faked RepoInfo
+      RepoInfo nrepo;
+      nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
+      satpool().addRepoHelix( path_r, nrepo );
+    }
+
+    // Load repos included in a solver testcase.
+    void loadTestcaseRepos( const Pathname & path_r )
+    {
+      filesystem::PathInfo pi( path_r / "solver-test.xml" );
+      if ( ! pi.isFile() )
+      {
+        ERR << "No testcase in " << filesystem::PathInfo( path_r ) << endl;
+        return;
+      }
+      // dumb parse
+      InputStream infile( pi.path() );
+      Arch sysarch( Arch_empty );
+      Url guessedUrl;
+      typedef std::map<std::string,RepoD> RepoI;
+      RepoI repoi;
+      for( iostr::EachLine in( infile ); in; in.next() )
+      {
+        if ( str::hasPrefix( *in, "\t<channel" ) )
+        {
+          RepoD & repod( repoi[getXmlNodeVal( *in, "file" )] );
+
+          repod.alias = getXmlNodeVal( *in, "name" );
+          repod.priority = str::strtonum<unsigned>( getXmlNodeVal( *in, "priority" ) );
+          repod.url = guessedUrl;
+          guessedUrl = Url();
+        }
+        else if ( str::hasPrefix( *in, "\t- url " ) )
+        {
+          std::string::size_type pos = in->find( ": " );
+          if ( pos != std::string::npos )
+          {
+            guessedUrl = Url( in->substr( pos+2 ) );
+          }
+        }
+        else if ( str::hasPrefix( *in, "\t<locale" ) )
+        {
+          satpool().addRequestedLocale( Locale( getXmlNodeVal( *in, "name" ) ) );
+        }
+       else if ( sysarch.empty() && str::hasPrefix( *in, "<setup" ) )
+        {
+          sysarch = Arch( getXmlNodeVal( *in, "arch" ) );
+          if ( ! sysarch.empty() )
+            ZConfig::instance().setSystemArchitecture( sysarch );
+        }
+      }
+
+      //
+      filesystem::Glob files( path_r/"*{.xml,.xml.gz}", filesystem::Glob::_BRACE );
+      for_( it, files.begin(), files.end() )
+      {
+        std::string basename( Pathname::basename( *it ) );
+        if ( str::hasPrefix( basename, "solver-test.xml" ) )
+          continue; // master index currently unevaluated
+        if ( str::hasPrefix( basename, "solver-system.xml" ) )
+          loadTargetHelix( *it );
+        else
+        {
+          const RepoD & repod( repoi[basename] );
+
+          RepoInfo nrepo;
+          nrepo.setAlias( repod.alias.empty() ? basename : repod.alias );
+          nrepo.setPriority( repod.priority );
+          nrepo.setBaseUrl( repod.url );
+          satpool().addRepoHelix( *it, nrepo );
+        }
+      }
+
+      poolProxy(); // prepare
+    }
+
+  public:
+    /** Load all enabled repos in repos.d to pool. */
+    void loadRepos()
+    {
+      RepoManager repoManager( repomanager() );
+      RepoInfoList repos = repoManager.knownRepositories();
+      for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
+      {
+        RepoInfo & nrepo( *it );
+        USR << nrepo << endl;
+
+        if ( ! nrepo.enabled() )
+          continue;
+
+        if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
+        {
+          if ( repoManager.isCached( nrepo ) )
+          {
+            USR << "cleanCache" << endl;
+            repoManager.cleanCache( nrepo );
+          }
+          //USR << "refreshMetadata" << endl;
+          //repoManager.refreshMetadata( nrepo );
+          USR << "buildCache" << endl;
+          repoManager.buildCache( nrepo );
+        }
+        USR << "Create from cache" << endl;
+        repoManager.loadFromCache( nrepo );
+      }
+    }
+
+  public:
+    /** Detect and load the system located at \a sysRoot.
+     *
+     * \a sysRoot needs to be a directory containing either a SolverTestcase,
+     * a TestSetup system or a real system. The  provided repostitories are
+     * loaded into the pool (without refresh).
+    */
+    static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
+    {
+      if ( ! PathInfo( sysRoot ).isDir() )
+        ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
+
+      if ( TestSetup::isTestcase( sysRoot ) )
+      {
+        USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
+        TestSetup test;
+        test.loadTestcaseRepos( sysRoot );
+      }
+      else if ( TestSetup::isTestSetup( sysRoot ) )
+      {
+        USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
+
+        TestSetup test( sysRoot, _testSetupArch_r );
+        test.loadRepos();
+
+        Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
+        Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
+        if ( PathInfo( fakeTargetSolv ).isFile() )
+        {
+          USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
+          test.target();
+          test.loadTargetRepo( fakeTargetSolv );
+        }
+      }
+      else
+      {
+        sat::Pool satpool( sat::Pool::instance() );
+        // a system
+        USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
+        if ( 1 )
+        {
+          USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
+          getZYpp()->initializeTarget( sysRoot );
+          getZYpp()->target()->load();
+          USR << satpool.systemRepo() << endl;
+        }
+
+        if ( 1 )
+        {
+          RepoManager repoManager( sysRoot );
+          RepoInfoList repos = repoManager.knownRepositories();
+          for_( it, repos.begin(), repos.end() )
+          {
+            RepoInfo & nrepo( *it );
+
+            if ( ! nrepo.enabled() )
+              continue;
+
+            if ( ! repoManager.isCached( nrepo ) )
+            {
+              USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+              continue;
+            }
+
+            USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
+            try
+            {
+              repoManager.loadFromCache( nrepo );
+              USR << satpool.reposFind( nrepo.alias() ) << endl;
+            }
+            catch ( const Exception & exp )
+            {
+              USR << exp.asString() + "\n" + exp.historyAsString() << endl;
+              USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+              continue;
+            }
+          }
+        }
+      }
+    }
+
+  private:
+    void _ctor( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
+    {
+      if ( rootdir_r.empty() )
+        _rootdir = _tmprootdir.path();
+      else
+      {
+        filesystem::assert_dir( (_rootdir = rootdir_r) );
+        if ( options_r.testFlag( TSO_CLEANROOT ) )
+          filesystem::clean_dir( _rootdir );
+      }
+
+      if ( ! sysarch_r.empty() )
+        ZConfig::instance().setSystemArchitecture( sysarch_r );
+      USR << "CREATED TESTSETUP below " << _rootdir << endl;
+    }
+  private:
+    filesystem::TmpDir _tmprootdir;
+    Pathname           _rootdir;
+};
+
+
+#endif //INCLUDE_TESTSETUP
diff --git a/tests/lib/WebServer.cc b/tests/lib/WebServer.cc
new file mode 100644 (file)
index 0000000..4a215ed
--- /dev/null
@@ -0,0 +1,258 @@
+
+#include <sstream>
+#include <string>
+#include "boost/bind.hpp"
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ExternalProgram.h"
+#include "WebServer.h"
+
+#include "mongoose.h"
+
+using namespace zypp;
+using namespace std;
+
+static inline string hostname()
+{
+    static char buf[256];
+    string result;
+    if (!::gethostname(buf, 255))
+        result += string(buf);
+    else
+        return "localhost";
+    return result;
+}
+
+#define WEBRICK 0
+
+class WebServer::Impl
+{
+public:
+    Impl()
+    {}
+    
+    virtual ~Impl()
+    {}
+    
+    virtual string log() const
+    { return string(); }
+            
+    virtual void start()
+    {}
+
+    virtual void stop()
+    {}
+    
+    virtual void worker_thread()
+    {}
+
+    virtual int port() const
+    {
+        return 0;
+    }
+    
+
+    
+private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+};    
+
+class WebServerWebrickImpl : public WebServer::Impl
+{
+public:
+    WebServerWebrickImpl(const Pathname &root, unsigned int port)
+        : _docroot(root), _port(port), _stop(false), _stopped(true)
+    {
+    }
+    
+    ~WebServerWebrickImpl()
+    {
+        if ( ! _stopped )
+            stop();
+    }
+
+    virtual int port() const
+    {
+        return _port;
+    }
+
+    
+    virtual void worker_thread()
+    {
+        _log.clear();
+            
+        stringstream strlog(_log);
+
+        string webrick_code = str::form("require \"webrick\"; s = WEBrick::HTTPServer.new(:Port => %d, :DocumentRoot    => \"%s\"); trap(\"INT\"){ s.shutdown }; trap(\"SIGKILL\") { s.shutdown }; s.start;", _port, _docroot.c_str());
+    
+        const char* argv[] =
+        {
+            "/usr/bin/ruby",
+            "-e",
+            webrick_code.c_str(),
+            NULL
+        };
+
+        ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+        string line;
+    
+        _stopped = false;
+    
+        while ( ! _stop );
+        
+        MIL << "Thread end requested" << endl;
+        //prog.close();
+        if ( prog.running() )
+            prog.kill();
+        MIL << "Thread about to finish" << endl;
+    }
+    
+    virtual string log() const
+    {
+        return _log;
+    }
+
+    virtual void stop()
+    {
+        MIL << "Waiting for Webrick thread to finish" << endl;
+        _stop = true;
+        _thrd->join();
+        MIL << "Webrick thread finished" << endl;
+        _thrd.reset();
+        _stopped = true;
+    }
+    
+    virtual void start()
+    {
+        _thrd.reset( new boost::thread( boost::bind(&WebServerWebrickImpl::worker_thread, this) ) );
+    }
+    
+    zypp::Pathname _docroot;
+    unsigned int _port;
+    zypp::shared_ptr<boost::thread> _thrd;
+    bool _stop;
+    bool _stopped;
+    std::string _log;
+};
+
+class WebServerMongooseImpl : public WebServer::Impl
+{
+public:
+    WebServerMongooseImpl(const Pathname &root, unsigned int port)
+        : _ctx(0L), _docroot(root)
+        , _port(port)
+        , _stopped(true)
+    {
+    }
+    
+    ~WebServerMongooseImpl()
+    {
+        MIL << "Destroying web server" << endl;
+        
+        if ( ! _stopped )
+            stop();
+    }
+    
+    virtual void start()
+    {
+        if ( ! _stopped )
+        {
+            MIL << "mongoose server already running, stopping." << endl;
+            stop();
+        }
+        
+        MIL << "Starting shttpd (mongoose)" << endl;
+        _log.clear();
+        _ctx = mg_start();
+
+        int ret = 0;
+        ret = mg_set_option(_ctx, "ports", str::form("%d", _port).c_str());
+        if (  ret != 1 )
+            ZYPP_THROW(Exception(str::form("Failed to set port: %d", ret)));
+        
+        MIL << "Setting root directory to : '" << _docroot << "'" << endl;
+        ret = mg_set_option(_ctx, "root", _docroot.c_str());
+        if (  ret != 1 )
+            ZYPP_THROW(Exception(str::form("Failed to set docroot: %d", ret)));
+
+        _stopped = false;
+    }
+
+    virtual int port() const
+    {
+        return _port;
+    }
+
+   
+    virtual string log() const
+    {
+        return _log;
+    }
+    
+    virtual void stop()
+    {
+        MIL << "Stopping shttpd" << endl;
+        mg_stop(_ctx);
+        MIL << "shttpd finished" << endl;
+        _ctx = 0;
+        _stopped = true;
+    }
+    
+    mg_context *_ctx;
+    zypp::Pathname _docroot;
+    unsigned int _port;
+    bool _stopped;
+    std::string _log;
+};
+
+
+WebServer::WebServer(const Pathname &root, unsigned int port)
+#if WEBRICK
+    : _pimpl(new WebServerWebrickImpl(root, port))
+#else
+    : _pimpl(new WebServerMongooseImpl(root, port))
+#endif
+{    
+}
+
+void WebServer::start()
+{
+    _pimpl->start();
+}
+
+
+std::string WebServer::log() const
+{
+    return _pimpl->log();
+}
+
+int WebServer::port() const
+{
+    return _pimpl->port();
+}
+
+
+Url WebServer::url() const
+{
+    Url url;
+    url.setHost(hostname());
+    url.setPort(str::numstring(port()));
+    url.setScheme("http");
+    return url;
+}
+
+void WebServer::stop()
+{
+    _pimpl->stop();
+}
+
+WebServer::~WebServer()
+{
+}
+
+
diff --git a/tests/lib/WebServer.h b/tests/lib/WebServer.h
new file mode 100644 (file)
index 0000000..ec62e41
--- /dev/null
@@ -0,0 +1,74 @@
+
+#ifndef ZYPP_TEST_WEBSERVER_H
+#define ZYPP_TEST_WEBSERVER_H
+
+#include "boost/thread.hpp"
+#include "boost/smart_ptr.hpp"
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/base/PtrTypes.h"
+
+/**
+ *
+ * Starts a webserver to simulate remote transfers in
+ * testcases
+ * \author Duncan Mac-Vicar P. <dmacvicar@suse.de>
+ *
+ * \code
+ * #include "WebServer.h"
+ *
+ * BOOST_AUTO_TEST_CASE(Foo)
+ * {
+ *
+ *      WebServer web((Pathname(TESTS_SRC_DIR) + "/datadir").c_str() );
+ *      web.start();
+ *
+ *     MediaSetAccess media( Url("http://localhost:9099"), "/" );
+ *     
+ *     // do something with the url
+ * 
+ *
+ *     web.stop();
+ *
+ * \endcode
+ */
+class WebServer
+{
+ public:
+  /**
+   * creates a web server on \ref root and \port
+   */
+  WebServer(const zypp::Pathname &root, unsigned int port=10001);
+  ~WebServer();
+  /**
+   * Starts the webserver worker thread
+   */
+  void start();
+  /**
+   * Stops the worker thread
+   */
+  void stop();
+
+  /**
+   * returns the port we are listening to
+   */
+  int port() const;
+
+  /**
+   * returns the base url where the webserver is listening
+   */
+  zypp::Url url() const;
+  /**
+   * shows the log of last run
+   */
+  std::string log() const;
+
+  class Impl;
+private:
+  /** Pointer to implementation */
+  zypp::RWCOW_pointer<Impl> _pimpl;
+};
+
+#endif
diff --git a/tests/media/CMakeLists.txt b/tests/media/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f524afe
--- /dev/null
@@ -0,0 +1,3 @@
+ADD_TESTS(CredentialManager CredentialFileReader MetaLinkParser)
+
+#ADD_TESTS(media1 media2 media3 media4 file_exists throw_if_not_exists)
diff --git a/tests/media/CredentialFileReader_test.cc b/tests/media/CredentialFileReader_test.cc
new file mode 100644 (file)
index 0000000..0dedd77
--- /dev/null
@@ -0,0 +1,38 @@
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+#include <set>
+
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/Easy.h"
+#include "zypp/media/MediaUserAuth.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::media;
+
+typedef std::set<AuthData_Ptr> CredentialSet;
+
+struct CredCollector
+{
+  bool collect(AuthData_Ptr & cred)
+  {
+    cout << "got: " << endl << *cred << endl;
+    creds.insert(cred);
+    return true;
+  }
+
+  CredentialSet creds;
+};
+
+BOOST_AUTO_TEST_CASE(read_cred)
+{
+  CredCollector collector;
+  Pathname credfile = TESTS_SRC_DIR "/media/data/credentials.cat";
+  CredentialFileReader reader(credfile,
+      bind( &CredCollector::collect, &collector, _1 ));
+
+  BOOST_CHECK(collector.creds.size() == 2);
+}
diff --git a/tests/media/CredentialManager_test.cc b/tests/media/CredentialManager_test.cc
new file mode 100644 (file)
index 0000000..3af6e66
--- /dev/null
@@ -0,0 +1,127 @@
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/Url.h"
+#include "zypp/TmpPath.h"
+#include "zypp/media/CredentialFileReader.cc"
+#include "zypp/media/CredentialManager.h"
+
+#include "zypp/PathInfo.h"
+
+using std::cout;
+using std::endl;
+using namespace zypp;
+using namespace zypp::media;
+
+
+BOOST_AUTO_TEST_CASE(read_cred_for_url)
+{
+  CredManagerOptions opts;
+  opts.globalCredFilePath = TESTS_SRC_DIR "/media/data/credentials.cat";
+  opts.userCredFilePath = Pathname();
+
+  CredentialManager cm(opts);
+  BOOST_CHECK(cm.credsGlobalSize() == 2);
+
+  Url url("https://drink.it/repo/roots");
+  AuthData_Ptr credentials = cm.getCred(url);
+  BOOST_CHECK(credentials.get() != NULL);
+  if (!credentials)
+    return;
+  BOOST_CHECK(credentials->username() == "ginger");
+  BOOST_CHECK(credentials->password() == "ale");
+
+  Url url2("ftp://magda@weprovidesoft.fr/download/opensuse/110");
+  credentials = cm.getCred(url2);
+  BOOST_CHECK(credentials.get() != NULL);
+  if (!credentials)
+    return;
+  BOOST_CHECK(credentials->username() == "magda");
+  BOOST_CHECK(credentials->password() == "richard");
+}
+
+struct CredCollector
+{
+  bool collect(AuthData_Ptr & cred)
+  {
+    cout << "got: " << endl << *cred << endl;
+    creds.insert(cred);
+    return true;
+  }
+
+  CredentialManager::CredentialSet creds;
+};
+
+
+BOOST_AUTO_TEST_CASE(save_creds)
+{
+  filesystem::TmpDir tmp;
+
+  CredManagerOptions opts;
+  opts.globalCredFilePath = tmp / "fooha";
+
+  CredentialManager cm1(opts);
+  AuthData cr1("benson","absolute");
+  cr1.setUrl(Url("http://joooha.com"));
+  AuthData cr2("pat","vymetheny");
+  cr2.setUrl(Url("ftp://filesuck.org"));
+
+  // should create a new file
+  cm1.saveInGlobal(cr1);
+
+  CredCollector collector;
+  CredentialFileReader reader(opts.globalCredFilePath,
+      bind( &CredCollector::collect, &collector, _1 ));
+  BOOST_CHECK(collector.creds.size() == 1);
+
+  cout << "----" << endl;
+  collector.creds.clear();
+
+  cm1.saveInGlobal(cr2);
+  
+  CredentialFileReader reader1(opts.globalCredFilePath,
+      bind( &CredCollector::collect, &collector, _1 ));
+  BOOST_CHECK(collector.creds.size() == 2);
+  
+  cout << "----" << endl;
+  collector.creds.clear();
+
+  // save the same creds again
+  cm1.saveInGlobal(cr2);
+  CredentialFileReader reader2(opts.globalCredFilePath,
+      bind( &CredCollector::collect, &collector, _1 ));
+  BOOST_CHECK(collector.creds.size() == 2);
+
+  // todo check created file permissions
+}
+
+BOOST_AUTO_TEST_CASE(service_base_url)
+{
+  filesystem::TmpDir tmp;
+
+  CredManagerOptions opts;
+  opts.globalCredFilePath = tmp / "fooha";
+
+  CredentialManager cm1(opts);
+  AuthData cr1("benson","absolute");
+  cr1.setUrl(Url("http://joooha.com/service/path"));
+  cm1.addGlobalCred(cr1);
+
+  AuthData_Ptr creds;
+  creds = cm1.getCred(Url("http://joooha.com/service/path/repo/repofoo"));
+
+  BOOST_CHECK(creds.get() != NULL);
+  if (!creds)
+    return;
+  BOOST_CHECK(creds->username() == "benson");
+
+  creds = cm1.getCred(Url("http://benson@joooha.com/service/path/repo/repofoo"));
+
+  BOOST_CHECK(creds.get() != NULL);
+  if (!creds)
+    return;
+  BOOST_CHECK(creds->username() == "benson");
+
+  creds = cm1.getCred(Url("http://nobody@joooha.com/service/path/repo/repofoo"));
+  BOOST_CHECK(creds.get() == NULL);
+}
diff --git a/tests/media/MetaLinkParser_test.cc b/tests/media/MetaLinkParser_test.cc
new file mode 100644 (file)
index 0000000..42c410c
--- /dev/null
@@ -0,0 +1,41 @@
+#include <iostream>
+#include <vector>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/media/MetaLinkParser.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::media;
+
+BOOST_AUTO_TEST_CASE(parse_metalink)
+{
+  Pathname meta3file = TESTS_SRC_DIR "/media/data/openSUSE-11.3-NET-i586.iso.metalink";
+  Pathname meta4file = TESTS_SRC_DIR "/media/data/openSUSE-11.3-NET-i586.iso.meta4";
+
+       MetaLinkParser mlp3;
+       MetaLinkParser mlp4;
+
+  mlp3.parse(meta3file);
+  MediaBlockList bl3 = mlp3.getBlockList();
+  vector<Url> urls3 = mlp3.getUrls();
+
+  mlp4.parse(meta4file);
+  MediaBlockList bl4 = mlp4.getBlockList();
+  vector<Url> urls4 = mlp4.getUrls();
+
+
+  BOOST_CHECK(bl3.asString() == bl4.asString());
+
+  BOOST_CHECK(urls3.size() == 94);
+  BOOST_CHECK(urls4.size() == 94);
+
+  BOOST_CHECK(urls3.begin()->asString() == "http://ftp.uni-kl.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso");
+  BOOST_CHECK(urls4.begin()->asString() == "http://ftp4.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso");
+
+  BOOST_CHECK(bl3.getFilesize() == 120285184);
+  BOOST_CHECK(bl4.getFilesize() == 120285184);
+
+  BOOST_CHECK(bl3.numBlocks() == 459);
+  BOOST_CHECK(bl4.numBlocks() == 459);
+}
diff --git a/tests/media/data/credentials.cat b/tests/media/data/credentials.cat
new file mode 100644 (file)
index 0000000..23ddee1
--- /dev/null
@@ -0,0 +1,15 @@
+[https://drink.it/repo/roots]
+username=ginger
+password=ale
+
+[ftp://weprovidesoft.fr/download/opensuse/110]
+username=magda
+password=richard
+
+[http://url.ok/but/not/creds]
+username=
+password=any
+
+[badurl]
+username=foo
+password=bar
diff --git a/tests/media/data/credentials.d/cred1 b/tests/media/data/credentials.d/cred1
new file mode 100644 (file)
index 0000000..24b3dc4
--- /dev/null
@@ -0,0 +1,2 @@
+username=helene
+password=elena
diff --git a/tests/media/data/openSUSE-11.3-NET-i586.iso.meta4 b/tests/media/data/openSUSE-11.3-NET-i586.iso.meta4
new file mode 100644 (file)
index 0000000..3678e76
--- /dev/null
@@ -0,0 +1,602 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink xmlns="urn:ietf:params:xml:ns:metalink">
+  <generator>MirrorBrain/2.13.2</generator>
+  <origin dynamic="true">http://download.opensuse.org/distribution/openSUSE-current/iso/openSUSE-11.3-NET-i586.iso.meta4</origin>
+  <published>2010-10-13T09:39:21Z</published>
+  <publisher>
+    <name>openSUSE</name>
+    <url>http://download.opensuse.org</url>
+  </publisher>
+
+  <file name="openSUSE-11.3-NET-i586.iso">
+    <size>120285184</size>
+
+    <!-- <mtime>1278483002</mtime> -->
+
+    <!-- internal id: 9644850 -->
+    <signature mediatype="application/pgp-signature">
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBMNZQUqE7a6JyACsoRAlr2AJ9o5fkD0DZ2MpyWC0S9EI6c7BXuPQCd
+ElMAtuZlO5NgYvG2bQ8xys5iWss=
+=WmmH
+-----END PGP SIGNATURE-----
+    </signature>
+    <hash type="md5">a0dc5f5132b0a26218f533d837d4fc1a</hash>
+    <hash type="sha-1">c7827b5a8e62d3971524ba0c438e9574e892103a</hash>
+    <hash type="sha-256">7db356f5e21547e423da0406b7ea36e3c6e4ee62975015294585593d3a2a88c8</hash>
+    <pieces length="262144" type="sha-1">
+      <hash>c5bc4f75f550e44491273720086bf1692c526cbd</hash>
+      <hash>bbe1177d325d1310868d34e2350666f9cb119e94</hash>
+      <hash>2a167f3a34a9a09577365b1015ca23b823f0a80d</hash>
+      <hash>48d2ba5258254dcd4e49140ac7857c9fdb38ab60</hash>
+      <hash>bde60d0206ac5a0198e898553b665f9d9c60babb</hash>
+      <hash>41437180a223068413028fd977ef4ea033bfbfce</hash>
+      <hash>81b7da73641a5e3391210bc9e3de1568a5208836</hash>
+      <hash>6f9c2895ec0a887fe2dabd521e40918244da0059</hash>
+      <hash>8bc4a733263a01658a7c58a53fb777b4c8b4ff28</hash>
+      <hash>ed21674a98ba5af37df44fe4ad0e140c82ac8d1e</hash>
+      <hash>3772fed4a8bacd4e20478375c9dc8784f9fef98f</hash>
+      <hash>62f0024c67ff6830a3355b9145f043c4d60b2f13</hash>
+      <hash>d9ac81f6753d14912ec643ad8e2982bd1e128c95</hash>
+      <hash>4f5369273b928b9aaee69736140b814ee9a228d7</hash>
+      <hash>d6b8bcaf68d07591774302417a3d2b4ee0948e60</hash>
+      <hash>c2a98d8a80ada0b7ecdf214ab6bd3331793cb120</hash>
+      <hash>d0725a3e8f0053cb76c1e4424cb2496092063836</hash>
+      <hash>50d34d802241b3b52937364a14d369f2644ea4f4</hash>
+      <hash>553ba2491229a1ccfd23d01e00df4d95e612d442</hash>
+      <hash>2943c0b02f824925e9b589bf3129f950c1e48e5d</hash>
+      <hash>5d4f21b818057eb23d9aa0e27e8fa5be4c1f3594</hash>
+      <hash>9630c21e84b85e27f9ee19e59ce1a633621a0b1b</hash>
+      <hash>ca3c553ec4b1310c1526b684d14c85640329cd1e</hash>
+      <hash>55bbc2e9d0a627b7481639f4d909118f6b811fc3</hash>
+      <hash>7843b05f31d95adac6f781f0de8e8af868490a76</hash>
+      <hash>479d93353bceea8744c7f1b4eac97b277e29ba30</hash>
+      <hash>28db10a11cf7fc317510230237b76df0dd0dc35c</hash>
+      <hash>128a62f1599591f9d7db1ce53d551a00555af265</hash>
+      <hash>582ff34a81fe4b3da54defa2b890946f1fce1646</hash>
+      <hash>03855419b291564e13510d81083d79816179e4f0</hash>
+      <hash>0e5a6eb83c9f358f4bb1e092f9a3bcef95dd0a41</hash>
+      <hash>f3e6e901bd37e3ec8d405a96128cc85bff450a83</hash>
+      <hash>dee45f794ec0fb5fb5a20826ba2d8d191a567733</hash>
+      <hash>390c6d1eaf5a28f93d7e8237e3972b9e93fe5cca</hash>
+      <hash>20336a02716605b12e6fafd18855cbae411a09eb</hash>
+      <hash>ef22857c452d0043adbc054dd4396c0838426ae2</hash>
+      <hash>574c3a9f656391757acb83fb5091dabeb78e2418</hash>
+      <hash>203e818753a68b0bea326ea1428972a92036d9d8</hash>
+      <hash>5b49222d21afb87a5de922e9c77ca255619d21ad</hash>
+      <hash>fdd7b232c5ba5fa75c30f0e628e506725869df25</hash>
+      <hash>360bb3a824926cb73357b7c587390867e944b28c</hash>
+      <hash>c0f194a68b2c1d8693a83ef162ad85f8c0bf93cd</hash>
+      <hash>25079830fe5d0c64d5c8f65c6beb61f2832995c8</hash>
+      <hash>fe5b97a5c807c66833a2f5c45a7c511299c8e7c5</hash>
+      <hash>64f287d9fc0c64c68c9b9f61711080b4072b11bc</hash>
+      <hash>fbd01b40db0d0d73c4346bfa6942cd23666d08d8</hash>
+      <hash>e73a60b7a8efde3d70da2935d85c3ebd6b623651</hash>
+      <hash>414299b4a2da9b50700d2d7d7721139291acd2b7</hash>
+      <hash>e2c03eac8cbd4ef1a1fde5b60c32f26c6760a32b</hash>
+      <hash>a44b4d98688b7dd787ee2c3029229ca3083b2633</hash>
+      <hash>635ab22c1507403e3ba9481b92c7637ebd58bb2e</hash>
+      <hash>bddcde253a718b50ab3e2517cc33938ce54baab4</hash>
+      <hash>bed44cc3cacd658389c4807edead76e71329dea9</hash>
+      <hash>efe42d31f00ced3aea5a9aef9a3820ec20ba9e73</hash>
+      <hash>d1b1f83099c80c9219386b38063588a94385ef5a</hash>
+      <hash>670abebd7cd7a0a62d4aeba3dee78c7512d2ea8c</hash>
+      <hash>f483d9fbe3d513be20aef6e8736c5755c9aea29e</hash>
+      <hash>46b4ce6520798c362a16ac3e293cbf94f979603a</hash>
+      <hash>8b5e93ad34582431b3efb89ec10e05ff4257c166</hash>
+      <hash>06e09067668ff5b9ebda474a71db44c9b8386eee</hash>
+      <hash>395843d5243a235c7803bba79eca803bb9c5c5a7</hash>
+      <hash>0a2cc9fe1a1a4591110f7235b1b9ca9eb4e59ec7</hash>
+      <hash>fe5ef70a92427bbded4cadcd74f648b7e33807c2</hash>
+      <hash>c743ddce2e930bcdf76db2f08f5a959f6f3cecd9</hash>
+      <hash>d5788b2dca4a6cb5d75cce8316db1e761c914da5</hash>
+      <hash>cf5c83859f55714544e42645db270d237b6d4499</hash>
+      <hash>497214fef565cfa04c2b8f96704be999b93df93f</hash>
+      <hash>2f4c7da4c7d25e6d8d63a995f5296c8295bc8d01</hash>
+      <hash>95550875564701777b6e6e02854865c991a121b6</hash>
+      <hash>c378b72cdc37c83d4f713e55915863babf949ba3</hash>
+      <hash>d67e326f31fcb79000e96b3032c1e16183e79d84</hash>
+      <hash>53b0067cc0e8eb9b856a11af29cf69ededfe4bfd</hash>
+      <hash>ebf37f7028112f669d244d55ae0290c003cc9cff</hash>
+      <hash>56e6482f5e0162ee16ee77496e583c9a0d5b0b31</hash>
+      <hash>772fb29f60ecd14b3c06a317258e86b6802a1310</hash>
+      <hash>b43a919ce9eae4c3f1b18f5d3f61328a9f67d885</hash>
+      <hash>cd93972f4cab7128bfefe836ac5601b2cced8a42</hash>
+      <hash>fe20173622a73d64f586f9594551bd5fe56ef690</hash>
+      <hash>2f22b34a49fef60f54f8f59789a439bc5232e0ab</hash>
+      <hash>cc1e04c724746e6058429c0610dafd639798bfab</hash>
+      <hash>95ba6eba3b22e6b473f7946c8c7aa66da6547ae7</hash>
+      <hash>2b7e254485c22686e0408ce76fa6b267aadf35ae</hash>
+      <hash>e35351bcda5aa019839b328106a60e24db7c5de7</hash>
+      <hash>47864400bc861e6102bdde3900342ee5ac9dfe51</hash>
+      <hash>f7bfb5331e63b5de7826d0fe5abb0a4e9dffa9b4</hash>
+      <hash>14cf61a6d461a67f569e0f4b99ebd21ce5925566</hash>
+      <hash>8a250ac80584f8acaf8af9a4a8d3f9f014999a12</hash>
+      <hash>a5731b1360d3a387576903da16ed50f390f00864</hash>
+      <hash>7c9a3b9f00754bc4da997730e75ddedbcdde5117</hash>
+      <hash>d3879d057ea46050c6286f6653c2bf86fb8938c3</hash>
+      <hash>a03f375f255e325f6a603934e15769044ecb25db</hash>
+      <hash>b8ac8d69c3bc16ad657ea2b4a7eace8ea6c60fd8</hash>
+      <hash>5c02fbca54bd8a035d59145d6d1be0be96f2197c</hash>
+      <hash>d43671790be7f5c39af346c8fd00003c135491a2</hash>
+      <hash>2381abd10331dfea9f5817487cf74923b040f307</hash>
+      <hash>734168c76e9770dd3f86338570d3e27ce7223251</hash>
+      <hash>859bf6ee62f7fcb94f052c594d470a61be63f084</hash>
+      <hash>98b4178ddd01b7959b0a117ccff0688489b8841d</hash>
+      <hash>954de9ea519a33bb4d32ae89f7cae93821e26c02</hash>
+      <hash>f41f1496594933eb8fb6869bff916d330f1c9152</hash>
+      <hash>d523b3606c19e8a0b161008846dac2d30fd92e4c</hash>
+      <hash>3fdf132ad91dcd4f9b466f304d51067baf2ee976</hash>
+      <hash>3dbde53bdcd43b022ed4575c40fa65ecd67603d0</hash>
+      <hash>cf977d863575f6f74a228a40e357a70a69700189</hash>
+      <hash>8c1fe8d6c2bc09f548c2059409d53bdeb5fc7d3f</hash>
+      <hash>cbfbcedeea1bfc1be031731f0c6de6b0897c84cc</hash>
+      <hash>0124bed97ba6c2970af5bff8ab82acb8affb4611</hash>
+      <hash>8cba87ed1e345d2312dd341ce7d1810305ccac41</hash>
+      <hash>6b6c38964701ba72e7838cea1d91c7ef91ec3f08</hash>
+      <hash>1f371667f596f11b40a46bcebd0ab6a90be2913d</hash>
+      <hash>708f8af393a828613c45eb5e408c0190b4c95622</hash>
+      <hash>d7682ba55ecd3abe1927b9e462fae1b4e9334800</hash>
+      <hash>459b073f48c2ba332fc39d3f47bd650a04c511ac</hash>
+      <hash>bb5d3039b65b985b375ff77e048fa690eca45cf4</hash>
+      <hash>ec3168ae0982393770fee2b55cef963a8c503644</hash>
+      <hash>0ba5f37dcd6bd6103203fe230d71ffda56502a88</hash>
+      <hash>80c37d76da053147a5267d03ee94e89d93fcf074</hash>
+      <hash>db4dccc57e290094edb2c9cc9f134b2e3077d22f</hash>
+      <hash>ea5981c1f1fb5c214ba0c9e82ec030c2a1bbf0a3</hash>
+      <hash>f92cf79330387d5b51b8efd6300179e1be15ae01</hash>
+      <hash>38e0eec3af20d607b15a9a21078373a338c1d4ba</hash>
+      <hash>9ee0001d0ee983caa4e1c469cdf77ed51a0ef9f2</hash>
+      <hash>c5a803fc6dd74028f63573e325d87c19001bea8d</hash>
+      <hash>11992d4f3a304d24962223cb14aeb9c67848e027</hash>
+      <hash>d7bb416faf5485896af7d349982a92cdf98e847d</hash>
+      <hash>632add982c1641009bafe85f691f6f21385cd34b</hash>
+      <hash>04bb6d36962f76334e71f90240d528e8a3ad459b</hash>
+      <hash>32ebc900994d00f4eda332631d687edf7982df0b</hash>
+      <hash>a302bf465ac38da7429548b80b288c70fe7f9ed4</hash>
+      <hash>ed87a36bfe5abd2bb0b0a4efb5932bfa425f80a0</hash>
+      <hash>f8c448216f59f523bcf33d2d99dc83e8ec7f46e1</hash>
+      <hash>82f5ffb770451bfd840d816946d9b59884d41b02</hash>
+      <hash>bd8faa855ca9377f46161b9427027ed8400eff3a</hash>
+      <hash>f680d78b7854ac0af443b879a880fe3c22cafbd5</hash>
+      <hash>5d357bb06899857d3e64468d79eef199882cc3d6</hash>
+      <hash>4be3cae2cd41742f8f33f80c41797a68b1510fb5</hash>
+      <hash>6d70253e7e22c6a30570d96b6cfb36772b33354f</hash>
+      <hash>ede70be18a4db080c3d046c5fd9f0bc6b625a772</hash>
+      <hash>14c8f1675f97244b14f12d6b32419a19cf814dbc</hash>
+      <hash>0f9943a582d5d514e9deb1253d4806156ad37c17</hash>
+      <hash>c0086d20b74ae26693716ec7bae9b530237d786b</hash>
+      <hash>356fe043c133aab0a922153f9cef068d90bf3f89</hash>
+      <hash>0d2420bcd3eabc17c319e73cb1c043fa47f8ac92</hash>
+      <hash>7b9496187417464ac70e4fa7a63a710eea14202a</hash>
+      <hash>fcf3ada8b083d0adffc0c5ecce437aaa483d4109</hash>
+      <hash>0b14dd454a228bf96d9a177fcc65234db93a740b</hash>
+      <hash>a7d2ed2809de3e928913a6a3f8efcf1f06f1f3ab</hash>
+      <hash>9752d14b59c1b60ad957de4982d84e0f3a398bcc</hash>
+      <hash>bdba58965077db889544e8bb086ddfa8d4394be2</hash>
+      <hash>93fa8444acd84d0a5580b553b9eec85a354aa4cb</hash>
+      <hash>dcdb227911b0bcd0ed6074711eb9ad6f686aef46</hash>
+      <hash>6cd9e2121fee8d0c0982d9976fc8b5eec99bed7c</hash>
+      <hash>e74ef9311f1a084f8d2625369cbd245872e7c7b5</hash>
+      <hash>d042115bc6a339f1bcd58d526c3617cc125311f3</hash>
+      <hash>fb68189e82b480de37bcb09a2e6ac699d7d81533</hash>
+      <hash>aacecb7a31699743342dce7e031818a809a6ef8a</hash>
+      <hash>431b9fe43c294a313042714f99e9a9ae59663992</hash>
+      <hash>fd84701b8a4453d227dffe54761824c4c690f1a4</hash>
+      <hash>a0d69bdc1b1d99b17e42fe9a0e48918344aa4e4e</hash>
+      <hash>3962f2a34b48853aa3885a25c5282640451b8935</hash>
+      <hash>66f1df2058e6b061623971b3c25f49a35a856f2e</hash>
+      <hash>06531afe357933551835fd816ce915c9737ae8fd</hash>
+      <hash>3642ad5a2bc42db69d495f0b4f506f5059761b94</hash>
+      <hash>7756a6cf4264d74e727fc84f377d4a7472f91469</hash>
+      <hash>a13af573eadb2998648ea050c65bcdc78e262ada</hash>
+      <hash>9ed650662bf9b8b236934799554d03b0b6ed64d4</hash>
+      <hash>404d94a3268699510b0dcddbc826bf724dca6658</hash>
+      <hash>85043d5ae0586b322f97310bc299aa9007101ee8</hash>
+      <hash>3c39c196c6ba4d90ba316ce3b451bffcc462a9dc</hash>
+      <hash>dbf5c2fcd32b32023a1431960ca8eac207b3e8c7</hash>
+      <hash>b2efc9445d6af07aaa64eb736815c1574abfff32</hash>
+      <hash>9e7443a1d9daa8c5a8e1db98e68fd839b7dec99e</hash>
+      <hash>92317de0fc58deba956a7c4cf23cd87028362826</hash>
+      <hash>0e53cb81bda4774c3c5ce8898e2a85d56704fdb6</hash>
+      <hash>d4b76a8dae36403ffb6c9bb93a93115c4fd74f18</hash>
+      <hash>cfc7ff0056327d1eb3c401885e49ee514acb5ea4</hash>
+      <hash>3e47d6f3d4512b56f8a48e028417da53a8f50163</hash>
+      <hash>fdc51eda2dd4f784e086e05efd50756e0e2e93f1</hash>
+      <hash>62b0642dd3583e46bc4a4a4e946f9f41a85316c5</hash>
+      <hash>1d699a2de913e0512b3069a56c634952e206f725</hash>
+      <hash>faa1518829eb7b7bafef580bdc7a375ab67df6c9</hash>
+      <hash>79a2069008732e01fafdc6bfae5c0d4878191cf8</hash>
+      <hash>cfe4b5a6ac9633bb476be67f9ce7326519498f66</hash>
+      <hash>faaba329d20c0fbd04ced467c68b4b466bdf7674</hash>
+      <hash>22902be8a6d4dcd61f2d5643093c16d91591453b</hash>
+      <hash>2bf01ec12eec43da8c15e0495a27c8e2e7d17975</hash>
+      <hash>d1341a1129f63c66136d661677f34aca91eab3cf</hash>
+      <hash>057e65f31bf494c532d1299368b273abafcbe24f</hash>
+      <hash>1640f880ba910be403d511f39d7d610a2f5aed92</hash>
+      <hash>4bbff42ed3ba0e6fb23ffc3170e884c1752b7bb9</hash>
+      <hash>82ec7855cd32e721445c3ec0f2dfae9814faaaa4</hash>
+      <hash>ee6e7a68f2c5b3cb3ade33066182c3fbc49317e6</hash>
+      <hash>542684dc2cbc3f02ea57a7e21382ecbb0b977c5d</hash>
+      <hash>9ee8f8ba9a51f1a2ffdeaec56b9f790a8246545f</hash>
+      <hash>cd0895b0a6f8c32ddf8e1774be61c2a448e113bc</hash>
+      <hash>4871e467373ed699071396b07774a1591d36a421</hash>
+      <hash>35b5604b108a59988aca58e6f4773b2f4cd5b88a</hash>
+      <hash>9c58cf38b6958c7e7400d19e8c98db2f5a43325d</hash>
+      <hash>00ee7a8491272475e6ece4cca82b7e9c29f740ba</hash>
+      <hash>6f7350658904cc01c8e9a7aab19a44c8c8f1ca4d</hash>
+      <hash>9b640920de57018780e3919b890d17ef09e8ed8c</hash>
+      <hash>17d551e8c57570bff19fd2fb8220bf101f3bc941</hash>
+      <hash>52e1f2ed6f21611b2efe4d7ef325aa24840470ad</hash>
+      <hash>863df77cbc0c93c5fc589c0e968ff7222cf2d42e</hash>
+      <hash>bfc8699fb1f8ee70c74e8914cd76f29ccf5b814f</hash>
+      <hash>a0ab0bccc949c52049273865495dd18b0fd11945</hash>
+      <hash>548a1690e5ffce54983a40f583f968235e480f45</hash>
+      <hash>4b88cc0e5d7bd519b6abe5f3ebc13b9c1b166506</hash>
+      <hash>5106ee73a301a0d38eabf0e71cb0fc9866bb8b69</hash>
+      <hash>032ce62bad50602863ae3f8409ae8b03a44fb017</hash>
+      <hash>c480c521a00374c0691de62fa415e6854ba3400d</hash>
+      <hash>3f63504eb9fc8fe250f7441699508c4c3704153e</hash>
+      <hash>8ea4edef9512aadc4c20dc54488c1569322cba72</hash>
+      <hash>69c3be28a635b360687e79a7fbc945bab87f7e19</hash>
+      <hash>072f4d5484893b8b995e19e31b192f414908ce46</hash>
+      <hash>e938a1c8e19fb02d8a59809005327ce91ea1453a</hash>
+      <hash>d30fc4f7643cc522965ee3c496d3d7601e73fb08</hash>
+      <hash>1ee78d5f582629784ecb286c551fcf3b335e74a8</hash>
+      <hash>54fb2f5699241e407e272d0e79469216d98e6b91</hash>
+      <hash>e7038177e7399d30956ebe5db5f0b5b4946528ff</hash>
+      <hash>68f175645387cf47fc0eed8a973ac4dab2657908</hash>
+      <hash>0a2f4c4a6abf92231d2c7f9e1dd8011c908fa356</hash>
+      <hash>741f332adb152ccb781c732755b551dd7c1481f0</hash>
+      <hash>0cfdbfaa362a5c4d22a1fe7f31375bf57ac7a1ec</hash>
+      <hash>3102a4d3fe18594ffd62696ba07f8ea9321eb222</hash>
+      <hash>49abbc86515bfa01e86f62f32a44f5663b3b6f27</hash>
+      <hash>e27321dd4d10129f095a0ab8e748898885869ac7</hash>
+      <hash>a4de9b37ffd591118d31be1602d2f20ac434b1fa</hash>
+      <hash>1fa1da65287c26594d76b478e3f5ffb5e80d61c9</hash>
+      <hash>6a298213a3bd89bfec478b5f265284cba1f09b3b</hash>
+      <hash>79fbd7d3834ab562828062d21dd3788c04c892a0</hash>
+      <hash>910b55f1f231d98b29ff9c8dbfe51c188f7edcc7</hash>
+      <hash>0a242b36214a6f44d6c15c7021b649793e779313</hash>
+      <hash>d639b6a1243d8b0536bc0f4c29bf1abb74aa541f</hash>
+      <hash>97ea28452ee779b1d146f02756f632dbca917a8a</hash>
+      <hash>58ca63e6293b38ed450683c0e2d0d778f6ea2fec</hash>
+      <hash>35989c2ef34043069106d0790f483c88dbe5563a</hash>
+      <hash>110cd1527cdd1ac7283f8299a3b57a2b6edf48ef</hash>
+      <hash>a2174fa136d4231799369916f9a07d7c8e02ec47</hash>
+      <hash>952eb637dbeb2e1366747cfa94cc70ee1e8ed493</hash>
+      <hash>3646a03568c6b81917fa35fb3733d31f96233f3c</hash>
+      <hash>7ff65f933bb17b5e413a892f81cb3f3c8ef6b9c1</hash>
+      <hash>06a00a18679a4c1524440c022ae4984fef35b937</hash>
+      <hash>f1a6a85df1c1ad73a4d86df3dfe040ad68cdf89a</hash>
+      <hash>b2d0c85ebfac8204670291cfe1267a0bd2053ee1</hash>
+      <hash>bc7fda6813272bf14a66aa38490310ecd33db06c</hash>
+      <hash>d518fe952e380e96676fa9d62d00ac328c332a5c</hash>
+      <hash>fb2c624674936a90916a3d8385325ef92201d317</hash>
+      <hash>c67d2837b586016e4898c830463383860d265adf</hash>
+      <hash>dd6d14ec3bba1d23c7234bb4af3e59462170d675</hash>
+      <hash>a771b7fa4312921ff1c2a19d82a90fa77c05f3e6</hash>
+      <hash>945d62fd50fa90858a0ef8062a45e719dbe6ee60</hash>
+      <hash>78ba1b8d5aa6964cb9ffc3632e41806a14427d58</hash>
+      <hash>e7dce40ce18becf249f9638c2ba294cd1805478a</hash>
+      <hash>2c7cad45471143608401188c8dcf155ba7a790a9</hash>
+      <hash>bdab487c1d4c977f5a6a0d1cac8f2d12679f6d86</hash>
+      <hash>d73e478a5e1b09047d8e3911948976f559c7e915</hash>
+      <hash>90a75eaffe4c4c870d3fa541a554a68d61678f67</hash>
+      <hash>7b040bfb1fea7283b028eb79341fb1f229f6054d</hash>
+      <hash>780282015861b75321929ec63433bad711b66d66</hash>
+      <hash>22085d54006694927e943f37bf856053f5ce58b0</hash>
+      <hash>634f46669410fde9a17d53ac19ee5a7d1d04b1a5</hash>
+      <hash>69dfd663f066f9674a12685116109098354d25f2</hash>
+      <hash>21fa816492c37c4927d5ea2a1cfc61ff5c2f847e</hash>
+      <hash>14cdeb8dd583856c9935be5a58e33187ccacbc66</hash>
+      <hash>71b0c9c229b934f78957235be17e9887405a37d5</hash>
+      <hash>8d744577eaa8d208f4b258197e01c65ed3b7e999</hash>
+      <hash>c4752ddef02d57f8409bb54053259c358f90e327</hash>
+      <hash>9f13ac8ed18542369833d5e4c9bad59be5f23db1</hash>
+      <hash>a869be8eb4e553fac5ac25e058d343dffbf601af</hash>
+      <hash>56f40091232d071f7a342efeb178aff27c70c29a</hash>
+      <hash>1a4f6184f9e69117990f5483bf05045ee7e17834</hash>
+      <hash>e7ad705497f2f07a91974729ed986e906861ff81</hash>
+      <hash>8140bcb9ce68579eb83ac751070d7a08057b3e3c</hash>
+      <hash>c463b5925b9f27d7f077bdff813cde19d3c2bf09</hash>
+      <hash>06e20fe2ee579bdd1e2fdef738485afd6d999418</hash>
+      <hash>91c259e944216ec83ddb541fbf53bbbd518d927f</hash>
+      <hash>8340d1cade0cc8ae45e8136add447649cd470607</hash>
+      <hash>8241dbbf5ff5cc381d7d6448f2c1348abec45203</hash>
+      <hash>594b9ab10319109e782cfc0eaabd038a8e744518</hash>
+      <hash>a7ade46fac338fd08ae322f9575ab95768fd4569</hash>
+      <hash>0146e3deeba63989108d1bcfcf0231c05d8d7a2e</hash>
+      <hash>07d371120735553c7295a9466ee6fe81ff46fe51</hash>
+      <hash>a7c05b98e519af4a86bd20beb6b964c94d48d0af</hash>
+      <hash>9e894b914ab144217181dbca27f955206b3c37b4</hash>
+      <hash>2205dd2085dada143b64785b1fd86b0d3a765bab</hash>
+      <hash>086cadf056dd73a5209df85f0ad2d3ccf7d14f7b</hash>
+      <hash>ae73594385fb7e5195c9c9dffc8b9dd5545a05a0</hash>
+      <hash>277ab18f0ff09f3a1a0a8d68715f7eb22eaba249</hash>
+      <hash>f6e148ba5488138825b8e2a8ba88f4cf7a24deef</hash>
+      <hash>76743b6a629f09b4cc06e7f871561ee87e518ebb</hash>
+      <hash>7367aff187fdf6e6d4a70f492e0cd907d3a4ff33</hash>
+      <hash>19ba943536dcb28b3e815c87f8ad9d910948a975</hash>
+      <hash>1db42e3489d6db0749401769978b04c05cf0f137</hash>
+      <hash>a3c1099e318a4915204318e5de0a4276afe0ef02</hash>
+      <hash>a7a87a47a61b0d93591c94589d39fd079a206ff3</hash>
+      <hash>a4eb1a071e724dca21ad02c912754edfaee1ab00</hash>
+      <hash>611664ce7faa6e14fb6a167758a53c52dac1084b</hash>
+      <hash>344dc7036a6e2d0ec784d3f60132046d021a24a4</hash>
+      <hash>02bf376576b2f7ae650e4d2769cb3caf1764a49f</hash>
+      <hash>35b2549570666e6c1cf7c3dbbf70e482c41d714f</hash>
+      <hash>e5fbe9e59d967812a34621a9b104968b0c353df4</hash>
+      <hash>2b8f1ed1d05393b612a433195f3752f874f8308e</hash>
+      <hash>647719776245276d1948c44b37003d0adc8c53ca</hash>
+      <hash>25c8f2a87b48b1e60700764054ce4aa3e5d14c25</hash>
+      <hash>271744f02f7b00fae63626940cb06e19c75d590e</hash>
+      <hash>cc6ed725952f3d39f570868692e86c0941de1c52</hash>
+      <hash>a081512a675ac3fc30bb21862feb2ab6e8094b55</hash>
+      <hash>d46a5d5044f6025a5188c3e6bdbb9f77ed98b1b1</hash>
+      <hash>891ed91b426b1c415d613128bc3227a350fadfec</hash>
+      <hash>dea127f7c5ac058465f1d0264b27659798780498</hash>
+      <hash>a5a7f6e00c529d1272b071ecbf6bbedbbbebf89a</hash>
+      <hash>ed61db99d5a7862a643f684e67770921c03400e1</hash>
+      <hash>b719a8c73ba5daee78c4b5f7de7d7c3055547f12</hash>
+      <hash>b6f33ac83b8a1db1db0246e15b6c1ac564db5a5b</hash>
+      <hash>c084dc77d693f6d3b3f72f8196cd3583d60545ca</hash>
+      <hash>a09e7af08be0e060d48a863d81eef2fc24456369</hash>
+      <hash>fd15cd7451b4df224b3076462cd3532726ed5700</hash>
+      <hash>36b5a41a18b7cfdcacbcb0def8957dbbbf443df8</hash>
+      <hash>1b810d454152f6e07bd161e02b40473bc0bd2efa</hash>
+      <hash>eb4a3a814b45d248bf3526fa2bf162c72a7d84be</hash>
+      <hash>65c26fe1a5ca80972835cd3546c2e794deae62cd</hash>
+      <hash>ddbbf176a6f2a6b0e856e50cc969bf157f4b8706</hash>
+      <hash>3712dda5fa2838d14b942e2dd43babd8dbd8fb71</hash>
+      <hash>2bb88a6732a8257da53fb795e4d2a1dabd44948c</hash>
+      <hash>322e7ced7dded5a26b775ed4dd425009c9cacf1a</hash>
+      <hash>fe863b60a63f8b844796455dede6206d057b3f7f</hash>
+      <hash>e023d908e76518afe0a73a9284691fc51d362673</hash>
+      <hash>9c4856b53ea1a6137fda6bbe6ba3f29355412c94</hash>
+      <hash>d733e415454ab0737f61ab6545a97aaa34a10981</hash>
+      <hash>b7aa3fe75fba78e3ef7a83657b0deb6cf563751a</hash>
+      <hash>d8226fae4f14c31362aaa403f4d6a8b84b89dfc5</hash>
+      <hash>e8a1cfa1e616e1af2a63ab32845ff719ed98c192</hash>
+      <hash>b28f88b4dbe3017d2194b3614321b0e85b1a817e</hash>
+      <hash>a4e6762a5454c1623ab580b1cea66e98d47a1207</hash>
+      <hash>dea8eb3fca200ec89e5ade0833a5954abdb3a38d</hash>
+      <hash>84f62cdaa7d95f934e355abf48af32f8b5aa6a92</hash>
+      <hash>9763c01fd40bd56ebad6adf07a4e00f670508b3a</hash>
+      <hash>35376a957ac89be433fa6a5af47bbb3a803b8439</hash>
+      <hash>9fbb9ef79f32e2b7e71cab55991ac6838f3cc727</hash>
+      <hash>d7a088da4afedcf3a6b2bf7b6c0405dee40dc0b1</hash>
+      <hash>c2a87610d343c57b3685843b722ea6839c2482c2</hash>
+      <hash>6698ac8c767847f828b3027930b3840c6c298da9</hash>
+      <hash>67f27e3c2aa7dab996b38f0b020e9e3a1ddc1a7a</hash>
+      <hash>5aa3383d759f101865c58ae95a7d28cb4bd90912</hash>
+      <hash>ea7bb4593fa94b3731ffcc0471f23e79487d90be</hash>
+      <hash>587d61a2389f0229988dfa3ba6356d34777f7ad7</hash>
+      <hash>96067e7f71d24311460e6faa73f43af14e1bd09c</hash>
+      <hash>b4ad2b6d784fb0455e0e3ffaf77ec6a5d3835be4</hash>
+      <hash>3c4a0493d869fb12d84fc84efbdf99d721e32795</hash>
+      <hash>7ebfd1a18b889c0ead28671811c3bc912accee7b</hash>
+      <hash>d279daafeba573f7ecff882f696733eeef1081e2</hash>
+      <hash>ca359bb82bf59c190ba4ec30e4f79e61cedf6216</hash>
+      <hash>213d1d15b0fdd65965916bae093710822c2f7434</hash>
+      <hash>20dff502ccf9b1dfae7f8c0b118382fa7c3198ac</hash>
+      <hash>4e0405347af59ede2c53c4c1a527ac46595829c5</hash>
+      <hash>127a4491a7c2310495b68127e9c6570b66600820</hash>
+      <hash>494ad84de129e62b21bba071d8051a6238a51a5a</hash>
+      <hash>f09c50833a58feb41e33cab11e41df1de8eeedcc</hash>
+      <hash>30a03fe42d2f7336b0ed8648a0db70b1462cb76b</hash>
+      <hash>017b17715d7c6a37e4839dbfd9f6498b3b17826c</hash>
+      <hash>a4720889b0785c30cc5b9ae56f404a55170d682a</hash>
+      <hash>5c78a48362ff5a2e28124c9901ac7ef2497fd30a</hash>
+      <hash>39e371cf264039216d190a3b7973f6ea9d745775</hash>
+      <hash>6894cd074f39c624be1f0efc4a4b273c1a579bd6</hash>
+      <hash>50c637cb403c92e3f6784162069d2604f1225725</hash>
+      <hash>128d969bbb6da6a34e838e3d95d68ec5dd294867</hash>
+      <hash>2114cf6fb464efa725f7fa5b8cfc1caa80e28672</hash>
+      <hash>ca7d1683820bae83b8690f38e5aaf9e1d636abe2</hash>
+      <hash>3257813fc3444c2e606b7f9969bbf315dd63fa90</hash>
+      <hash>1597f152441f6b0a3ebdccfe0c8350e2242616d2</hash>
+      <hash>35a4b71263aeab8206ba33e23c77696cd36b4518</hash>
+      <hash>f6334b906ac6f4e068ca73b39fee2e7b25bf7981</hash>
+      <hash>8e91d3f29603675132f56aec4fe7ef6d6a63fca4</hash>
+      <hash>59df53686a09f36836696d627f26f684bc82afd0</hash>
+      <hash>b5fdd020ab89b9eef96fedd11924eab2f33fe1ac</hash>
+      <hash>a10410318a1572e3db0cb85b8535493cfe023016</hash>
+      <hash>51124ed652ab51c6f9562e687b70c89ae504529d</hash>
+      <hash>cf873592a2b70ec6033e1165fd67a321003464b9</hash>
+      <hash>d4315f51b5ddc3ec94a0183674dca9a400be10cc</hash>
+      <hash>dc2f46596ab80be510e874b19702478a04f4e5c3</hash>
+      <hash>5b49e74cf77ce61c433a1144e214c3f88910962d</hash>
+      <hash>3ac5906a9f85304d580b71cd9327fd61edeffa44</hash>
+      <hash>81e74202fd189c83953fab52df68951d8169c459</hash>
+      <hash>bc472cf7d207b3f2c8b5ece2409185091c4fb98a</hash>
+      <hash>278be38283d7cfc425f8d2d3ff878d092d9abc20</hash>
+      <hash>8c59a07a0f16d2c73b18cf9c094a217ac7549b0f</hash>
+      <hash>717c55da1fa0210d630fd39d903cc9e8d1f819dd</hash>
+      <hash>724c8b2141b7be2ad5d8fe35f90db84e634f3caf</hash>
+      <hash>e35c251905928e7cdecab7d5335420866e282099</hash>
+      <hash>e9f39bf25ab70cde0ee6dc97803566fa72ab88f8</hash>
+      <hash>07e733a23c110efd3f61d00d2bf9a932e5b17b3b</hash>
+      <hash>f8c9efd365ea8e16a6bb7960dbf2e8923b6daf38</hash>
+      <hash>0630f187f5d846a3afaf517d2ffeeb90d48c5af4</hash>
+      <hash>46d00fc1685b39e7a46899416ff11f793e49c722</hash>
+      <hash>444b3637266774f24ea0757b29e9f4ba0de3057e</hash>
+      <hash>fc726962d0b4f888edab8d6088529b3ae4784d28</hash>
+      <hash>98a01edc5b8d0f543df51f98b9eb55a755ebe364</hash>
+      <hash>05aa7ad756232555f12ef6bb8fa8356bffbd435f</hash>
+      <hash>d6afccb033d4708a3dccde50bf12183072baf439</hash>
+      <hash>a26c492ac46ccaefe9dc5c7c9400aec8d796e261</hash>
+      <hash>ad068476ecd3b0483b1e15dcbf874286e02707b3</hash>
+      <hash>d3a1206d02257a61dc3afc496d6485c4fec7638a</hash>
+      <hash>fe449f8645437f64f33b1f1ca8f0a099cdef3a0f</hash>
+      <hash>fd07b0f9ca72b8a0b1ee2b6f919a27caf613fbfd</hash>
+      <hash>3a1a5eed724e229a45512ace76fc2c4cdb4aad2d</hash>
+      <hash>1e018eb2b2c3c53c9a51d8ec518d051113ec6529</hash>
+      <hash>564393b0a9363665c7362f0a5ac71316f4a9265e</hash>
+      <hash>17a78772871dc7e7e5c978f91f97a9b0660f40ac</hash>
+      <hash>80b40c9929082efca003180e089bbfe5f8082d2e</hash>
+      <hash>0944557d7186e1316b470f41e268b5c4a30ed206</hash>
+      <hash>944691065bdffa40bf18cfa3da7cbc2f5356ff08</hash>
+      <hash>f85307e1e85f3769ebaf8731b3fbc3400fc08108</hash>
+      <hash>af7354a57bb251dd4bcc0c07cb77993243be5e87</hash>
+      <hash>eff8227e4995441276416cca50fd14fef2ba428c</hash>
+      <hash>ec673322ba6bad3b270b19d3ceb05305a6d6af45</hash>
+      <hash>dd59aec11fc5b728aff4998e12f53a1112cfbc31</hash>
+      <hash>252086811d30ceb5badcaaa69c4cb32e7303b7d2</hash>
+      <hash>0e00907a75b843cd935ef6a451b3c82af49e3008</hash>
+      <hash>2a4a79d2c884543a3ba2a73afa47eb9f0bd29698</hash>
+      <hash>9aa7bafb92bd0e1fa42ca81b34828291174cfa45</hash>
+      <hash>50a700806286465c356dc6a5aa9d77646be2371f</hash>
+      <hash>0d5c84f521e82a262bc5ad047798096be0ad8217</hash>
+      <hash>f9009d3620cb8bf7e644cb35868b26b1f9f72e93</hash>
+      <hash>22174bf7c7f885afaced65e3af0453917aecb803</hash>
+      <hash>b4492302cc57880ca4c32471fb531b1ca41eb2a1</hash>
+      <hash>bc9a39507de82e1190121b248e032384d998cac7</hash>
+      <hash>08aabd9ac83078444ac22dc9dc0936c8455ec6aa</hash>
+      <hash>045c55a19bf74a4c09cc350d912287505f49bcc6</hash>
+      <hash>57cd4adf110b641b0872349a1905853ce7c3be6f</hash>
+      <hash>73fd179fd5237c71f0d69ebcaaeefddcc7372034</hash>
+      <hash>02a1892f86b351e7d25c86de00f1976fe4c36726</hash>
+      <hash>b4520a04516e18d6484cbb97d494dc301ac4150a</hash>
+      <hash>5b1ec54158bc5fd716dee1175a5b7a69c4f67f97</hash>
+      <hash>2508fb878c117c6eaf1adb44f047463d8302bc27</hash>
+      <hash>1de3dbd76c42457a59daea2ef4f9bfee2677cf08</hash>
+      <hash>9fc5feaa0027b690edd68f9e184388d586ae3405</hash>
+      <hash>1fa4c67e95d7a6ac3c626c744e703bcc858968f8</hash>
+      <hash>138631842aeda8e493affbfb915acf75ea0d1b4b</hash>
+      <hash>300ae5f1277a0374ed60996a6cd166f8ce19cc19</hash>
+      <hash>4a9578fb889bd57ac08f383c501887c6f6cdaacb</hash>
+      <hash>ca33f2402ae60e2ca34a082cb2ff57b251fae0b9</hash>
+      <hash>797e7fffb8ba0b568c2b32947bd27459e581b931</hash>
+      <hash>e7dd92f6b9fcb9cf6e8a7aababb4fd8d5286438f</hash>
+      <hash>0d86c365f6789a0ed89663c9b4ce2baafe631198</hash>
+      <hash>d13836d6d14856c6322e7a02688191f8e403a003</hash>
+      <hash>f76040312df1f9bffffee5da1f3760bbb078a9d5</hash>
+      <hash>4c292b1cf4f0d198c7a0c62feaec65a7209af089</hash>
+      <hash>5705d5b44a8475dc9e930e25c9131860282eacce</hash>
+      <hash>7900c7881264321732e18f13318af526f19a8302</hash>
+      <hash>652da028e5240027a861cd0089652c9a9dd47735</hash>
+      <hash>0799bdab13d97e3acac01947e581260d1ff96dcc</hash>
+      <hash>06e826c833a096daa1c4c209f09b8a1bb1bf0e88</hash>
+      <hash>3f87b9557090c2ad7bb155e5ac2a35d242831f9f</hash>
+      <hash>5c2d2fc46f2b4867843780a9925d90231b80ad2e</hash>
+      <hash>57b5897d995a27a65a80e0a63f0625f6a752c081</hash>
+      <hash>6dd50b9ba1a288cf2e7765e5dcc5bfd80ba8c92a</hash>
+      <hash>2a35d531b30f15cbf91bcdbb6eb715f1656e5a0a</hash>
+      <hash>69f3e10e839bbe57a61afabbe32f4c7e125e3874</hash>
+    </pieces>
+
+
+    <!-- Meta URLs -->
+
+
+    <!-- Found 119 mirrors: 0 in the same network prefix, 0 in the same autonomous system,
+         19 handling this country, 44 in the same region, 32 elsewhere -->
+
+    <!-- Mirrors in the same network (195.135.220.0/22): -->
+
+    <!-- Mirrors in the same AS (29298): -->
+
+    <!-- Mirrors which handle this country (DE): -->
+    <url location="de" priority="1">http://ftp4.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="2">http://ftp3.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="3">http://ftp.uni-kassel.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="4">http://ftp.tu-chemnitz.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="5">http://ftp.rz.uni-wuerzburg.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="6">http://vesta.informatik.rwth-aachen.de/ftp/pub/comp/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="7">http://ftp.uni-erlangen.de/pub/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="8">http://ftp.halifax.rwth-aachen.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="9">http://ftp.uni-ulm.de/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="10">http://ftp.uni-kl.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="11">http://widehat.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="12">http://ftp5.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="13">http://opensuse.intergenia.de/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="14">http://download.uni-hd.de/ftp/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="15">http://ftp.uni-bayreuth.de/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="16">http://suse.uni-leipzig.de/pub/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="17">http://ftp.hosteurope.de/mirror/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="18">http://ftp-stud.fht-esslingen.de/pub/Mirrors/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="de" priority="19">http://ftp.uni-siegen.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+
+    <!-- Mirrors in the same continent (EU): -->
+    <url location="nl" priority="20">http://ftp2.nluug.nl/os/Linux/distr/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="se" priority="21">http://ftp.sunet.se/pub/Linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="no" priority="22">http://opensuse.uib.no/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="gr" priority="23">http://ftp.cc.uoc.gr/mirrors/linux/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="es" priority="24">http://ftp.gui.uva.es/sites/opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="fr" priority="25">http://opensuse.mirrors.proxad.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ro" priority="26">http://ftp.gts.lug.ro/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="dk" priority="27">http://ftp.klid.dk/ftp/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="at" priority="28">http://ftp.tugraz.at/mirror/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="cz" priority="29">http://ftp.sh.cvut.cz/MIRRORS/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ch" priority="30">http://mirror.switch.ch/ftp/mirror/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="pl" priority="31">ftp://ftp.pbone.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="pt" priority="32">http://ftp.isr.ist.utl.pt/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="cz" priority="33">http://ftp.linux.cz/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="cz" priority="34">http://mirror.karneval.cz/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="se" priority="35">http://mirrors.se.eu.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="be" priority="36">http://ftp.belnet.be/mirror/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="gb" priority="37">http://anorien.csc.warwick.ac.uk/mirrors/download.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="fr" priority="38">http://mirror.ovh.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="si" priority="39">http://mirror.kreksi.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="nl" priority="40">http://mirror.leaseweb.com/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="at" priority="41">http://suse.inode.at/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="fr" priority="42">http://fr2.rpmfind.net/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="pl" priority="43">http://ftp.icm.edu.pl/pub/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="at" priority="44">http://suse.lagis.at/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="pl" priority="45">ftp://ftp.man.szczecin.pl/pub/Linux/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="at" priority="46">http://gd.tuwien.ac.at/opsys/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="nl" priority="47">http://mirrors.nl.eu.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="sk" priority="48">http://opensuse.ynet.sk/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="hu" priority="49">http://ftp.fsn.hu/pub/linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="fi" priority="50">http://ftp.funet.fi/pub/mirrors/ftp.opensuse.com/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="gb" priority="51">http://www.mirrorservice.org/sites/download.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="it" priority="52">http://opensuse.mirror.garr.it/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ro" priority="53">http://ftp.ines.lug.ro/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ie" priority="54">http://ftp.esat.net/mirrors/ftp.opensuse.org/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="no" priority="55">http://ftp.uninett.no/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="es" priority="56">http://suse.bifi.unizar.es/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ie" priority="57">http://ftp.heanet.ie/mirrors/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="gr" priority="58">http://ftp.ntua.gr/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="nl" priority="59">http://ftp1.nluug.nl/os/Linux/distr/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ro" priority="60">http://ftp.roedu.net/mirrors/opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="hu" priority="61">http://ftp.novell.hu/pub/mirrors/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="nl" priority="62">http://opensuse.hro.nl/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ee" priority="63">http://ftp.estpak.ee/pub/suse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+
+    <!-- Mirrors in the rest of the world: -->
+    <url location="sa" priority="64">http://mirrors.isu.net.sa/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="65">http://mirror.umoss.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="cn" priority="66">http://fundawang.lcuc.org.cn/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="au" priority="67">http://opensuse.mirror.aussiehq.net.au/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="jp" priority="68">http://ftp.riken.jp/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="jp" priority="69">http://ftp.kddilabs.jp/Linux/packages/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="jp" priority="70">http://ftp.novell.co.jp/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ar" priority="71">http://mirror.fcaglp.unlp.edu.ar/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="jp" priority="72">http://ftp.jaist.ac.jp/pub/Linux/openSUSE/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="73">http://opensuse.cs.utah.edu/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="74">http://mirrors2.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="CA" priority="75">http://mirror.its.dal.ca/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="kr" priority="76">http://ftp.kaist.ac.kr/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="77">http://mirrors.xmission.com/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="id" priority="78">http://opensuse.idrepo.or.id/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="79">http://mirror.anl.gov/pub/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="80">http://suse.mirrors.tds.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="81">http://ftp.osuosl.org/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ca" priority="82">http://www.muug.mb.ca/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="83">http://mirror.rackspace.com/openSUSE/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="84">http://mirrors1.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="85">http://www.gtlib.gatech.edu/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="86">http://ftp.utexas.edu/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="87">http://ftp.ussg.iu.edu/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="88">http://mirror.nyi.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="89">http://130.57.19.201/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="90">http://distro.ibiblio.org/pub/linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="us" priority="91">http://mirrors.rit.edu/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="ru" priority="92">http://ftp.chg.ru/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="pt" priority="93">http://ftp.nux.ipb.pt/pub/dists/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url location="au" priority="94">http://mirror.internode.on.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+  </file>
+</metalink>
diff --git a/tests/media/data/openSUSE-11.3-NET-i586.iso.metalink b/tests/media/data/openSUSE-11.3-NET-i586.iso.metalink
new file mode 100644 (file)
index 0000000..a2c1e13
--- /dev/null
@@ -0,0 +1,607 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink version="3.0" xmlns="http://www.metalinker.org/"
+  origin="http://download.opensuse.org/distribution/openSUSE-current/iso/openSUSE-11.3-NET-i586.iso.metalink"
+  generator="MirrorBrain 2.13.2 (see http://mirrorbrain.org/)"
+  type="dynamic"  pubdate="Wed, 13 Oct 2010 09:39:27 GMT"  refreshdate="Wed, 13 Oct 2010 09:39:27 GMT">
+
+  <publisher>
+    <name>openSUSE</name>
+    <url>http://download.opensuse.org</url>
+  </publisher>
+
+  <files>
+  <file name="openSUSE-11.3-NET-i586.iso">
+    <size>120285184</size>
+
+    <!-- <mtime>1278483002</mtime> -->
+
+    <!-- internal id: 9644850 -->
+      <verification>
+    <signature type="pgp" file="openSUSE-11.3-NET-i586.iso.asc">
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBMNZQUqE7a6JyACsoRAlr2AJ9o5fkD0DZ2MpyWC0S9EI6c7BXuPQCd
+ElMAtuZlO5NgYvG2bQ8xys5iWss=
+=WmmH
+-----END PGP SIGNATURE-----
+    </signature>
+        <hash type="md5">a0dc5f5132b0a26218f533d837d4fc1a</hash>
+        <hash type="sha1">c7827b5a8e62d3971524ba0c438e9574e892103a</hash>
+        <hash type="sha256">7db356f5e21547e423da0406b7ea36e3c6e4ee62975015294585593d3a2a88c8</hash>
+        <pieces length="262144" type="sha1">
+          <hash piece="0">c5bc4f75f550e44491273720086bf1692c526cbd</hash>
+          <hash piece="1">bbe1177d325d1310868d34e2350666f9cb119e94</hash>
+          <hash piece="2">2a167f3a34a9a09577365b1015ca23b823f0a80d</hash>
+          <hash piece="3">48d2ba5258254dcd4e49140ac7857c9fdb38ab60</hash>
+          <hash piece="4">bde60d0206ac5a0198e898553b665f9d9c60babb</hash>
+          <hash piece="5">41437180a223068413028fd977ef4ea033bfbfce</hash>
+          <hash piece="6">81b7da73641a5e3391210bc9e3de1568a5208836</hash>
+          <hash piece="7">6f9c2895ec0a887fe2dabd521e40918244da0059</hash>
+          <hash piece="8">8bc4a733263a01658a7c58a53fb777b4c8b4ff28</hash>
+          <hash piece="9">ed21674a98ba5af37df44fe4ad0e140c82ac8d1e</hash>
+          <hash piece="10">3772fed4a8bacd4e20478375c9dc8784f9fef98f</hash>
+          <hash piece="11">62f0024c67ff6830a3355b9145f043c4d60b2f13</hash>
+          <hash piece="12">d9ac81f6753d14912ec643ad8e2982bd1e128c95</hash>
+          <hash piece="13">4f5369273b928b9aaee69736140b814ee9a228d7</hash>
+          <hash piece="14">d6b8bcaf68d07591774302417a3d2b4ee0948e60</hash>
+          <hash piece="15">c2a98d8a80ada0b7ecdf214ab6bd3331793cb120</hash>
+          <hash piece="16">d0725a3e8f0053cb76c1e4424cb2496092063836</hash>
+          <hash piece="17">50d34d802241b3b52937364a14d369f2644ea4f4</hash>
+          <hash piece="18">553ba2491229a1ccfd23d01e00df4d95e612d442</hash>
+          <hash piece="19">2943c0b02f824925e9b589bf3129f950c1e48e5d</hash>
+          <hash piece="20">5d4f21b818057eb23d9aa0e27e8fa5be4c1f3594</hash>
+          <hash piece="21">9630c21e84b85e27f9ee19e59ce1a633621a0b1b</hash>
+          <hash piece="22">ca3c553ec4b1310c1526b684d14c85640329cd1e</hash>
+          <hash piece="23">55bbc2e9d0a627b7481639f4d909118f6b811fc3</hash>
+          <hash piece="24">7843b05f31d95adac6f781f0de8e8af868490a76</hash>
+          <hash piece="25">479d93353bceea8744c7f1b4eac97b277e29ba30</hash>
+          <hash piece="26">28db10a11cf7fc317510230237b76df0dd0dc35c</hash>
+          <hash piece="27">128a62f1599591f9d7db1ce53d551a00555af265</hash>
+          <hash piece="28">582ff34a81fe4b3da54defa2b890946f1fce1646</hash>
+          <hash piece="29">03855419b291564e13510d81083d79816179e4f0</hash>
+          <hash piece="30">0e5a6eb83c9f358f4bb1e092f9a3bcef95dd0a41</hash>
+          <hash piece="31">f3e6e901bd37e3ec8d405a96128cc85bff450a83</hash>
+          <hash piece="32">dee45f794ec0fb5fb5a20826ba2d8d191a567733</hash>
+          <hash piece="33">390c6d1eaf5a28f93d7e8237e3972b9e93fe5cca</hash>
+          <hash piece="34">20336a02716605b12e6fafd18855cbae411a09eb</hash>
+          <hash piece="35">ef22857c452d0043adbc054dd4396c0838426ae2</hash>
+          <hash piece="36">574c3a9f656391757acb83fb5091dabeb78e2418</hash>
+          <hash piece="37">203e818753a68b0bea326ea1428972a92036d9d8</hash>
+          <hash piece="38">5b49222d21afb87a5de922e9c77ca255619d21ad</hash>
+          <hash piece="39">fdd7b232c5ba5fa75c30f0e628e506725869df25</hash>
+          <hash piece="40">360bb3a824926cb73357b7c587390867e944b28c</hash>
+          <hash piece="41">c0f194a68b2c1d8693a83ef162ad85f8c0bf93cd</hash>
+          <hash piece="42">25079830fe5d0c64d5c8f65c6beb61f2832995c8</hash>
+          <hash piece="43">fe5b97a5c807c66833a2f5c45a7c511299c8e7c5</hash>
+          <hash piece="44">64f287d9fc0c64c68c9b9f61711080b4072b11bc</hash>
+          <hash piece="45">fbd01b40db0d0d73c4346bfa6942cd23666d08d8</hash>
+          <hash piece="46">e73a60b7a8efde3d70da2935d85c3ebd6b623651</hash>
+          <hash piece="47">414299b4a2da9b50700d2d7d7721139291acd2b7</hash>
+          <hash piece="48">e2c03eac8cbd4ef1a1fde5b60c32f26c6760a32b</hash>
+          <hash piece="49">a44b4d98688b7dd787ee2c3029229ca3083b2633</hash>
+          <hash piece="50">635ab22c1507403e3ba9481b92c7637ebd58bb2e</hash>
+          <hash piece="51">bddcde253a718b50ab3e2517cc33938ce54baab4</hash>
+          <hash piece="52">bed44cc3cacd658389c4807edead76e71329dea9</hash>
+          <hash piece="53">efe42d31f00ced3aea5a9aef9a3820ec20ba9e73</hash>
+          <hash piece="54">d1b1f83099c80c9219386b38063588a94385ef5a</hash>
+          <hash piece="55">670abebd7cd7a0a62d4aeba3dee78c7512d2ea8c</hash>
+          <hash piece="56">f483d9fbe3d513be20aef6e8736c5755c9aea29e</hash>
+          <hash piece="57">46b4ce6520798c362a16ac3e293cbf94f979603a</hash>
+          <hash piece="58">8b5e93ad34582431b3efb89ec10e05ff4257c166</hash>
+          <hash piece="59">06e09067668ff5b9ebda474a71db44c9b8386eee</hash>
+          <hash piece="60">395843d5243a235c7803bba79eca803bb9c5c5a7</hash>
+          <hash piece="61">0a2cc9fe1a1a4591110f7235b1b9ca9eb4e59ec7</hash>
+          <hash piece="62">fe5ef70a92427bbded4cadcd74f648b7e33807c2</hash>
+          <hash piece="63">c743ddce2e930bcdf76db2f08f5a959f6f3cecd9</hash>
+          <hash piece="64">d5788b2dca4a6cb5d75cce8316db1e761c914da5</hash>
+          <hash piece="65">cf5c83859f55714544e42645db270d237b6d4499</hash>
+          <hash piece="66">497214fef565cfa04c2b8f96704be999b93df93f</hash>
+          <hash piece="67">2f4c7da4c7d25e6d8d63a995f5296c8295bc8d01</hash>
+          <hash piece="68">95550875564701777b6e6e02854865c991a121b6</hash>
+          <hash piece="69">c378b72cdc37c83d4f713e55915863babf949ba3</hash>
+          <hash piece="70">d67e326f31fcb79000e96b3032c1e16183e79d84</hash>
+          <hash piece="71">53b0067cc0e8eb9b856a11af29cf69ededfe4bfd</hash>
+          <hash piece="72">ebf37f7028112f669d244d55ae0290c003cc9cff</hash>
+          <hash piece="73">56e6482f5e0162ee16ee77496e583c9a0d5b0b31</hash>
+          <hash piece="74">772fb29f60ecd14b3c06a317258e86b6802a1310</hash>
+          <hash piece="75">b43a919ce9eae4c3f1b18f5d3f61328a9f67d885</hash>
+          <hash piece="76">cd93972f4cab7128bfefe836ac5601b2cced8a42</hash>
+          <hash piece="77">fe20173622a73d64f586f9594551bd5fe56ef690</hash>
+          <hash piece="78">2f22b34a49fef60f54f8f59789a439bc5232e0ab</hash>
+          <hash piece="79">cc1e04c724746e6058429c0610dafd639798bfab</hash>
+          <hash piece="80">95ba6eba3b22e6b473f7946c8c7aa66da6547ae7</hash>
+          <hash piece="81">2b7e254485c22686e0408ce76fa6b267aadf35ae</hash>
+          <hash piece="82">e35351bcda5aa019839b328106a60e24db7c5de7</hash>
+          <hash piece="83">47864400bc861e6102bdde3900342ee5ac9dfe51</hash>
+          <hash piece="84">f7bfb5331e63b5de7826d0fe5abb0a4e9dffa9b4</hash>
+          <hash piece="85">14cf61a6d461a67f569e0f4b99ebd21ce5925566</hash>
+          <hash piece="86">8a250ac80584f8acaf8af9a4a8d3f9f014999a12</hash>
+          <hash piece="87">a5731b1360d3a387576903da16ed50f390f00864</hash>
+          <hash piece="88">7c9a3b9f00754bc4da997730e75ddedbcdde5117</hash>
+          <hash piece="89">d3879d057ea46050c6286f6653c2bf86fb8938c3</hash>
+          <hash piece="90">a03f375f255e325f6a603934e15769044ecb25db</hash>
+          <hash piece="91">b8ac8d69c3bc16ad657ea2b4a7eace8ea6c60fd8</hash>
+          <hash piece="92">5c02fbca54bd8a035d59145d6d1be0be96f2197c</hash>
+          <hash piece="93">d43671790be7f5c39af346c8fd00003c135491a2</hash>
+          <hash piece="94">2381abd10331dfea9f5817487cf74923b040f307</hash>
+          <hash piece="95">734168c76e9770dd3f86338570d3e27ce7223251</hash>
+          <hash piece="96">859bf6ee62f7fcb94f052c594d470a61be63f084</hash>
+          <hash piece="97">98b4178ddd01b7959b0a117ccff0688489b8841d</hash>
+          <hash piece="98">954de9ea519a33bb4d32ae89f7cae93821e26c02</hash>
+          <hash piece="99">f41f1496594933eb8fb6869bff916d330f1c9152</hash>
+          <hash piece="100">d523b3606c19e8a0b161008846dac2d30fd92e4c</hash>
+          <hash piece="101">3fdf132ad91dcd4f9b466f304d51067baf2ee976</hash>
+          <hash piece="102">3dbde53bdcd43b022ed4575c40fa65ecd67603d0</hash>
+          <hash piece="103">cf977d863575f6f74a228a40e357a70a69700189</hash>
+          <hash piece="104">8c1fe8d6c2bc09f548c2059409d53bdeb5fc7d3f</hash>
+          <hash piece="105">cbfbcedeea1bfc1be031731f0c6de6b0897c84cc</hash>
+          <hash piece="106">0124bed97ba6c2970af5bff8ab82acb8affb4611</hash>
+          <hash piece="107">8cba87ed1e345d2312dd341ce7d1810305ccac41</hash>
+          <hash piece="108">6b6c38964701ba72e7838cea1d91c7ef91ec3f08</hash>
+          <hash piece="109">1f371667f596f11b40a46bcebd0ab6a90be2913d</hash>
+          <hash piece="110">708f8af393a828613c45eb5e408c0190b4c95622</hash>
+          <hash piece="111">d7682ba55ecd3abe1927b9e462fae1b4e9334800</hash>
+          <hash piece="112">459b073f48c2ba332fc39d3f47bd650a04c511ac</hash>
+          <hash piece="113">bb5d3039b65b985b375ff77e048fa690eca45cf4</hash>
+          <hash piece="114">ec3168ae0982393770fee2b55cef963a8c503644</hash>
+          <hash piece="115">0ba5f37dcd6bd6103203fe230d71ffda56502a88</hash>
+          <hash piece="116">80c37d76da053147a5267d03ee94e89d93fcf074</hash>
+          <hash piece="117">db4dccc57e290094edb2c9cc9f134b2e3077d22f</hash>
+          <hash piece="118">ea5981c1f1fb5c214ba0c9e82ec030c2a1bbf0a3</hash>
+          <hash piece="119">f92cf79330387d5b51b8efd6300179e1be15ae01</hash>
+          <hash piece="120">38e0eec3af20d607b15a9a21078373a338c1d4ba</hash>
+          <hash piece="121">9ee0001d0ee983caa4e1c469cdf77ed51a0ef9f2</hash>
+          <hash piece="122">c5a803fc6dd74028f63573e325d87c19001bea8d</hash>
+          <hash piece="123">11992d4f3a304d24962223cb14aeb9c67848e027</hash>
+          <hash piece="124">d7bb416faf5485896af7d349982a92cdf98e847d</hash>
+          <hash piece="125">632add982c1641009bafe85f691f6f21385cd34b</hash>
+          <hash piece="126">04bb6d36962f76334e71f90240d528e8a3ad459b</hash>
+          <hash piece="127">32ebc900994d00f4eda332631d687edf7982df0b</hash>
+          <hash piece="128">a302bf465ac38da7429548b80b288c70fe7f9ed4</hash>
+          <hash piece="129">ed87a36bfe5abd2bb0b0a4efb5932bfa425f80a0</hash>
+          <hash piece="130">f8c448216f59f523bcf33d2d99dc83e8ec7f46e1</hash>
+          <hash piece="131">82f5ffb770451bfd840d816946d9b59884d41b02</hash>
+          <hash piece="132">bd8faa855ca9377f46161b9427027ed8400eff3a</hash>
+          <hash piece="133">f680d78b7854ac0af443b879a880fe3c22cafbd5</hash>
+          <hash piece="134">5d357bb06899857d3e64468d79eef199882cc3d6</hash>
+          <hash piece="135">4be3cae2cd41742f8f33f80c41797a68b1510fb5</hash>
+          <hash piece="136">6d70253e7e22c6a30570d96b6cfb36772b33354f</hash>
+          <hash piece="137">ede70be18a4db080c3d046c5fd9f0bc6b625a772</hash>
+          <hash piece="138">14c8f1675f97244b14f12d6b32419a19cf814dbc</hash>
+          <hash piece="139">0f9943a582d5d514e9deb1253d4806156ad37c17</hash>
+          <hash piece="140">c0086d20b74ae26693716ec7bae9b530237d786b</hash>
+          <hash piece="141">356fe043c133aab0a922153f9cef068d90bf3f89</hash>
+          <hash piece="142">0d2420bcd3eabc17c319e73cb1c043fa47f8ac92</hash>
+          <hash piece="143">7b9496187417464ac70e4fa7a63a710eea14202a</hash>
+          <hash piece="144">fcf3ada8b083d0adffc0c5ecce437aaa483d4109</hash>
+          <hash piece="145">0b14dd454a228bf96d9a177fcc65234db93a740b</hash>
+          <hash piece="146">a7d2ed2809de3e928913a6a3f8efcf1f06f1f3ab</hash>
+          <hash piece="147">9752d14b59c1b60ad957de4982d84e0f3a398bcc</hash>
+          <hash piece="148">bdba58965077db889544e8bb086ddfa8d4394be2</hash>
+          <hash piece="149">93fa8444acd84d0a5580b553b9eec85a354aa4cb</hash>
+          <hash piece="150">dcdb227911b0bcd0ed6074711eb9ad6f686aef46</hash>
+          <hash piece="151">6cd9e2121fee8d0c0982d9976fc8b5eec99bed7c</hash>
+          <hash piece="152">e74ef9311f1a084f8d2625369cbd245872e7c7b5</hash>
+          <hash piece="153">d042115bc6a339f1bcd58d526c3617cc125311f3</hash>
+          <hash piece="154">fb68189e82b480de37bcb09a2e6ac699d7d81533</hash>
+          <hash piece="155">aacecb7a31699743342dce7e031818a809a6ef8a</hash>
+          <hash piece="156">431b9fe43c294a313042714f99e9a9ae59663992</hash>
+          <hash piece="157">fd84701b8a4453d227dffe54761824c4c690f1a4</hash>
+          <hash piece="158">a0d69bdc1b1d99b17e42fe9a0e48918344aa4e4e</hash>
+          <hash piece="159">3962f2a34b48853aa3885a25c5282640451b8935</hash>
+          <hash piece="160">66f1df2058e6b061623971b3c25f49a35a856f2e</hash>
+          <hash piece="161">06531afe357933551835fd816ce915c9737ae8fd</hash>
+          <hash piece="162">3642ad5a2bc42db69d495f0b4f506f5059761b94</hash>
+          <hash piece="163">7756a6cf4264d74e727fc84f377d4a7472f91469</hash>
+          <hash piece="164">a13af573eadb2998648ea050c65bcdc78e262ada</hash>
+          <hash piece="165">9ed650662bf9b8b236934799554d03b0b6ed64d4</hash>
+          <hash piece="166">404d94a3268699510b0dcddbc826bf724dca6658</hash>
+          <hash piece="167">85043d5ae0586b322f97310bc299aa9007101ee8</hash>
+          <hash piece="168">3c39c196c6ba4d90ba316ce3b451bffcc462a9dc</hash>
+          <hash piece="169">dbf5c2fcd32b32023a1431960ca8eac207b3e8c7</hash>
+          <hash piece="170">b2efc9445d6af07aaa64eb736815c1574abfff32</hash>
+          <hash piece="171">9e7443a1d9daa8c5a8e1db98e68fd839b7dec99e</hash>
+          <hash piece="172">92317de0fc58deba956a7c4cf23cd87028362826</hash>
+          <hash piece="173">0e53cb81bda4774c3c5ce8898e2a85d56704fdb6</hash>
+          <hash piece="174">d4b76a8dae36403ffb6c9bb93a93115c4fd74f18</hash>
+          <hash piece="175">cfc7ff0056327d1eb3c401885e49ee514acb5ea4</hash>
+          <hash piece="176">3e47d6f3d4512b56f8a48e028417da53a8f50163</hash>
+          <hash piece="177">fdc51eda2dd4f784e086e05efd50756e0e2e93f1</hash>
+          <hash piece="178">62b0642dd3583e46bc4a4a4e946f9f41a85316c5</hash>
+          <hash piece="179">1d699a2de913e0512b3069a56c634952e206f725</hash>
+          <hash piece="180">faa1518829eb7b7bafef580bdc7a375ab67df6c9</hash>
+          <hash piece="181">79a2069008732e01fafdc6bfae5c0d4878191cf8</hash>
+          <hash piece="182">cfe4b5a6ac9633bb476be67f9ce7326519498f66</hash>
+          <hash piece="183">faaba329d20c0fbd04ced467c68b4b466bdf7674</hash>
+          <hash piece="184">22902be8a6d4dcd61f2d5643093c16d91591453b</hash>
+          <hash piece="185">2bf01ec12eec43da8c15e0495a27c8e2e7d17975</hash>
+          <hash piece="186">d1341a1129f63c66136d661677f34aca91eab3cf</hash>
+          <hash piece="187">057e65f31bf494c532d1299368b273abafcbe24f</hash>
+          <hash piece="188">1640f880ba910be403d511f39d7d610a2f5aed92</hash>
+          <hash piece="189">4bbff42ed3ba0e6fb23ffc3170e884c1752b7bb9</hash>
+          <hash piece="190">82ec7855cd32e721445c3ec0f2dfae9814faaaa4</hash>
+          <hash piece="191">ee6e7a68f2c5b3cb3ade33066182c3fbc49317e6</hash>
+          <hash piece="192">542684dc2cbc3f02ea57a7e21382ecbb0b977c5d</hash>
+          <hash piece="193">9ee8f8ba9a51f1a2ffdeaec56b9f790a8246545f</hash>
+          <hash piece="194">cd0895b0a6f8c32ddf8e1774be61c2a448e113bc</hash>
+          <hash piece="195">4871e467373ed699071396b07774a1591d36a421</hash>
+          <hash piece="196">35b5604b108a59988aca58e6f4773b2f4cd5b88a</hash>
+          <hash piece="197">9c58cf38b6958c7e7400d19e8c98db2f5a43325d</hash>
+          <hash piece="198">00ee7a8491272475e6ece4cca82b7e9c29f740ba</hash>
+          <hash piece="199">6f7350658904cc01c8e9a7aab19a44c8c8f1ca4d</hash>
+          <hash piece="200">9b640920de57018780e3919b890d17ef09e8ed8c</hash>
+          <hash piece="201">17d551e8c57570bff19fd2fb8220bf101f3bc941</hash>
+          <hash piece="202">52e1f2ed6f21611b2efe4d7ef325aa24840470ad</hash>
+          <hash piece="203">863df77cbc0c93c5fc589c0e968ff7222cf2d42e</hash>
+          <hash piece="204">bfc8699fb1f8ee70c74e8914cd76f29ccf5b814f</hash>
+          <hash piece="205">a0ab0bccc949c52049273865495dd18b0fd11945</hash>
+          <hash piece="206">548a1690e5ffce54983a40f583f968235e480f45</hash>
+          <hash piece="207">4b88cc0e5d7bd519b6abe5f3ebc13b9c1b166506</hash>
+          <hash piece="208">5106ee73a301a0d38eabf0e71cb0fc9866bb8b69</hash>
+          <hash piece="209">032ce62bad50602863ae3f8409ae8b03a44fb017</hash>
+          <hash piece="210">c480c521a00374c0691de62fa415e6854ba3400d</hash>
+          <hash piece="211">3f63504eb9fc8fe250f7441699508c4c3704153e</hash>
+          <hash piece="212">8ea4edef9512aadc4c20dc54488c1569322cba72</hash>
+          <hash piece="213">69c3be28a635b360687e79a7fbc945bab87f7e19</hash>
+          <hash piece="214">072f4d5484893b8b995e19e31b192f414908ce46</hash>
+          <hash piece="215">e938a1c8e19fb02d8a59809005327ce91ea1453a</hash>
+          <hash piece="216">d30fc4f7643cc522965ee3c496d3d7601e73fb08</hash>
+          <hash piece="217">1ee78d5f582629784ecb286c551fcf3b335e74a8</hash>
+          <hash piece="218">54fb2f5699241e407e272d0e79469216d98e6b91</hash>
+          <hash piece="219">e7038177e7399d30956ebe5db5f0b5b4946528ff</hash>
+          <hash piece="220">68f175645387cf47fc0eed8a973ac4dab2657908</hash>
+          <hash piece="221">0a2f4c4a6abf92231d2c7f9e1dd8011c908fa356</hash>
+          <hash piece="222">741f332adb152ccb781c732755b551dd7c1481f0</hash>
+          <hash piece="223">0cfdbfaa362a5c4d22a1fe7f31375bf57ac7a1ec</hash>
+          <hash piece="224">3102a4d3fe18594ffd62696ba07f8ea9321eb222</hash>
+          <hash piece="225">49abbc86515bfa01e86f62f32a44f5663b3b6f27</hash>
+          <hash piece="226">e27321dd4d10129f095a0ab8e748898885869ac7</hash>
+          <hash piece="227">a4de9b37ffd591118d31be1602d2f20ac434b1fa</hash>
+          <hash piece="228">1fa1da65287c26594d76b478e3f5ffb5e80d61c9</hash>
+          <hash piece="229">6a298213a3bd89bfec478b5f265284cba1f09b3b</hash>
+          <hash piece="230">79fbd7d3834ab562828062d21dd3788c04c892a0</hash>
+          <hash piece="231">910b55f1f231d98b29ff9c8dbfe51c188f7edcc7</hash>
+          <hash piece="232">0a242b36214a6f44d6c15c7021b649793e779313</hash>
+          <hash piece="233">d639b6a1243d8b0536bc0f4c29bf1abb74aa541f</hash>
+          <hash piece="234">97ea28452ee779b1d146f02756f632dbca917a8a</hash>
+          <hash piece="235">58ca63e6293b38ed450683c0e2d0d778f6ea2fec</hash>
+          <hash piece="236">35989c2ef34043069106d0790f483c88dbe5563a</hash>
+          <hash piece="237">110cd1527cdd1ac7283f8299a3b57a2b6edf48ef</hash>
+          <hash piece="238">a2174fa136d4231799369916f9a07d7c8e02ec47</hash>
+          <hash piece="239">952eb637dbeb2e1366747cfa94cc70ee1e8ed493</hash>
+          <hash piece="240">3646a03568c6b81917fa35fb3733d31f96233f3c</hash>
+          <hash piece="241">7ff65f933bb17b5e413a892f81cb3f3c8ef6b9c1</hash>
+          <hash piece="242">06a00a18679a4c1524440c022ae4984fef35b937</hash>
+          <hash piece="243">f1a6a85df1c1ad73a4d86df3dfe040ad68cdf89a</hash>
+          <hash piece="244">b2d0c85ebfac8204670291cfe1267a0bd2053ee1</hash>
+          <hash piece="245">bc7fda6813272bf14a66aa38490310ecd33db06c</hash>
+          <hash piece="246">d518fe952e380e96676fa9d62d00ac328c332a5c</hash>
+          <hash piece="247">fb2c624674936a90916a3d8385325ef92201d317</hash>
+          <hash piece="248">c67d2837b586016e4898c830463383860d265adf</hash>
+          <hash piece="249">dd6d14ec3bba1d23c7234bb4af3e59462170d675</hash>
+          <hash piece="250">a771b7fa4312921ff1c2a19d82a90fa77c05f3e6</hash>
+          <hash piece="251">945d62fd50fa90858a0ef8062a45e719dbe6ee60</hash>
+          <hash piece="252">78ba1b8d5aa6964cb9ffc3632e41806a14427d58</hash>
+          <hash piece="253">e7dce40ce18becf249f9638c2ba294cd1805478a</hash>
+          <hash piece="254">2c7cad45471143608401188c8dcf155ba7a790a9</hash>
+          <hash piece="255">bdab487c1d4c977f5a6a0d1cac8f2d12679f6d86</hash>
+          <hash piece="256">d73e478a5e1b09047d8e3911948976f559c7e915</hash>
+          <hash piece="257">90a75eaffe4c4c870d3fa541a554a68d61678f67</hash>
+          <hash piece="258">7b040bfb1fea7283b028eb79341fb1f229f6054d</hash>
+          <hash piece="259">780282015861b75321929ec63433bad711b66d66</hash>
+          <hash piece="260">22085d54006694927e943f37bf856053f5ce58b0</hash>
+          <hash piece="261">634f46669410fde9a17d53ac19ee5a7d1d04b1a5</hash>
+          <hash piece="262">69dfd663f066f9674a12685116109098354d25f2</hash>
+          <hash piece="263">21fa816492c37c4927d5ea2a1cfc61ff5c2f847e</hash>
+          <hash piece="264">14cdeb8dd583856c9935be5a58e33187ccacbc66</hash>
+          <hash piece="265">71b0c9c229b934f78957235be17e9887405a37d5</hash>
+          <hash piece="266">8d744577eaa8d208f4b258197e01c65ed3b7e999</hash>
+          <hash piece="267">c4752ddef02d57f8409bb54053259c358f90e327</hash>
+          <hash piece="268">9f13ac8ed18542369833d5e4c9bad59be5f23db1</hash>
+          <hash piece="269">a869be8eb4e553fac5ac25e058d343dffbf601af</hash>
+          <hash piece="270">56f40091232d071f7a342efeb178aff27c70c29a</hash>
+          <hash piece="271">1a4f6184f9e69117990f5483bf05045ee7e17834</hash>
+          <hash piece="272">e7ad705497f2f07a91974729ed986e906861ff81</hash>
+          <hash piece="273">8140bcb9ce68579eb83ac751070d7a08057b3e3c</hash>
+          <hash piece="274">c463b5925b9f27d7f077bdff813cde19d3c2bf09</hash>
+          <hash piece="275">06e20fe2ee579bdd1e2fdef738485afd6d999418</hash>
+          <hash piece="276">91c259e944216ec83ddb541fbf53bbbd518d927f</hash>
+          <hash piece="277">8340d1cade0cc8ae45e8136add447649cd470607</hash>
+          <hash piece="278">8241dbbf5ff5cc381d7d6448f2c1348abec45203</hash>
+          <hash piece="279">594b9ab10319109e782cfc0eaabd038a8e744518</hash>
+          <hash piece="280">a7ade46fac338fd08ae322f9575ab95768fd4569</hash>
+          <hash piece="281">0146e3deeba63989108d1bcfcf0231c05d8d7a2e</hash>
+          <hash piece="282">07d371120735553c7295a9466ee6fe81ff46fe51</hash>
+          <hash piece="283">a7c05b98e519af4a86bd20beb6b964c94d48d0af</hash>
+          <hash piece="284">9e894b914ab144217181dbca27f955206b3c37b4</hash>
+          <hash piece="285">2205dd2085dada143b64785b1fd86b0d3a765bab</hash>
+          <hash piece="286">086cadf056dd73a5209df85f0ad2d3ccf7d14f7b</hash>
+          <hash piece="287">ae73594385fb7e5195c9c9dffc8b9dd5545a05a0</hash>
+          <hash piece="288">277ab18f0ff09f3a1a0a8d68715f7eb22eaba249</hash>
+          <hash piece="289">f6e148ba5488138825b8e2a8ba88f4cf7a24deef</hash>
+          <hash piece="290">76743b6a629f09b4cc06e7f871561ee87e518ebb</hash>
+          <hash piece="291">7367aff187fdf6e6d4a70f492e0cd907d3a4ff33</hash>
+          <hash piece="292">19ba943536dcb28b3e815c87f8ad9d910948a975</hash>
+          <hash piece="293">1db42e3489d6db0749401769978b04c05cf0f137</hash>
+          <hash piece="294">a3c1099e318a4915204318e5de0a4276afe0ef02</hash>
+          <hash piece="295">a7a87a47a61b0d93591c94589d39fd079a206ff3</hash>
+          <hash piece="296">a4eb1a071e724dca21ad02c912754edfaee1ab00</hash>
+          <hash piece="297">611664ce7faa6e14fb6a167758a53c52dac1084b</hash>
+          <hash piece="298">344dc7036a6e2d0ec784d3f60132046d021a24a4</hash>
+          <hash piece="299">02bf376576b2f7ae650e4d2769cb3caf1764a49f</hash>
+          <hash piece="300">35b2549570666e6c1cf7c3dbbf70e482c41d714f</hash>
+          <hash piece="301">e5fbe9e59d967812a34621a9b104968b0c353df4</hash>
+          <hash piece="302">2b8f1ed1d05393b612a433195f3752f874f8308e</hash>
+          <hash piece="303">647719776245276d1948c44b37003d0adc8c53ca</hash>
+          <hash piece="304">25c8f2a87b48b1e60700764054ce4aa3e5d14c25</hash>
+          <hash piece="305">271744f02f7b00fae63626940cb06e19c75d590e</hash>
+          <hash piece="306">cc6ed725952f3d39f570868692e86c0941de1c52</hash>
+          <hash piece="307">a081512a675ac3fc30bb21862feb2ab6e8094b55</hash>
+          <hash piece="308">d46a5d5044f6025a5188c3e6bdbb9f77ed98b1b1</hash>
+          <hash piece="309">891ed91b426b1c415d613128bc3227a350fadfec</hash>
+          <hash piece="310">dea127f7c5ac058465f1d0264b27659798780498</hash>
+          <hash piece="311">a5a7f6e00c529d1272b071ecbf6bbedbbbebf89a</hash>
+          <hash piece="312">ed61db99d5a7862a643f684e67770921c03400e1</hash>
+          <hash piece="313">b719a8c73ba5daee78c4b5f7de7d7c3055547f12</hash>
+          <hash piece="314">b6f33ac83b8a1db1db0246e15b6c1ac564db5a5b</hash>
+          <hash piece="315">c084dc77d693f6d3b3f72f8196cd3583d60545ca</hash>
+          <hash piece="316">a09e7af08be0e060d48a863d81eef2fc24456369</hash>
+          <hash piece="317">fd15cd7451b4df224b3076462cd3532726ed5700</hash>
+          <hash piece="318">36b5a41a18b7cfdcacbcb0def8957dbbbf443df8</hash>
+          <hash piece="319">1b810d454152f6e07bd161e02b40473bc0bd2efa</hash>
+          <hash piece="320">eb4a3a814b45d248bf3526fa2bf162c72a7d84be</hash>
+          <hash piece="321">65c26fe1a5ca80972835cd3546c2e794deae62cd</hash>
+          <hash piece="322">ddbbf176a6f2a6b0e856e50cc969bf157f4b8706</hash>
+          <hash piece="323">3712dda5fa2838d14b942e2dd43babd8dbd8fb71</hash>
+          <hash piece="324">2bb88a6732a8257da53fb795e4d2a1dabd44948c</hash>
+          <hash piece="325">322e7ced7dded5a26b775ed4dd425009c9cacf1a</hash>
+          <hash piece="326">fe863b60a63f8b844796455dede6206d057b3f7f</hash>
+          <hash piece="327">e023d908e76518afe0a73a9284691fc51d362673</hash>
+          <hash piece="328">9c4856b53ea1a6137fda6bbe6ba3f29355412c94</hash>
+          <hash piece="329">d733e415454ab0737f61ab6545a97aaa34a10981</hash>
+          <hash piece="330">b7aa3fe75fba78e3ef7a83657b0deb6cf563751a</hash>
+          <hash piece="331">d8226fae4f14c31362aaa403f4d6a8b84b89dfc5</hash>
+          <hash piece="332">e8a1cfa1e616e1af2a63ab32845ff719ed98c192</hash>
+          <hash piece="333">b28f88b4dbe3017d2194b3614321b0e85b1a817e</hash>
+          <hash piece="334">a4e6762a5454c1623ab580b1cea66e98d47a1207</hash>
+          <hash piece="335">dea8eb3fca200ec89e5ade0833a5954abdb3a38d</hash>
+          <hash piece="336">84f62cdaa7d95f934e355abf48af32f8b5aa6a92</hash>
+          <hash piece="337">9763c01fd40bd56ebad6adf07a4e00f670508b3a</hash>
+          <hash piece="338">35376a957ac89be433fa6a5af47bbb3a803b8439</hash>
+          <hash piece="339">9fbb9ef79f32e2b7e71cab55991ac6838f3cc727</hash>
+          <hash piece="340">d7a088da4afedcf3a6b2bf7b6c0405dee40dc0b1</hash>
+          <hash piece="341">c2a87610d343c57b3685843b722ea6839c2482c2</hash>
+          <hash piece="342">6698ac8c767847f828b3027930b3840c6c298da9</hash>
+          <hash piece="343">67f27e3c2aa7dab996b38f0b020e9e3a1ddc1a7a</hash>
+          <hash piece="344">5aa3383d759f101865c58ae95a7d28cb4bd90912</hash>
+          <hash piece="345">ea7bb4593fa94b3731ffcc0471f23e79487d90be</hash>
+          <hash piece="346">587d61a2389f0229988dfa3ba6356d34777f7ad7</hash>
+          <hash piece="347">96067e7f71d24311460e6faa73f43af14e1bd09c</hash>
+          <hash piece="348">b4ad2b6d784fb0455e0e3ffaf77ec6a5d3835be4</hash>
+          <hash piece="349">3c4a0493d869fb12d84fc84efbdf99d721e32795</hash>
+          <hash piece="350">7ebfd1a18b889c0ead28671811c3bc912accee7b</hash>
+          <hash piece="351">d279daafeba573f7ecff882f696733eeef1081e2</hash>
+          <hash piece="352">ca359bb82bf59c190ba4ec30e4f79e61cedf6216</hash>
+          <hash piece="353">213d1d15b0fdd65965916bae093710822c2f7434</hash>
+          <hash piece="354">20dff502ccf9b1dfae7f8c0b118382fa7c3198ac</hash>
+          <hash piece="355">4e0405347af59ede2c53c4c1a527ac46595829c5</hash>
+          <hash piece="356">127a4491a7c2310495b68127e9c6570b66600820</hash>
+          <hash piece="357">494ad84de129e62b21bba071d8051a6238a51a5a</hash>
+          <hash piece="358">f09c50833a58feb41e33cab11e41df1de8eeedcc</hash>
+          <hash piece="359">30a03fe42d2f7336b0ed8648a0db70b1462cb76b</hash>
+          <hash piece="360">017b17715d7c6a37e4839dbfd9f6498b3b17826c</hash>
+          <hash piece="361">a4720889b0785c30cc5b9ae56f404a55170d682a</hash>
+          <hash piece="362">5c78a48362ff5a2e28124c9901ac7ef2497fd30a</hash>
+          <hash piece="363">39e371cf264039216d190a3b7973f6ea9d745775</hash>
+          <hash piece="364">6894cd074f39c624be1f0efc4a4b273c1a579bd6</hash>
+          <hash piece="365">50c637cb403c92e3f6784162069d2604f1225725</hash>
+          <hash piece="366">128d969bbb6da6a34e838e3d95d68ec5dd294867</hash>
+          <hash piece="367">2114cf6fb464efa725f7fa5b8cfc1caa80e28672</hash>
+          <hash piece="368">ca7d1683820bae83b8690f38e5aaf9e1d636abe2</hash>
+          <hash piece="369">3257813fc3444c2e606b7f9969bbf315dd63fa90</hash>
+          <hash piece="370">1597f152441f6b0a3ebdccfe0c8350e2242616d2</hash>
+          <hash piece="371">35a4b71263aeab8206ba33e23c77696cd36b4518</hash>
+          <hash piece="372">f6334b906ac6f4e068ca73b39fee2e7b25bf7981</hash>
+          <hash piece="373">8e91d3f29603675132f56aec4fe7ef6d6a63fca4</hash>
+          <hash piece="374">59df53686a09f36836696d627f26f684bc82afd0</hash>
+          <hash piece="375">b5fdd020ab89b9eef96fedd11924eab2f33fe1ac</hash>
+          <hash piece="376">a10410318a1572e3db0cb85b8535493cfe023016</hash>
+          <hash piece="377">51124ed652ab51c6f9562e687b70c89ae504529d</hash>
+          <hash piece="378">cf873592a2b70ec6033e1165fd67a321003464b9</hash>
+          <hash piece="379">d4315f51b5ddc3ec94a0183674dca9a400be10cc</hash>
+          <hash piece="380">dc2f46596ab80be510e874b19702478a04f4e5c3</hash>
+          <hash piece="381">5b49e74cf77ce61c433a1144e214c3f88910962d</hash>
+          <hash piece="382">3ac5906a9f85304d580b71cd9327fd61edeffa44</hash>
+          <hash piece="383">81e74202fd189c83953fab52df68951d8169c459</hash>
+          <hash piece="384">bc472cf7d207b3f2c8b5ece2409185091c4fb98a</hash>
+          <hash piece="385">278be38283d7cfc425f8d2d3ff878d092d9abc20</hash>
+          <hash piece="386">8c59a07a0f16d2c73b18cf9c094a217ac7549b0f</hash>
+          <hash piece="387">717c55da1fa0210d630fd39d903cc9e8d1f819dd</hash>
+          <hash piece="388">724c8b2141b7be2ad5d8fe35f90db84e634f3caf</hash>
+          <hash piece="389">e35c251905928e7cdecab7d5335420866e282099</hash>
+          <hash piece="390">e9f39bf25ab70cde0ee6dc97803566fa72ab88f8</hash>
+          <hash piece="391">07e733a23c110efd3f61d00d2bf9a932e5b17b3b</hash>
+          <hash piece="392">f8c9efd365ea8e16a6bb7960dbf2e8923b6daf38</hash>
+          <hash piece="393">0630f187f5d846a3afaf517d2ffeeb90d48c5af4</hash>
+          <hash piece="394">46d00fc1685b39e7a46899416ff11f793e49c722</hash>
+          <hash piece="395">444b3637266774f24ea0757b29e9f4ba0de3057e</hash>
+          <hash piece="396">fc726962d0b4f888edab8d6088529b3ae4784d28</hash>
+          <hash piece="397">98a01edc5b8d0f543df51f98b9eb55a755ebe364</hash>
+          <hash piece="398">05aa7ad756232555f12ef6bb8fa8356bffbd435f</hash>
+          <hash piece="399">d6afccb033d4708a3dccde50bf12183072baf439</hash>
+          <hash piece="400">a26c492ac46ccaefe9dc5c7c9400aec8d796e261</hash>
+          <hash piece="401">ad068476ecd3b0483b1e15dcbf874286e02707b3</hash>
+          <hash piece="402">d3a1206d02257a61dc3afc496d6485c4fec7638a</hash>
+          <hash piece="403">fe449f8645437f64f33b1f1ca8f0a099cdef3a0f</hash>
+          <hash piece="404">fd07b0f9ca72b8a0b1ee2b6f919a27caf613fbfd</hash>
+          <hash piece="405">3a1a5eed724e229a45512ace76fc2c4cdb4aad2d</hash>
+          <hash piece="406">1e018eb2b2c3c53c9a51d8ec518d051113ec6529</hash>
+          <hash piece="407">564393b0a9363665c7362f0a5ac71316f4a9265e</hash>
+          <hash piece="408">17a78772871dc7e7e5c978f91f97a9b0660f40ac</hash>
+          <hash piece="409">80b40c9929082efca003180e089bbfe5f8082d2e</hash>
+          <hash piece="410">0944557d7186e1316b470f41e268b5c4a30ed206</hash>
+          <hash piece="411">944691065bdffa40bf18cfa3da7cbc2f5356ff08</hash>
+          <hash piece="412">f85307e1e85f3769ebaf8731b3fbc3400fc08108</hash>
+          <hash piece="413">af7354a57bb251dd4bcc0c07cb77993243be5e87</hash>
+          <hash piece="414">eff8227e4995441276416cca50fd14fef2ba428c</hash>
+          <hash piece="415">ec673322ba6bad3b270b19d3ceb05305a6d6af45</hash>
+          <hash piece="416">dd59aec11fc5b728aff4998e12f53a1112cfbc31</hash>
+          <hash piece="417">252086811d30ceb5badcaaa69c4cb32e7303b7d2</hash>
+          <hash piece="418">0e00907a75b843cd935ef6a451b3c82af49e3008</hash>
+          <hash piece="419">2a4a79d2c884543a3ba2a73afa47eb9f0bd29698</hash>
+          <hash piece="420">9aa7bafb92bd0e1fa42ca81b34828291174cfa45</hash>
+          <hash piece="421">50a700806286465c356dc6a5aa9d77646be2371f</hash>
+          <hash piece="422">0d5c84f521e82a262bc5ad047798096be0ad8217</hash>
+          <hash piece="423">f9009d3620cb8bf7e644cb35868b26b1f9f72e93</hash>
+          <hash piece="424">22174bf7c7f885afaced65e3af0453917aecb803</hash>
+          <hash piece="425">b4492302cc57880ca4c32471fb531b1ca41eb2a1</hash>
+          <hash piece="426">bc9a39507de82e1190121b248e032384d998cac7</hash>
+          <hash piece="427">08aabd9ac83078444ac22dc9dc0936c8455ec6aa</hash>
+          <hash piece="428">045c55a19bf74a4c09cc350d912287505f49bcc6</hash>
+          <hash piece="429">57cd4adf110b641b0872349a1905853ce7c3be6f</hash>
+          <hash piece="430">73fd179fd5237c71f0d69ebcaaeefddcc7372034</hash>
+          <hash piece="431">02a1892f86b351e7d25c86de00f1976fe4c36726</hash>
+          <hash piece="432">b4520a04516e18d6484cbb97d494dc301ac4150a</hash>
+          <hash piece="433">5b1ec54158bc5fd716dee1175a5b7a69c4f67f97</hash>
+          <hash piece="434">2508fb878c117c6eaf1adb44f047463d8302bc27</hash>
+          <hash piece="435">1de3dbd76c42457a59daea2ef4f9bfee2677cf08</hash>
+          <hash piece="436">9fc5feaa0027b690edd68f9e184388d586ae3405</hash>
+          <hash piece="437">1fa4c67e95d7a6ac3c626c744e703bcc858968f8</hash>
+          <hash piece="438">138631842aeda8e493affbfb915acf75ea0d1b4b</hash>
+          <hash piece="439">300ae5f1277a0374ed60996a6cd166f8ce19cc19</hash>
+          <hash piece="440">4a9578fb889bd57ac08f383c501887c6f6cdaacb</hash>
+          <hash piece="441">ca33f2402ae60e2ca34a082cb2ff57b251fae0b9</hash>
+          <hash piece="442">797e7fffb8ba0b568c2b32947bd27459e581b931</hash>
+          <hash piece="443">e7dd92f6b9fcb9cf6e8a7aababb4fd8d5286438f</hash>
+          <hash piece="444">0d86c365f6789a0ed89663c9b4ce2baafe631198</hash>
+          <hash piece="445">d13836d6d14856c6322e7a02688191f8e403a003</hash>
+          <hash piece="446">f76040312df1f9bffffee5da1f3760bbb078a9d5</hash>
+          <hash piece="447">4c292b1cf4f0d198c7a0c62feaec65a7209af089</hash>
+          <hash piece="448">5705d5b44a8475dc9e930e25c9131860282eacce</hash>
+          <hash piece="449">7900c7881264321732e18f13318af526f19a8302</hash>
+          <hash piece="450">652da028e5240027a861cd0089652c9a9dd47735</hash>
+          <hash piece="451">0799bdab13d97e3acac01947e581260d1ff96dcc</hash>
+          <hash piece="452">06e826c833a096daa1c4c209f09b8a1bb1bf0e88</hash>
+          <hash piece="453">3f87b9557090c2ad7bb155e5ac2a35d242831f9f</hash>
+          <hash piece="454">5c2d2fc46f2b4867843780a9925d90231b80ad2e</hash>
+          <hash piece="455">57b5897d995a27a65a80e0a63f0625f6a752c081</hash>
+          <hash piece="456">6dd50b9ba1a288cf2e7765e5dcc5bfd80ba8c92a</hash>
+          <hash piece="457">2a35d531b30f15cbf91bcdbb6eb715f1656e5a0a</hash>
+          <hash piece="458">69f3e10e839bbe57a61afabbe32f4c7e125e3874</hash>
+        </pieces>
+      </verification>
+    <resources>
+
+
+
+    <!-- Found 119 mirrors: 0 in the same network prefix, 0 in the same autonomous system,
+         19 handling this country, 44 in the same region, 32 elsewhere -->
+
+    <!-- Mirrors in the same network (195.135.220.0/22): -->
+
+    <!-- Mirrors in the same AS (29298): -->
+
+    <!-- Mirrors which handle this country (DE): -->
+    <url type="http" location="de" preference="100">http://ftp.uni-kl.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="99">http://ftp5.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="98">http://ftp.uni-bayreuth.de/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="97">http://download.uni-hd.de/ftp/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="96">http://ftp4.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="95">http://ftp.uni-erlangen.de/pub/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="94">http://ftp.rz.uni-wuerzburg.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="93">http://suse.uni-leipzig.de/pub/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="92">http://ftp.halifax.rwth-aachen.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="91">http://ftp3.gwdg.de/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="90">http://opensuse.intergenia.de/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="89">http://ftp.tu-chemnitz.de/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="88">http://ftp.uni-ulm.de/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="87">http://vesta.informatik.rwth-aachen.de/ftp/pub/comp/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="86">http://ftp.uni-kassel.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="85">http://widehat.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="84">http://ftp.hosteurope.de/mirror/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="83">http://ftp-stud.fht-esslingen.de/pub/Mirrors/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="de" preference="82">http://ftp.uni-siegen.de/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+
+    <!-- Mirrors in the same continent (EU): -->
+    <url type="http" location="gb" preference="81">http://anorien.csc.warwick.ac.uk/mirrors/download.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ie" preference="80">http://ftp.esat.net/mirrors/ftp.opensuse.org/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ch" preference="79">http://mirror.switch.ch/ftp/mirror/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="es" preference="78">http://ftp.gui.uva.es/sites/opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ro" preference="77">http://ftp.ines.lug.ro/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="gb" preference="76">http://www.mirrorservice.org/sites/download.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="pl" preference="75">http://ftp.icm.edu.pl/pub/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ie" preference="74">http://ftp.heanet.ie/mirrors/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="si" preference="73">http://mirror.kreksi.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="be" preference="72">http://ftp.belnet.be/mirror/ftp.opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="hu" preference="71">http://ftp.fsn.hu/pub/linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="sk" preference="70">http://opensuse.ynet.sk/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="fi" preference="69">http://ftp.funet.fi/pub/mirrors/ftp.opensuse.com/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="fr" preference="68">http://mirror.ovh.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="at" preference="67">http://suse.lagis.at/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="dk" preference="66">http://ftp.klid.dk/ftp/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="ftp" location="pl" preference="65">ftp://ftp.pbone.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="se" preference="64">http://mirrors.se.eu.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="cz" preference="63">http://ftp.sh.cvut.cz/MIRRORS/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="at" preference="62">http://suse.inode.at/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="at" preference="61">http://gd.tuwien.ac.at/opsys/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="fr" preference="60">http://opensuse.mirrors.proxad.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="nl" preference="59">http://mirror.leaseweb.com/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="nl" preference="58">http://ftp2.nluug.nl/os/Linux/distr/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="gr" preference="57">http://ftp.cc.uoc.gr/mirrors/linux/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="cz" preference="56">http://mirror.karneval.cz/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="ftp" location="pl" preference="55">ftp://ftp.man.szczecin.pl/pub/Linux/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="at" preference="54">http://ftp.tugraz.at/mirror/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="it" preference="53">http://opensuse.mirror.garr.it/mirrors/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="cz" preference="52">http://ftp.linux.cz/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="no" preference="51">http://opensuse.uib.no/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="se" preference="50">http://ftp.sunet.se/pub/Linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ro" preference="49">http://ftp.gts.lug.ro/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="fr" preference="48">http://fr2.rpmfind.net/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="pt" preference="47">http://ftp.isr.ist.utl.pt/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="es" preference="46">http://suse.bifi.unizar.es/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="no" preference="45">http://ftp.uninett.no/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="nl" preference="44">http://mirrors.nl.eu.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="nl" preference="43">http://ftp1.nluug.nl/os/Linux/distr/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="hu" preference="42">http://ftp.novell.hu/pub/mirrors/ftp.opensuse.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="gr" preference="41">http://ftp.ntua.gr/pub/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="nl" preference="40">http://opensuse.hro.nl/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ro" preference="39">http://ftp.roedu.net/mirrors/opensuse.org/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ee" preference="38">http://ftp.estpak.ee/pub/suse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+
+    <!-- Mirrors in the rest of the world: -->
+    <url type="http" location="us" preference="37">http://opensuse.cs.utah.edu/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="kr" preference="36">http://ftp.kaist.ac.kr/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="CA" preference="35">http://mirror.its.dal.ca/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="jp" preference="34">http://ftp.kddilabs.jp/Linux/packages/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="jp" preference="33">http://ftp.jaist.ac.jp/pub/Linux/openSUSE/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="32">http://www.gtlib.gatech.edu/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="id" preference="31">http://opensuse.idrepo.or.id/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="jp" preference="30">http://ftp.novell.co.jp/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="jp" preference="29">http://ftp.riken.jp/Linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ca" preference="28">http://www.muug.mb.ca/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="27">http://mirror.umoss.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="26">http://ftp.osuosl.org/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="sa" preference="25">http://mirrors.isu.net.sa/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ar" preference="24">http://mirror.fcaglp.unlp.edu.ar/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="au" preference="23">http://opensuse.mirror.aussiehq.net.au/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="22">http://suse.mirrors.tds.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="21">http://mirror.anl.gov/pub/opensuse/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="20">http://mirrors1.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="cn" preference="19">http://fundawang.lcuc.org.cn/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="18">http://mirror.rackspace.com/openSUSE/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="17">http://mirrors2.kernel.org/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="16">http://mirrors.xmission.com/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="15">http://ftp.utexas.edu/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="14">http://130.57.19.201/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="13">http://mirror.nyi.net/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="ru" preference="12">http://ftp.chg.ru/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="11">http://distro.ibiblio.org/pub/linux/distributions/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="10">http://ftp.ussg.iu.edu/linux/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="us" preference="9">http://mirrors.rit.edu/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="pt" preference="8">http://ftp.nux.ipb.pt/pub/dists/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    <url type="http" location="au" preference="7">http://mirror.internode.on.net/pub/opensuse/distribution/11.3/iso/openSUSE-11.3-NET-i586.iso</url>
+    </resources>
+    </file>
+  </files>
+</metalink>
diff --git a/tests/media/file_exists_test.cc b/tests/media/file_exists_test.cc
new file mode 100644 (file)
index 0000000..de159b0
--- /dev/null
@@ -0,0 +1,130 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <unistd.h>
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+#include "mymediaverifier.h"
+
+using namespace zypp;
+using namespace zypp::media;
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+BOOST_AUTO_TEST_CASE(curl_params_reset)
+{
+  MediaManager     mm;
+  media::MediaId   id;
+  
+  Url url("http://ftp.kernel.org/pub/");
+  
+  id = mm.open( url, "");
+  mm.attach(id);
+
+  Pathname dest;
+  Pathname src("/README");
+  mm.doesFileExist(id, src);
+  mm.provideFile(id, src);
+  dest = mm.localPath(id, src);
+  BOOST_REQUIRE( PathInfo(dest).size() != 0 );
+  mm.provideFile(id, src);
+  dest = mm.localPath(id, src);
+  BOOST_REQUIRE( PathInfo(dest).size() != 0 );
+  mm.doesFileExist(id, src);
+  BOOST_REQUIRE( PathInfo(dest).size() != 0 );
+  mm.release(id);   
+}
+
+BOOST_AUTO_TEST_CASE(http_test)
+{
+  //MediaVerifierRef verifier( new MyMediaVerifier() );
+  MediaManager     mm;
+  media::MediaId   id;
+  
+  Url url("http://ftp.kernel.org/pub/");
+  
+//   iso_url = "iso:/";
+//   iso_url.setQueryParam("iso", "SUSE-10.1-Beta5/SUSE-Linux-10.1-beta5-i386-CD1.iso");
+//   iso_url.setQueryParam("url", src_url.asString());
+
+  id = mm.open( url, "");
+  //mm.addVerifier( id, verifier);
+  mm.attach(id);
+  BOOST_REQUIRE( mm.doesFileExist(id, Pathname("/README")) );
+  BOOST_REQUIRE( ! mm.doesFileExist(id, Pathname("/fakefile")) );
+  mm.release(id); 
+}
+
+BOOST_AUTO_TEST_CASE(ftp_test)
+{
+  //MediaVerifierRef verifier( new MyMediaVerifier() );
+  MediaManager     mm;
+  media::MediaId   id;
+  
+  Url url("ftp://ftp.kernel.org/pub/");
+  
+//   iso_url = "iso:/";
+//   iso_url.setQueryParam("iso", "SUSE-10.1-Beta5/SUSE-Linux-10.1-beta5-i386-CD1.iso");
+//   iso_url.setQueryParam("url", src_url.asString());
+
+  id = mm.open( url, "");
+  //mm.addVerifier( id, verifier);
+  mm.attach(id);
+  BOOST_REQUIRE( mm.doesFileExist(id, Pathname("/README")) );
+  BOOST_REQUIRE( ! mm.doesFileExist(id, Pathname("/fakefile")) );
+  mm.release(id); 
+}
+
+BOOST_AUTO_TEST_CASE(isotest)
+{
+   if ( geteuid() != 0 )
+   {
+     BOOST_WARN( "ISO test requires root permissions! (mount)");
+     return;
+   }
+  
+  MediaManager     mm;
+  media::MediaId   id;
+  
+  //Url url("nfs://dist.suse.de/dist/install/openSUSE-10.2-GM/");
+  Url url("dir:/mounts/dist/install/openSUSE-10.2-GM/");
+  
+  Url iso_url("iso:/");
+  iso_url.setQueryParam("iso", "openSUSE-10.2-RC5-PromoDVD-i386.iso");
+  iso_url.setQueryParam("url", url.asString());
+
+  id = mm.open( iso_url, "");
+  mm.attach(id);
+  BOOST_REQUIRE( mm.doesFileExist(id, Pathname("/README")) );
+  BOOST_REQUIRE( ! mm.doesFileExist(id, Pathname("/fakefile")) );
+  mm.release(id); 
+}
+
+BOOST_AUTO_TEST_CASE(nfs_tst)
+{
+   if ( geteuid() != 0 )
+   {
+     BOOST_WARN( "NFS test requires root permissions! (mount)");
+     return;
+   }
+  
+  MediaManager     mm;
+  media::MediaId   id;
+  Url url("nfs://dist.suse.de/dist/install");
+  
+  id = mm.open( url, "");
+  mm.attach(id);
+  BOOST_REQUIRE( mm.doesFileExist(id, Pathname("/SLP/openSUSE-10.2-RM/i386/DVD1/README")) );
+  BOOST_REQUIRE( ! mm.doesFileExist(id, Pathname("/fakefile")) );
+  mm.release(id);
+  
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/media/media1_test.cc b/tests/media/media1_test.cc
new file mode 100644 (file)
index 0000000..fbd7721
--- /dev/null
@@ -0,0 +1,39 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "mymediaverifier.h"
+
+using namespace zypp;
+using namespace zypp::media;
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+BOOST_AUTO_TEST_CASE(verifier_test)
+{
+  MediaVerifierRef verifier(
+      new MyMediaVerifier(/* "SUSE-Linux-CORE-i386 9" */)
+                           );
+  MediaManager     mm;
+  media::MediaId   id;
+
+  //id = mm.open(zypp::Url("cd:/"), "");
+  id = mm.open(zypp::Url("ftp://machcd2/CDs/SLES-10-ISSLE-Beta1a-ppc/CD1"), "");
+  mm.addVerifier( id, verifier);
+  mm.attach(id);
+  mm.provideFile(id, Pathname("/suse/setup/descr/EXTRA_PROV"));
+  mm.release(id);
+  mm.attach(id);
+  mm.provideFile(id, Pathname("/suse/setup/descr/EXTRA_PROV"));  
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/media/media2_test.cc b/tests/media/media2_test.cc
new file mode 100644 (file)
index 0000000..bdf21ef
--- /dev/null
@@ -0,0 +1,153 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <cstdlib>
+
+#include <signal.h>
+
+#include "mymediaverifier.h"
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace zypp;
+using namespace zypp::media;
+
+
+bool       do_step = false;
+int        do_quit = 0;
+
+void quit(int)
+{
+    do_quit = 1;
+}
+
+void goon(int)
+{
+}
+
+#define ONE_STEP(MSG) \
+do { \
+  DBG << "======================================" << std::endl; \
+  DBG << "==>> " << MSG << std::endl; \
+  DBG << "======================================" << std::endl; \
+  if( do_step) { pause(); if( do_quit) exit(0); } \
+} while(0);
+
+BOOST_AUTO_TEST_CASE(strange_test)
+{
+  {
+      struct sigaction sa;
+      sigemptyset(&sa.sa_mask);
+      sa.sa_flags   = 0;
+      sa.sa_handler = goon;
+      sigaction(SIGINT,  &sa, NULL);
+      sa.sa_handler = quit;
+      sigaction(SIGTERM, &sa, NULL);
+
+      //if( argc > 1 && std::string(argv[1]) == "-i")
+      //  do_step = true;
+  }
+
+  MediaVerifierRef verifier(
+    new MyMediaVerifier(/* "SUSE-Linux-CORE-i386 9" */)
+  );
+  MediaManager     mm;
+  media::MediaId   one;
+  media::MediaId   two;
+  zypp::Url        url;
+
+  url = "cd:/";
+
+  try
+  {
+    ONE_STEP("ONE: open " + url.asString());
+    one = mm.open(url);
+
+    ONE_STEP("TWO: open " + url.asString());
+    two = mm.open(url);
+
+
+    ONE_STEP("ONE: add verifier")
+    mm.addVerifier( one, verifier);
+
+    ONE_STEP("TWO: add verifier")
+    mm.addVerifier( two, verifier);
+
+
+    ONE_STEP("ONE: attach")
+    mm.attach(one);
+
+    ONE_STEP("ONE: provideFile(/INDEX.gz)")
+    mm.provideFile(one, Pathname("/INDEX.gz"));
+
+    ONE_STEP("TWO: attach")
+    mm.attach(two);
+
+
+    ONE_STEP("ONE: provideFile(/content)")
+    mm.provideFile(one, Pathname("/content"));
+
+    ONE_STEP("TWO: provideFile(/INDEX.gz)")
+    mm.provideFile(two, Pathname("/INDEX.gz"));
+
+
+    try
+    {
+      ONE_STEP("ONE: release()")
+      mm.release(one); //! \todo add the device argument once mm.getDevices() is ready
+    }
+    catch(const MediaException &e)
+    {
+      ZYPP_CAUGHT(e);
+      ERR << "ONE: HUH? Eject hasn't worked?!" << std::endl;
+    }
+
+    try {
+      ONE_STEP("ONE: provideFile(/content)")
+      mm.provideFile(one, Pathname("/content"));
+    }
+    catch(const MediaException &e)
+    {
+      ZYPP_CAUGHT(e);
+      DBG << "ONE: OK, EXPECTED IT (released)" << std::endl;
+    }
+
+    try {
+      ONE_STEP("TWO: provideFile(/ls-lR.gz)")
+      mm.provideFile(two, Pathname("/ls-lR.gz"));
+    }
+    catch(const MediaException &e)
+    {
+      ZYPP_CAUGHT(e);
+      DBG << "TWO: OK, EXPECTED IT (released)" << std::endl;
+    }
+
+    ONE_STEP("TWO: (RE)ATTACH IT")
+    mm.attach(two);
+
+    ONE_STEP("TWO: provideFile(/INDEX.gz)")
+    mm.provideFile(two, Pathname("/INDEX.gz"));
+
+    ONE_STEP("CLEANUP")
+  }
+  catch(const MediaException &e)
+  {
+    ZYPP_CAUGHT(e);
+  }
+  catch( ... )
+  {
+    // hmm...
+    ERR << "Catched *unknown* exception" << std::endl;
+  }
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/media/media3_test.cc b/tests/media/media3_test.cc
new file mode 100644 (file)
index 0000000..cc11a8b
--- /dev/null
@@ -0,0 +1,159 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <cstdlib>
+
+#include <signal.h>
+
+#include "mymediaverifier.h"
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace zypp;
+using namespace zypp::media;
+
+bool       do_step = false;
+int        do_quit = 0;
+
+void quit(int)
+{
+    do_quit = 1;
+}
+
+void goon(int)
+{
+}
+
+#define ONE_STEP(MSG) \
+do { \
+  DBG << "======================================" << std::endl; \
+  DBG << "==>> " << MSG << std::endl; \
+  DBG << "======================================" << std::endl; \
+  if( do_step) { pause(); if( do_quit) exit(0); } \
+} while(0);
+
+BOOST_AUTO_TEST_CASE(strange_test)
+{
+  bool eject_src = false;
+  bool close_src = false;
+  {
+      struct sigaction sa;
+      sigemptyset(&sa.sa_mask);
+      sa.sa_flags   = 0;
+      sa.sa_handler = goon;
+      sigaction(SIGINT,  &sa, NULL);
+      sa.sa_handler = quit;
+      sigaction(SIGTERM, &sa, NULL);
+
+//       std::cerr << "ARGS=" << argc << std::endl;
+//       for(int i=1; i < argc; i++)
+//       {
+//         if( std::string(argv[i]) == "-i")
+//           do_step = true;
+//         else
+//         if( std::string(argv[i]) == "-e")
+//           eject_src = true;
+//         else
+//         if( std::string(argv[i]) == "-c")
+//           close_src = true;
+//       }
+  }
+
+  MediaVerifierRef verifier(
+    new MyMediaVerifier(/* "SUSE-Linux-CORE-i386 9" */)
+  );
+  MediaManager     mm;
+  media::MediaId   src = 0;
+  media::MediaId   iso;
+  zypp::Url        src_url;
+  zypp::Url        iso_url;
+
+  src_url = "nfs://dist.suse.de/dist/install";
+
+  iso_url = "iso:/";
+  iso_url.setQueryParam("iso", "SUSE-10.1-Beta5/SUSE-Linux-10.1-beta5-i386-CD1.iso");
+  iso_url.setQueryParam("url", src_url.asString());
+
+/*
+  iso_url = "iso:/";
+  iso_url.setQueryParam("iso", "/space/tmp/iso/SUSE-Linux-10.1-beta7-i386-CD1.iso");
+*/
+
+  try
+  {
+    if( eject_src || close_src)
+    {
+      ONE_STEP("SRC: open " + src_url.asString());
+      src = mm.open(src_url);
+
+      ONE_STEP("SRC: attach")
+      mm.attach(src);
+    }
+
+    ONE_STEP("ISO: open " + iso_url.asString());
+    iso = mm.open(iso_url);
+
+    ONE_STEP("ISO: add verifier")
+    mm.addVerifier(iso, verifier);
+
+    ONE_STEP("ISO: attach")
+    mm.attach(iso);
+
+    ONE_STEP("provideFile(/INDEX.gz)")
+    mm.provideFile(iso, Pathname("/INDEX.gz"));
+
+    if( eject_src)
+    {
+      try
+      {
+        ONE_STEP("SRC: release(ejectDev=\"/dev/device\")")
+        mm.release(src);//! \todo add the device argument once mm.getDevices() is ready
+      }
+      catch(const MediaException &e)
+      {
+        ZYPP_CAUGHT(e);
+        ERR << "ONE: HUH? Eject hasn't worked?!" << std::endl;
+      }
+    }
+    else
+    if( close_src)
+    {
+      try
+      {
+        ONE_STEP("SRC: close()")
+        mm.close(src);
+      }
+      catch(const MediaException &e)
+      {
+        ZYPP_CAUGHT(e);
+        ERR << "SRC: HUH? Close hasn't worked?!" << std::endl;
+      }
+    }
+
+    ONE_STEP("ISO: RELEASE")
+    mm.release(iso);
+
+    ONE_STEP("CLEANUP")
+  }
+  catch(const MediaException &e)
+  {
+    ERR << "Catched media exception..." << std::endl;
+    ZYPP_CAUGHT(e);
+  }
+  catch( ... )
+  {
+    // hmm...
+    ERR << "Catched *unknown* exception" << std::endl;
+  }
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/media/media4_test.cc b/tests/media/media4_test.cc
new file mode 100644 (file)
index 0000000..e905c49
--- /dev/null
@@ -0,0 +1,102 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+#include <zypp/PathInfo.h>
+#include <zypp/TmpPath.h>
+#include <zypp/ExternalProgram.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <cstdlib>
+
+#include <signal.h>
+#include <stdlib.h>
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace zypp;
+using namespace zypp::media;
+
+bool       do_step = false;
+int        do_quit = 0;
+
+void quit(int)
+{
+    do_quit = 1;
+}
+
+void goon(int)
+{
+}
+
+#define ONE_STEP(MSG) \
+do { \
+  DBG << "======================================" << std::endl; \
+  DBG << "==>> " << MSG << std::endl; \
+  DBG << "======================================" << std::endl; \
+  if( do_step) { pause(); if( do_quit) exit(0); } \
+} while(0);
+
+BOOST_AUTO_TEST_CASE(strange_test)
+{
+  {
+      struct sigaction sa;
+      sigemptyset(&sa.sa_mask);
+      sa.sa_flags   = 0;
+      sa.sa_handler = goon;
+      sigaction(SIGINT,  &sa, NULL);
+      sa.sa_handler = quit;
+      sigaction(SIGTERM, &sa, NULL);
+
+//       if( argc > 1 && std::string(argv[1]) == "-i")
+//         do_step = true;
+  }
+
+  MediaManager     mm;
+  media::MediaId   id;
+  zypp::Url        url;
+  Pathname         dir("./suse/setup/descr");
+
+  url = "cd:/";
+
+  try
+  {
+    ONE_STEP("open " + url.asString());
+    id = mm.open(url);
+
+    ONE_STEP("attach")
+    mm.attach(id);
+
+    ONE_STEP("provideDirTree(" + dir.asString() + ")");
+    mm.provideDirTree(id, Pathname(dir));
+
+    ONE_STEP("Create a temporary dir");
+    zypp::filesystem::TmpDir temp;
+
+    ONE_STEP("Create a copy of " + dir.asString());
+    zypp::filesystem::copy_dir(mm.localPath(id, dir), temp.path());
+
+    std::string cmd("/bin/ls -lR ");
+                cmd += temp.path().asString();
+
+    ONE_STEP("Check the directory copy")
+    system( cmd.c_str());
+
+    ONE_STEP("CLEANUP")
+  }
+  catch(const MediaException &e)
+  {
+    ZYPP_CAUGHT(e);
+  }
+  catch( ... )
+  {
+    // hmm...
+    ERR << "Catched *unknown* exception" << std::endl;
+  }
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/media/mymediaverifier.h b/tests/media/mymediaverifier.h
new file mode 100644 (file)
index 0000000..5f48136
--- /dev/null
@@ -0,0 +1,65 @@
+
+#ifndef TEST_MYMEDIAVERIFIER
+#define TEST_MYMEDIAVERIFIER
+
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+using namespace zypp;
+using namespace zypp::media;
+
+/*
+** Very basic example verifier.
+**
+** This one does not know anything about the product, it
+** just checks if /media.1 (limited to 1st CD) exists...
+*/
+class MyMediaVerifier: public MediaVerifierBase
+{
+  private:
+  // std::string _productname;
+  public:
+    MyMediaVerifier(/* std::string &productname */)
+  : MediaVerifierBase()
+    //, _productname(productname)
+    {}
+
+    virtual
+        ~MyMediaVerifier()
+    {}
+
+    virtual bool
+        isDesiredMedia(const MediaAccessRef &ref)
+    {
+      DBG << "isDesiredMedia(): for media nr 1 " << std::endl;
+
+      if( !ref)
+        DBG << "isDesiredMedia(): invalid media handle" << std::endl;
+
+      std::list<std::string> lst;
+      Pathname               dir("/media.1");
+
+      DBG << "isDesiredMedia(): checking " << dir.asString() << std::endl;
+
+    // check the product e.g. via /media.1/products as well...
+      try
+      {
+        if( ref)
+          ref->dirInfo(lst, dir, false);
+      }
+      catch(const zypp::Exception &e)
+      {
+        ZYPP_CAUGHT(e);
+      }
+      DBG << "isDesiredMedia(): media "
+          << (lst.empty() ? "does not contain" : "contains")
+          << " the " << dir.asString() << " directory."
+          << std::endl;
+
+      return !lst.empty();
+    }
+};
+
+#endif
diff --git a/tests/media/throw_if_not_exists_test.cc b/tests/media/throw_if_not_exists_test.cc
new file mode 100644 (file)
index 0000000..d712d85
--- /dev/null
@@ -0,0 +1,37 @@
+#include <zypp/media/MediaManager.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Logger.h>
+#include <zypp/Pathname.h>
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <unistd.h>
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "mymediaverifier.h"
+
+using namespace zypp;
+using namespace zypp::media;
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+BOOST_AUTO_TEST_CASE(http_test)
+{
+  //MediaVerifierRef verifier( new MyMediaVerifier() );
+  MediaManager     mm;
+  media::MediaId   id;
+  
+  Url url("http://www.google.com");
+  
+  id = mm.open( url, "");
+  //mm.addVerifier( id, verifier);
+  mm.attach(id);
+  BOOST_CHECK_THROW( mm.provideFile(id, Pathname("/file-not-exists")), Exception );
+  mm.release(id); 
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/parser/CMakeLists.txt b/tests/parser/CMakeLists.txt
new file mode 100644 (file)
index 0000000..92a1a8a
--- /dev/null
@@ -0,0 +1,8 @@
+ADD_SUBDIRECTORY( yum )
+ADD_SUBDIRECTORY( inifile )
+ADD_SUBDIRECTORY(ws)
+
+ADD_TESTS( ProductFileReader )
+ADD_TESTS( RepoFileReader )
+ADD_TESTS( RepoindexFileReader )
+
diff --git a/tests/parser/ProductFileReader_test.cc b/tests/parser/ProductFileReader_test.cc
new file mode 100644 (file)
index 0000000..3904883
--- /dev/null
@@ -0,0 +1,39 @@
+#include "TestSetup.h"
+#include <zypp/parser/ProductFileReader.h>
+
+//static TestSetup test( Arch_x86_64 );
+
+// Must be the first test!
+BOOST_AUTO_TEST_CASE(basic)
+{
+  parser::ProductFileData data;
+  BOOST_CHECK( data.empty() );
+
+  data = parser::ProductFileReader::scanFile( TESTS_SRC_DIR "/parser/ProductFileReader_test.dat" );
+  BOOST_REQUIRE( ! data.empty() );
+
+  BOOST_CHECK_EQUAL( data.vendor(), "Novell" );
+  BOOST_CHECK_EQUAL( data.name(), "SUSE_SLES" );
+  BOOST_CHECK_EQUAL( data.edition(), "11-0" );
+  BOOST_CHECK_EQUAL( data.arch(), Arch_i586 );
+  BOOST_CHECK_EQUAL( data.productline(), "" );
+  BOOST_CHECK_EQUAL( data.registerTarget(), "sle-11-i586" );
+  BOOST_CHECK_EQUAL( data.registerRelease(), "whatever" );
+  BOOST_CHECK_EQUAL( data.updaterepokey(), "A43242DKD" );
+
+  BOOST_REQUIRE_EQUAL( data.upgrades().size(), 2 );
+
+  BOOST_CHECK_EQUAL( data.upgrades()[0].name(), "openSUSE_11.1" );
+  BOOST_CHECK_EQUAL( data.upgrades()[0].summary(), "openSUSE 11.1" );
+  BOOST_CHECK_EQUAL( data.upgrades()[0].repository(), "http://download.opensuse.org/distribution/openSUSE/11.1" );
+  BOOST_CHECK_EQUAL( data.upgrades()[0].product(), "used on entreprise products" );
+  BOOST_CHECK_EQUAL( data.upgrades()[0].notify(), true );
+  BOOST_CHECK_EQUAL( data.upgrades()[0].status(), "stable" );
+
+  BOOST_CHECK_EQUAL( data.upgrades()[1].name(), "openSUSE_Factory" );
+  BOOST_CHECK_EQUAL( data.upgrades()[1].summary(), "openSUSE Factory" );
+  BOOST_CHECK_EQUAL( data.upgrades()[1].repository(), "http://download.opensuse.org/distribution/openSUSE/Factory" );
+  BOOST_CHECK_EQUAL( data.upgrades()[1].product(), "" );
+  BOOST_CHECK_EQUAL( data.upgrades()[1].notify(), false );
+  BOOST_CHECK_EQUAL( data.upgrades()[1].status(), "unstable" );
+}
diff --git a/tests/parser/ProductFileReader_test.dat b/tests/parser/ProductFileReader_test.dat
new file mode 100644 (file)
index 0000000..3fe0033
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<product schemeversion="0">
+    <vendor>Novell</vendor>
+    <name>SUSE_SLES</name>
+    <version>11</version>
+    <release>0</release>
+    <arch>i586</arch>
+    <updaterepokey>A43242DKD</updaterepokey>
+    <summary>SUSE Linux Enterprise Server</summary>
+    <description>
+This is the Server product of the SUSE Linux Enterprise edition. It is an alternative product for the Windows Server and other UNIX Operating Systems, providing Server functionality and interoperability with UNIX servers. This is the replacement product intended to sunset SUSE Linux Enterprise Server10 which was introduced in May of 2006
+       The SUSE Linux Enterprise Server 11 addresses very large enterprises as well as         large   enterprises and in some ways also medium, small, government and educational needs for a         secure, stable, reliable and high-performance operating system platform.
+       It is the core and base offering to provide a general-purpose infrastructure solution.  This    offering is engineered, tested and proven to handle mission-critical workloads in the data      center.
+       Developed and backed by Novell, only SUSE Linux Enterprise Server offers an open, scalable,     high-performance data center solution that comes with application security, virtualization and  Integrated systems management across a range of hardware architectures.
+       SUSE Linux Enterprise Server is deployable as a general-purpose server or can be tailored to    run a variety of specialized workloads, and it offers seamless interoperability with your       existing data center infrastructure.
+    </description>
+    <urls>
+      <url name="releasenotes">http://www.novell.com/linux/releasenotes/i586/SUSE-SLES/11/release-notes-sles.rpm</url>
+    </urls>
+
+    <upgrades>
+       <upgrade>
+         <name>openSUSE_11.1</name>
+         <summary>openSUSE 11.1</summary>
+         <repository>http://download.opensuse.org/distribution/openSUSE/11.1</repository>
+         <product>used on entreprise products</product>
+         <notify>true</notify>
+         <status>stable</status>
+       </upgrade>
+       <upgrade>
+         <name>openSUSE_Factory</name>
+         <summary>openSUSE Factory</summary>
+         <repository>http://download.opensuse.org/distribution/openSUSE/Factory</repository>
+         <notify>no</notify>
+         <status>unstable</status>
+       </upgrade>
+    </upgrades>
+
+    <register>
+        <target>sle-11-i586</target>
+        <release>whatever</release>
+    </register>
+    <buildconfig>
+        <!-- This section is needed to generate the installation media -->
+        <producttheme>SLES</producttheme>
+        <allowresolving>false</allowresolving>
+     </buildconfig>
+     <installconfig>
+        <!-- All flags needed during installation -->
+        <defaultlang>en_US</defaultlang>
+        <!-- needed for current content file -->
+        <datadir>suse</datadir>
+        <descrdir>suse/setup/descr</descrdir>
+        <references name="sles-release" version="11"/>
+        <distribution>SUSE_SLE</distribution>
+     </installconfig>
+     <runtimeconfig>
+        <!-- All Flags needed in the running system -->
+     </runtimeconfig>
+</product>
diff --git a/tests/parser/RepoFileReader_test.cc b/tests/parser/RepoFileReader_test.cc
new file mode 100644 (file)
index 0000000..7f14cc0
--- /dev/null
@@ -0,0 +1,62 @@
+#include <sstream>
+#include <string>
+#include <zypp/parser/RepoFileReader.h>
+#include <zypp/base/NonCopyable.h>
+
+#include "TestSetup.h"
+
+using std::stringstream;
+using std::string;
+using namespace zypp;
+
+static string suse_repo = "[factory-oss]\n"
+"name=factory-oss\n"
+"enabled=1\n"
+"autorefresh=0\n"
+"baseurl=http://download.opensuse.org/factory-tested/repo/oss/\n"
+"type=yast2\n"
+"keeppackages=0\n";
+
+static string fedora_repo = "[fedora]\n"
+"name=Fedora $releasever - $basearch\n"
+"failovermethod=priority\n"
+"#baseurl=http://download.fedora.redhat.com/pub/fedora/linux/releases/$releasever/Everything/$basearch/os/\n"
+"#mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$releasever&arch=$basearch\n"
+"mirrorlist=file:///etc/yum.repos.d/local.mirror\n"
+"enabled=1\n"
+"gpgcheck=1\n"
+"gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora file:///etc/pki/rpm-gpg/RPM-GPG-KEY\n";
+
+struct RepoCollector : private base::NonCopyable
+{
+  bool collect( const RepoInfo &repo )
+  {
+    repos.push_back(repo);
+    return true;
+  }
+
+  RepoInfoList repos;
+};
+
+// Must be the first test!
+BOOST_AUTO_TEST_CASE(read_repo_file)
+{
+  {
+    stringstream input(suse_repo);
+    RepoCollector collector;
+    parser::RepoFileReader parser( input, bind( &RepoCollector::collect, &collector, _1 ) );
+    BOOST_CHECK_EQUAL(1, collector.repos.size());
+  }
+  // fedora
+  {
+    stringstream input(fedora_repo);
+    RepoCollector collector;
+    parser::RepoFileReader parser( input, bind( &RepoCollector::collect, &collector, _1 ) );
+    BOOST_REQUIRE_EQUAL(1, collector.repos.size());
+
+    RepoInfo repo = *collector.repos.begin();
+    // should have taken the first url if more are present
+    BOOST_CHECK_EQUAL(Url("file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora"), repo.gpgKeyUrl());
+  }
+
+}
diff --git a/tests/parser/RepoindexFileReader_test.cc b/tests/parser/RepoindexFileReader_test.cc
new file mode 100644 (file)
index 0000000..dd1f25e
--- /dev/null
@@ -0,0 +1,67 @@
+#include <sstream>
+#include <string>
+#include <zypp/Pathname.h>
+#include <zypp/parser/RepoindexFileReader.h>
+#include <zypp/base/NonCopyable.h>
+
+#include "TestSetup.h"
+
+using std::stringstream;
+using std::string;
+using namespace zypp;
+
+static string service = "<repoindex>"
+  "<repo alias=\"company-foo\" name=\"Company's Foo\""
+  "      path=\"products/foo\" distro_target=\"sle-11-i386\" priority=\"20\"/>"
+  "<repo alias=\"company-bar\" name=\"Company's Bar\""
+  "      path=\"products/bar\" distro_target=\"sle-11-i386\" enabled=\"true\"/>"
+  "<repo alias=\"company-foo-upd\" name=\"Company's Foo Updates\""
+  "      path=\"products/foo/updates\" distro_target=\"sle-11-i386\" priority=\"1\"/>"
+  "</repoindex>";
+
+struct RepoCollector : private base::NonCopyable
+{
+  bool collect( const RepoInfo &repo )
+  {
+    repos.push_back(repo);
+    return true;
+  }
+
+  RepoInfoList repos;
+};
+
+// Must be the first test!
+BOOST_AUTO_TEST_CASE(read_index_file)
+{
+  {
+    stringstream input(service);
+    RepoCollector collector;
+    parser::RepoindexFileReader parser( input, bind( &RepoCollector::collect, &collector, _1 ) );
+    BOOST_REQUIRE_EQUAL(3, collector.repos.size());
+
+    RepoInfo repo;
+    repo = collector.repos.front();
+
+    BOOST_CHECK_EQUAL("Company's Foo", repo.name());
+    BOOST_CHECK_EQUAL("company-foo", repo.alias());
+    BOOST_CHECK_EQUAL("sle-11-i386", repo.targetDistribution());
+    BOOST_CHECK_EQUAL(20, repo.priority());
+    // "Repository is per default disabled"
+    BOOST_CHECK(!repo.enabled());
+    // "Repository autorefresh is per default enabled"
+    BOOST_CHECK(repo.autorefresh());
+    BOOST_CHECK_EQUAL("/repo/products/foo", repo.path());
+
+    collector.repos.pop_front( );
+    repo = collector.repos.front();
+
+    BOOST_CHECK_EQUAL("company-bar", repo.alias());
+    BOOST_CHECK_EQUAL("sle-11-i386", repo.targetDistribution());
+    // "Priority should be 99 when not explictly defined"
+    BOOST_CHECK_EQUAL(99, repo.priority());
+    // "Repository is explicitly enabled"
+    BOOST_CHECK(repo.enabled());
+
+
+  }
+}
diff --git a/tests/parser/inifile/CMakeLists.txt b/tests/parser/inifile/CMakeLists.txt
new file mode 100644 (file)
index 0000000..25e1113
--- /dev/null
@@ -0,0 +1,2 @@
+
+ADD_TESTS(inidict iniparser)
\ No newline at end of file
diff --git a/tests/parser/inifile/data/1.ini b/tests/parser/inifile/data/1.ini
new file mode 100644 (file)
index 0000000..b53fd48
--- /dev/null
@@ -0,0 +1,54 @@
+[base]
+name=CentOS-$releasever - Base
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
+baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
+gpgcheck=1
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=1
+
+#released updates 
+[update]
+name=CentOS-$releasever - Updates
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
+baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
+gpgcheck=1
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=1 
+
+#packages used/produced in the build but not released
+[addons]
+name=CentOS-$releasever - Addons
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=addons
+baseurl=http://mirror.centos.org/centos/$releasever/addons/$basearch/
+gpgcheck=1
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=0 
+
+#additional packages that may be useful
+[extras]
+name=CentOS-$releasever - Extras
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
+baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
+gpgcheck=1
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=0 
+
+#additional packages that extend functionality of existing packages
+[centosplus]
+name=CentOS-$releasever - Plus
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
+baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/
+gpgcheck=1
+enabled=0
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=0 
+
+#contrib - packages by Centos Users
+[contrib]
+name=CentOS-$releasever - Contrib
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib
+baseurl=http://mirror.centos.org/centos/$releasever/contrib/$basearch/
+gpgcheck=1
+enabled=0
+gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
+protect=0
\ No newline at end of file
diff --git a/tests/parser/inifile/data/2.ini b/tests/parser/inifile/data/2.ini
new file mode 100644 (file)
index 0000000..049247a
--- /dev/null
@@ -0,0 +1,10 @@
+[base]
+name=foo
+name= foo
+name =foo
+name = foo
+[equal]
+name1==foo
+name1= =foo
+name2=f=oo
+name3=foo=
diff --git a/tests/parser/inifile/inidict_test.cc b/tests/parser/inifile/inidict_test.cc
new file mode 100644 (file)
index 0000000..8a180f5
--- /dev/null
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/parser/IniDict.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using std::map;
+using namespace zypp;
+using namespace zypp::parser;
+using namespace boost::unit_test;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) +  "/parser/inifile/data")
+
+BOOST_AUTO_TEST_CASE(ini_read)
+{
+  InputStream is((DATADIR+"/1.ini"));
+  IniDict dict(is);
+
+  //MIL << dict["homedmacvicar"]["type"] << endl;
+
+  for ( IniDict::section_const_iterator it = dict.sectionsBegin(); it != dict.sectionsEnd(); ++it )
+  {
+    MIL << (*it) << endl;
+    
+    for ( IniDict::entry_const_iterator it2 = dict.entriesBegin(*it); it2 != dict.entriesEnd(*it); ++it2 )
+    {
+      MIL << "  - " << (*it2).first << " | " << (*it2).second << endl;
+    }
+  }
+
+  BOOST_CHECK( dict.hasSection("addons") );
+  BOOST_CHECK( !dict.hasSection("uhlala") );
+  BOOST_CHECK( dict.hasEntry("contrib", "name") );
+  BOOST_CHECK( !dict.hasEntry("foo", "bar") );
+}
+
+BOOST_AUTO_TEST_CASE(ini_read2)
+{
+  InputStream is((DATADIR+"/2.ini"));
+  IniDict dict(is);
+
+  BOOST_CHECK( find( dict.sectionsBegin(), dict.sectionsEnd(), "base" ) != dict.sectionsEnd() );
+  //IniDict::entry_const_iterator i = find( dict.entriesBegin("base"), dict.entriesEnd("base"), "name");
+  //BOOST_CHECK( i != dict.entriesEnd("base") );
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/parser/inifile/iniparser_test.cc b/tests/parser/inifile/iniparser_test.cc
new file mode 100644 (file)
index 0000000..8857bd3
--- /dev/null
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/parser/IniParser.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace zypp::parser;
+using namespace boost::unit_test;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) +  "/parser/inifile/data")
+
+class IniTest : public IniParser
+{
+  virtual void consume( const std::string &section )
+  {
+    MIL << section << endl;
+  }
+
+  virtual void consume( const std::string &section, const std::string &key, const std::string &value )
+  {
+    MIL << "'" << section << "'" << " | " << "'" << key << "'" << " | " << "'" << value << "'" << endl;
+    if (section == "base" && key == "gpgcheck")
+      BOOST_CHECK_EQUAL(value, "1");
+  }
+};
+
+
+class WithSpacesTest : public IniParser
+{
+  virtual void consume( const std::string &section )
+  {
+    MIL << section << endl;
+    BOOST_CHECK(section == "base" || section == "equal");
+  }
+
+  virtual void consume( const std::string &section, const std::string &key, const std::string &value )
+  {
+    MIL << "'" << section << "'" << " | " << "'" << key << "'" << " | " << "'" << value << "'" << endl;
+    if ( section == "base")
+    {
+      if ( key == "name" )
+        BOOST_CHECK_EQUAL( value, "foo" );
+    }
+    else if ( section == "equal" )
+    {
+      if ( key == "name1" )
+        BOOST_CHECK_EQUAL( value, "=foo" );
+      else if ( key == "name2" )
+        BOOST_CHECK_EQUAL( value, "f=oo" );
+      else if ( key == "name3" )
+        BOOST_CHECK_EQUAL( value, "foo=" );
+      else
+      {
+        cout << "'" << section << "'" << " | " << "'" << key << "'" << " | " << "'" << value << "'" << endl;
+        BOOST_CHECK_MESSAGE( false, "Unhandled key" );
+      }
+    }
+  }
+};
+
+BOOST_AUTO_TEST_CASE(ini_read)
+{
+  InputStream is((DATADIR+"/1.ini"));
+  IniTest parser;
+  parser.parse(is);
+}
+
+BOOST_AUTO_TEST_CASE(ini_spaces_test)
+{
+  InputStream is((DATADIR+"/2.ini"));
+  WithSpacesTest parser;
+  parser.parse(is);
+}
diff --git a/tests/parser/ws/CMakeLists.txt b/tests/parser/ws/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c9a97b8
--- /dev/null
@@ -0,0 +1 @@
+ADD_TESTS(WebpinResultFileReader)
diff --git a/tests/parser/ws/WebpinResultFileReader_test.cc b/tests/parser/ws/WebpinResultFileReader_test.cc
new file mode 100644 (file)
index 0000000..73ffde2
--- /dev/null
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/parser/ws/WebpinResultFileReader.h"
+#include "zypp/ws/WebpinResult.h"
+
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+using namespace zypp;
+using namespace boost::unit_test;
+
+using namespace zypp::ws;
+using namespace zypp::parser::ws;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "parser/ws/data")
+
+class Collector
+{
+public:
+  Collector()
+  {}
+  
+  bool callback( const WebpinResult &result )
+  {
+    items.push_back(result);
+    //items.push_back(loc);
+    //cout << items.size() << endl;
+    return true;
+  }
+  
+  vector<WebpinResult> items;
+};
+
+BOOST_AUTO_TEST_CASE(result_read)
+{  
+    Collector collect;
+    Pathname file;
+
+    // this testcase represents this search:
+    // http://api.opensuse-community.org/searchservice/Search/Simple/openSUSE_103/kopete
+
+    file = DATADIR + "/search-kopete.xml";
+    
+    WebpinResultFileReader reader( file, bind( &Collector::callback, &collect, _1));
+    BOOST_CHECK_EQUAL( collect.items.size(), 17);
+    
+    WebpinResult first = collect.items[0];
+    BOOST_CHECK_EQUAL( first.name(), "kopete-otr");
+    BOOST_CHECK_EQUAL( first.edition(), "0.6");
+    BOOST_CHECK_EQUAL( first.repositoryUrl(), Url("http://download.opensuse.org/repositories/home:/burnickl_andreas/openSUSE_10.3"));
+    BOOST_CHECK_EQUAL( first.distribution(), "openSUSE_103");
+    BOOST_CHECK_EQUAL( first.checksum(), CheckSum::sha1("2a4d9e95f87abe16c28e4aefa0b3a0ae52220429"));
+    BOOST_CHECK_EQUAL( first.priority(), 0);
+    BOOST_CHECK_EQUAL( first.summary(), "OTR Plugin for Kopete");
+}
+
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/parser/ws/data/search-kopete.xml b/tests/parser/ws/data/search-kopete.xml
new file mode 100644 (file)
index 0000000..d814c04
--- /dev/null
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns2:packages xmlns:ns2="http://datastructures.pkgsearch.benjiweber.co.uk">
+  <package>
+    <name>kopete-otr</name>
+    <version>0.6</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/burnickl_andreas/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>2a4d9e95f87abe16c28e4aefa0b3a0ae52220429</checksum>
+    <summary>OTR Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-otr</name>
+    <version>0.7</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/burnickl_andreas/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>b413eb994360c8285f021e69aab2fa006adfcd1e</checksum>
+    <summary>OTR Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-floor</name>
+    <version>0.1.0</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/dmacvicar/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>3c2a1f903106e4f804b811b8f22cb407225a93dd</checksum>
+    <summary>Send your Kopete status to SUSE Floor tool</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/dsbhayangkara/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>f6d0db3322d75ff51628d59897d5a895d00b2e95</checksum>
+    <summary>anyRemote Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote-debuginfo</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/dsbhayangkara/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>6716db855c2e6f13436b1f1bc3ac2d9c807da8f8</checksum>
+    <summary>Debug information for package kopete-anyremote</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/dsbhayangkara/openSUSE_10.3_Update</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>8d5210aa7cd048ff572edf5c68687ddc06e08cda</checksum>
+    <summary>anyRemote Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote-debuginfo</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/home:/dsbhayangkara/openSUSE_10.3_Update</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>87a6b154f660b8eb91539f932c7567fabd2bf914</checksum>
+    <summary>Debug information for package kopete-anyremote</summary>
+  </package>
+  <package>
+    <name>extragear-network-kopete</name>
+    <version>4.0.81.svn816196</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/UNSTABLE:/Extra-Apps/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>7297c1ab95569bc2db635ea046390d3eba43f510</checksum>
+    <summary>Instant Messenger</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/Community/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>edb649f5ff1908cbc5fb33c96c19994ca6375e90</checksum>
+    <summary>anyRemote Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-otr</name>
+    <version>0.6</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/Community/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>src</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>48f011d09d2f5d06d570383106f7ea630a04f704</checksum>
+    <summary>OTR Plugin for Kopete</summary>
+  </package>
+  <package>
+    <name>kopete-anyremote-debuginfo</name>
+    <version>0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/Community/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>ba9a0a3e438b7a032514db2abbe1235e5511fdc9</checksum>
+    <summary>Debug information for package kopete-anyremote</summary>
+  </package>
+  <package>
+    <name>kde4-kopete-devel</name>
+    <version>4.0.83</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/UNSTABLE:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>a42adacc32abbb0af41620f1aed1cb071ca20406</checksum>
+    <summary>Instant Messenger - Development Files</summary>
+  </package>
+  <package>
+    <name>kde4-kopete</name>
+    <version>4.0.83</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/UNSTABLE:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>e6a3481e960ebd4400fc6783774a1553ffc83f13</checksum>
+    <summary>Instant Messenger</summary>
+  </package>
+  <package>
+    <name>kde4-kopete</name>
+    <version>4.0.84</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/Factory:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>11f952a8c8f72fa96507509a9c15e1a322729f7d</checksum>
+    <summary>Instant Messenger</summary>
+  </package>
+  <package>
+    <name>kde4-kopete-devel</name>
+    <version>4.0.84</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/Factory:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>f213abb894d16d76c3598a54bc55ca58e88c1483</checksum>
+    <summary>Instant Messenger - Development Files</summary>
+  </package>
+  <package>
+    <name>kde4-kopete-devel</name>
+    <version>4.0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/STABLE:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>71b6461f35fe255cb70881d415577a5cd573e096</checksum>
+    <summary>Instant Messenger - Development Files</summary>
+  </package>
+  <package>
+    <name>kde4-kopete</name>
+    <version>4.0.4</version>
+    <repoURL>http://download.opensuse.org/repositories/KDE:/KDE4:/STABLE:/Desktop/openSUSE_10.3</repoURL>
+    <archs>
+      <arch>i586</arch>
+    </archs>
+    <distro>openSUSE_103</distro>
+    <priority>10</priority>
+    <checksum>c761db2406163ab025cfb4005fe5b05e83bbb78a</checksum>
+    <summary>Instant Messenger</summary>
+  </package>
+</ns2:packages>
diff --git a/tests/parser/yum/CMakeLists.txt b/tests/parser/yum/CMakeLists.txt
new file mode 100644 (file)
index 0000000..44d1901
--- /dev/null
@@ -0,0 +1 @@
+ADD_TESTS(RepomdFileReader PatchesFileReader)
diff --git a/tests/parser/yum/PatchesFileReader_test.cc b/tests/parser/yum/PatchesFileReader_test.cc
new file mode 100644 (file)
index 0000000..7c57a2e
--- /dev/null
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/OnMediaLocation.h"
+#include "zypp/parser/yum/PatchesFileReader.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+using namespace zypp;
+using namespace boost::unit_test;
+
+using namespace zypp::parser::yum;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/parser/yum/data")
+
+class Collector
+{
+public:
+  Collector()
+  {}
+  
+  bool callback( const OnMediaLocation &loc, const string &id )
+  {
+    items.push_back( make_pair( id, loc ) );
+    //items.push_back(loc);
+    //cout << items.size() << endl;
+    return true;
+  }
+  
+  vector<pair<string, OnMediaLocation> > items;
+  //vector<OnMediaLocation> items;
+};
+
+BOOST_AUTO_TEST_CASE(patches_read_test)
+{
+  list<Pathname> entries;
+  if ( filesystem::readdir( entries, DATADIR, false ) != 0 )
+    ZYPP_THROW(Exception("failed to read directory"));
+  
+  for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+  {
+    Pathname file = *it;
+    //cout << file.basename().substr(0, 7) << " " << file.extension() << endl;
+    if ( ( file.basename().substr(0, 7) == "patches" ) && (file.extension() == ".xml" ) )
+    {
+      //cout << *it << endl;
+      
+      Collector collect;
+      PatchesFileReader( file, bind( &Collector::callback, &collect, _1, _2 ));
+      
+      std::ifstream ifs( file.extend(".solution").asString().c_str() );
+      cout << "Comparing to " << file.extend(".solution") << endl;
+      unsigned int count = 0;
+      while ( ifs && ! ifs.eof() && count < collect.items.size() )
+      {
+        string id;
+        string checksum_type;
+        string checksum;
+        string loc;
+        
+        getline(ifs, id);
+        BOOST_CHECK_EQUAL( collect.items[count].first, id);
+        getline(ifs, checksum_type);
+        getline(ifs, checksum);
+        BOOST_CHECK_EQUAL( collect.items[count].second.checksum(), CheckSum(checksum_type, checksum) );
+        getline(ifs, loc);
+        BOOST_CHECK_EQUAL( collect.items[count].second.filename(), Pathname(loc) );
+        
+        count++;
+      }
+      BOOST_CHECK_EQUAL( collect.items.size(), count );
+    }
+  }
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/parser/yum/RepomdFileReader_test.cc b/tests/parser/yum/RepomdFileReader_test.cc
new file mode 100644 (file)
index 0000000..b5554c4
--- /dev/null
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/parser/yum/RepomdFileReader.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+using namespace zypp;
+using namespace boost::unit_test;
+
+using namespace zypp::parser::yum;
+using repo::yum::ResourceType;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "parser/yum/data")
+
+class Collector
+{
+public:
+  Collector()
+  {}
+  
+  bool callback( const OnMediaLocation &loc, const ResourceType &t )
+  {
+    items.push_back( make_pair( t, loc ) );
+    //items.push_back(loc);
+    //cout << items.size() << endl;
+    return true;
+  }
+  
+  vector<pair<ResourceType, OnMediaLocation> > items;
+  //vector<OnMediaLocation> items;
+};
+
+BOOST_AUTO_TEST_CASE(repomd_read)
+{
+  list<Pathname> entries;
+  if ( filesystem::readdir( entries, DATADIR, false ) != 0 )
+    ZYPP_THROW(Exception("failed to read directory"));
+    
+  for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+  {
+    Pathname file = *it;
+    if ( ( file.basename().substr(0, 6) == "repomd" ) && (file.extension() == ".xml" ) )
+    {
+      cout << *it << endl;
+      
+      Collector collect;
+      RepomdFileReader( file, bind( &Collector::callback, &collect, _1, _2 ));
+      
+      std::ifstream ifs( file.extend(".solution").asString().c_str() );
+      
+      unsigned int count = 0;
+      while ( ifs && ! ifs.eof() && count < collect.items.size() )
+      {
+        string dtype;
+        string checksum_type;
+        string checksum;
+        string loc;
+        
+        getline(ifs, dtype);
+        BOOST_CHECK_EQUAL( collect.items[count].first, ResourceType(dtype));
+        getline(ifs, checksum_type);
+        getline(ifs, checksum);
+        BOOST_CHECK_EQUAL( collect.items[count].second.checksum(), CheckSum(checksum_type, checksum) );
+        getline(ifs, loc);
+        BOOST_CHECK_EQUAL( collect.items[count].second.filename(), Pathname(loc) );
+        
+        count++;
+      }
+      BOOST_CHECK_EQUAL( collect.items.size(), count );
+    }
+  }
+}
+
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/parser/yum/data/README b/tests/parser/yum/data/README
new file mode 100644 (file)
index 0000000..348bf4c
--- /dev/null
@@ -0,0 +1,4 @@
+to generate a solution, take the
+patches.xml, remove the namespace and do:
+
+xsltproc patches.xsl patches2.xml | html2tex
diff --git a/tests/parser/yum/data/patch-fetchmsttfonts.sh-4347.xml b/tests/parser/yum/data/patch-fetchmsttfonts.sh-4347.xml
new file mode 100644 (file)
index 0000000..86ec18c
--- /dev/null
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="287e34f0d7e54e31ac219b16fd6961cb"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="fetchmsttfonts.sh-4347"
+    timestamp="20070919"
+    engine="1.0">
+  <yum:name>fetchmsttfonts.sh</yum:name>
+  <summary lang="en">Download Microsoft(r) TrueType Core Fonts</summary>
+  <summary lang="de">Download Microsoft(r) TrueType Core Fonts</summary>
+  <description lang="en">For legal reasons we can't include the Microsoft(r)
+TrueType Core Fonts in our product. This patch downloads
+these fonts and installs them on your system. Please note
+that about 4 MByte data are downloaded therefore. License
+for the fonts will be installed as
+/usr/share/doc/corefonts/EULA.html.
+</description>
+  <description lang="de">Aus rechtlichen Gründen können wir leider die TrueType Core
+Fonts von Microsoft(r) auf unserem Produkt nicht
+mitliefern. Dieser Patch lädt diese Fonts herunter und
+installiert diese auf Ihrem System. Beachten Sie bitte,
+dass dazu in etwa 4 MByte an Daten heruntergeladen werden.
+Die Lizenz für die Fonts wird unter
+/usr/share/doc/corefonts/EULA.html abgelegt.
+
+</description>
+  <yum:version ver="4347" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="script" name="fetchmsttfonts.sh-4347-patch-fetchmsttfonts.sh-2" epoch="0" ver="4347" rel="1" flags="EQ"/>
+  </rpm:requires>
+  <category>optional</category>
+    <license-to-confirm>
+END-USER LICENSE AGREEMENT FOR
+MICROSOFT SOFTWARE
+
+IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement (&quot;EULA&quot;) is
+a legal agreement between you (either an individual or a single entity) and
+Microsoft Corporation for the Microsoft software accompanying this EULA, which
+includes computer software and may include associated media, printed materials,
+and &quot;on-line&quot; or electronic documentation (&quot;SOFTWARE PRODUCT&quot; or &quot;SOFTWARE&quot;).
+By exercising your rights to make and use copies of the SOFTWARE PRODUCT, you
+agree to be bound by the terms of this EULA. If you do not agree to the terms
+of this EULA, you may not use the SOFTWARE PRODUCT.
+
+
+SOFTWARE PRODUCT LICENSE
+The SOFTWARE PRODUCT is protected by copyright laws and international copyright
+treaties, as well as other intellectual property laws and treaties. The
+SOFTWARE PRODUCT is licensed, not sold.
+
+
+1. GRANT OF LICENSE. This EULA grants you the following rights:
+
+  * Installation and Use. You may install and use an unlimited number of copies
+    of the SOFTWARE PRODUCT.
+  * Reproduction and Distribution. You may reproduce and distribute an
+    unlimited number of copies of the SOFTWARE PRODUCT; provided that each copy
+    shall be a true and complete copy, including all copyright and trademark
+    notices, and shall be accompanied by a copy of this EULA. Copies of the
+    SOFTWARE PRODUCT may not be distributed for profit either on a standalone
+    basis or included as part of your own product.
+
+
+2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS.
+
+  * Limitations on Reverse Engineering, Decompilation, and Disassembly. You may
+    not reverse engineer, decompile, or disassemble the SOFTWARE PRODUCT,
+    except and only to the extent that such activity is expressly permitted by
+    applicable law notwithstanding this limitation.
+  * Restrictions on Alteration. You may not rename, edit or create any
+    derivative works from the SOFTWARE PRODUCT, other than subsetting when
+    embedding them in documents.
+  * Software Transfer. You may permanently transfer all of your rights under
+    this EULA, provided the recipient agrees to the terms of this EULA.
+  * Termination. Without prejudice to any other rights, Microsoft may terminate
+    this EULA if you fail to comply with the terms and conditions of this EULA.
+    In such event, you must destroy all copies of the SOFTWARE PRODUCT and all
+    of its component parts.
+
+
+3. COPYRIGHT. All title and copyrights in and to the SOFTWARE PRODUCT
+(including but not limited to any images, text, and &quot;applets&quot; incorporated into
+the SOFTWARE PRODUCT), the accompanying printed materials, and any copies of
+the SOFTWARE PRODUCT are owned by Microsoft or its suppliers. The SOFTWARE
+PRODUCT is protected by copyright laws and international treaty provisions.
+Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted
+material.
+
+
+4. U.S. GOVERNMENT RESTRICTED RIGHTS. The SOFTWARE PRODUCT and documentation
+are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the
+Government is subject to restrictions as set forth in subparagraph (c)(1)(ii)
+of the Rights in Technical Data and Computer Software clause at DFARS
+252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer
+Software - Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer
+is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399.
+
+
+LIMITED WARRANTY
+
+NO WARRANTIES. Microsoft expressly disclaims any warranty for the SOFTWARE
+PRODUCT. The SOFTWARE PRODUCT and any related documentation is provided &quot;as is&quot;
+without warranty of any kind, either express or implied, including, without
+limitation, the implied warranties or merchantability, fitness for a particular
+purpose, or noninfringement. The entire risk arising out of use or performance
+of the SOFTWARE PRODUCT remains with you.
+
+NO LIABILITY FOR CONSEQUENTIAL DAMAGES. In no event shall Microsoft or its
+suppliers be liable for any damages whatsoever (including, without limitation,
+damages for loss of business profits, business interruption, loss of business
+information, or any other pecuniary loss) arising out of the use of or
+inability to use this Microsoft product, even if Microsoft has been advised of
+the possibility of such damages. Because some states/jurisdictions do not allow
+the exclusion or limitation of liability for consequential or incidental
+damages, the above limitation may not apply to you.
+
+
+MISCELLANEOUS
+
+If you acquired this product in the United States, this EULA is governed by the
+laws of the State of Washington.
+
+If this product was acquired outside the United States, then local laws may
+apply.
+
+Should you have any questions concerning this EULA, or if you desire to contact
+Microsoft for any reason, please contact the Microsoft subsidiary serving your
+country, or write: Microsoft Sales Information Center/One Microsoft Way/
+Redmond, WA 98052-6399.
+    </license-to-confirm>
+  <atoms>
+    <script>
+      <yum:name>fetchmsttfonts.sh-4347-patch-fetchmsttfonts.sh-2</yum:name>
+      <yum:version ver="4347" rel="1"/>
+      <do>
+#!/bin/sh
+
+EULA=&quot;http://corefonts.sourceforge.net/eula.htm&quot;
+
+FONTS=&quot; \
+dl.sourceforge.net/sourceforge/corefonts/andale32.exe \
+dl.sourceforge.net/sourceforge/corefonts/arial32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/arialb32.exe \
+dl.sourceforge.net/sourceforge/corefonts/comic32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/courie32.exe \
+dl.sourceforge.net/sourceforge/corefonts/georgi32.exe \
+dl.sourceforge.net/sourceforge/corefonts/impact32.exe \
+dl.sourceforge.net/sourceforge/corefonts/times32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/trebuc32.exe \
+dl.sourceforge.net/sourceforge/corefonts/verdan32.exe \
+dl.sourceforge.net/sourceforge/corefonts/webdin32.exe \
+&quot;
+
+SERVER=&quot; \
+switch   \
+mesh     \
+jaist    \
+kent     \
+nchc     \
+heanet   \
+easynews \
+optusnet \
+&quot;
+
+CURL_OPTIONS=&quot;-s --speed-limit 3500 --speed-time 15&quot;
+
+if [ &quot;`id -u`&quot; != &quot;0&quot; ]; then
+ echo &quot;error: You must be root to use this program!&quot;
+ exit 1
+fi
+
+if [ ! -x /usr/bin/cabextract ]; then
+  echo &quot;error: cabextract missing! Please install package cabextract first.&quot;
+  exit 2
+fi
+
+. /etc/sysconfig/proxy
+
+if test &quot;$PROXY_ENABLED&quot; != &quot;no&quot;; then
+  if test -n &quot;$HTTP_PROXY&quot; ; then
+    export http_proxy=&quot;$HTTP_PROXY&quot;
+  fi
+fi
+
+if [ -z $http_proxy ]; then
+  echo 
+  echo &quot;note: No proxy is used. Please set the environment variable \&quot;http_proxy\&quot;&quot;
+  echo &quot;note: to your favorite proxy, if you want to use a proxy for the download.&quot;
+  echo &quot;note:&quot;
+  echo &quot;note:   bash: export http_proxy=\&quot;http://proxy.example.com:3128/\&quot;&quot;
+  echo &quot;note:   tcsh: setenv http_proxy \&quot;http://proxy.example.com:3128/\&quot;&quot;
+fi
+
+echo &quot;EULA:&quot;
+mkdir -p /usr/share/doc/corefonts
+echo -n &quot;  Fetching   ... &quot;
+curl $CURL_OPTIONS -o /usr/share/doc/corefonts/EULA.html $EULA || \
+  rm -f /usr/share/doc/corefonts/EULA.html
+echo &quot;done&quot;
+
+tmpname=`basename $0`
+tmpdir=`mktemp -d /tmp/$tmpname.XXXXXX`
+trap &quot;rm -rf $tmpdir&quot; EXIT
+if [ $? -ne 0 ]; then
+  echo &quot;$0: Can't create temp dir, exiting...&quot;
+  exit 4
+fi
+
+pushd $tmpdir &amp;&gt; /dev/null
+
+echo
+echo &quot;Trying to find the fastest server:&quot;
+besttime=1000
+
+for server in $SERVER; do
+  echo -n &quot; $server ... &quot;
+  start=$SECONDS
+  curl $CURL_OPTIONS --connect-timeout 10 -o cabextract.rpm \
+    http://$server.dl.sourceforge.net/sourceforge/corefonts/cabextract-0.5-1.i386.rpm
+  if [ $? -ne 0 ]; then
+    echo &quot;too slow (aborted)&quot;
+    continue
+  fi
+  stop=$SECONDS
+  time=$((stop - start))
+  echo &quot;$time sec&quot;
+  if [ $time -lt $besttime ]; then 
+    besttime=$time
+    useserver=$server
+  fi
+done
+
+rm -f cabextract.rpm
+if [ -n &quot;$useserver&quot; ]; then
+  echo &quot;The winner is: &gt;&gt; $useserver &lt;&lt;&quot;
+  echo
+else
+  echo &quot;Connection too slow or no server available. Aborting ... &quot;
+  exit 5
+fi
+
+for font in $FONTS; do
+ for i in $useserver $SERVER; do
+  archive=http://$i.$font
+  file=`echo $archive|awk -F &quot;/&quot; '{print $NF}'`
+  rm -f $file
+  echo &quot;$file ($archive):&quot;
+  echo -n &quot;  Fetching   ... &quot;
+  curl $CURL_OPTIONS -o $file $archive
+  if [ $? -ne 0 ]; then
+    rm -f $file
+    echo &quot;failed ... deleted!&quot;
+    continue
+  fi
+  echo done
+  echo -n &quot;  Extracting ... &quot;
+  cabextract -l $file &amp;&gt; /dev/null
+  if [ $? -ne 0 ]; then
+    rm -f $file
+    echo &quot;failed ... deleted!&quot;
+  else
+    cabextract $file &amp;&gt; /dev/null
+    echo &quot;done&quot;
+    success=true
+    break
+  fi
+  rm -f $file
+ done
+done
+
+if [ &quot;x$success&quot; != &quot;x&quot; ]; then 
+  for i in *.[Tt][Tt][CFcf]; do
+    lower=`echo $i|tr [:upper:] [:lower:]`
+    test &quot;$i&quot; != &quot;$lower&quot; &amp;&amp; mv $i $lower
+  done
+  chmod 644 *.tt[cf]
+  # impact.ttf already in agfa-fonts package
+  test -s /usr/share/fonts/truetype/impact.ttf &amp;&amp; rm impact.ttf
+  mv -f *.tt[cf] /usr/share/fonts/truetype
+  /usr/sbin/fonts-config
+  echo &quot;*** Fonts installed. ***&quot;
+else
+  echo &quot;*** No Fonts installed. ***&quot;
+fi
+
+popd &amp;&gt; /dev/null
+      </do>
+      <suse:freshens>
+        <suse:entry kind="package" name="glibc"/>
+      </suse:freshens>
+    </script>
+  </atoms>
+</patch>
diff --git a/tests/parser/yum/data/patches-1.xml b/tests/parser/yum/data/patches-1.xml
new file mode 100644 (file)
index 0000000..42de48f
--- /dev/null
@@ -0,0 +1,763 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<patches xmlns="http://novell.com/package/metadata/suse/patches">
+  <patch id="fetchmsttfonts.sh-2333">
+    <checksum type="sha">6b72b4f3617d0d51af28399c0f5e0af401440245</checksum>
+    <location href="repodata/patch-fetchmsttfonts.sh-2333.xml"/>
+  </patch>
+  <patch id="glabels-2348">
+    <checksum type="sha">b02ba598d8ed5f8a31859d3b34e72e1ddecbe894</checksum>
+    <location href="repodata/patch-glabels-2348.xml"/>
+  </patch>
+  <patch id="openssl-2349">
+    <checksum type="sha">321ee41de68be4e83dfb74559c14300a59b85ccf</checksum>
+    <location href="repodata/patch-openssl-2349.xml"/>
+  </patch>
+  <patch id="gv-2350">
+    <checksum type="sha">ec9e8a3f3ce2588cecd84ab95ec910d41db5d74b</checksum>
+    <location href="repodata/patch-gv-2350.xml"/>
+  </patch>
+  <patch id="tar-2351">
+    <checksum type="sha">d803372cd5d844ee01ab6fb3d1b4332391fa1206</checksum>
+    <location href="repodata/patch-tar-2351.xml"/>
+  </patch>
+  <patch id="flash-player-2359">
+    <checksum type="sha">c2de5dd35ec2dcccc118d9d7f539e768dfd5ec50</checksum>
+    <location href="repodata/patch-flash-player-2359.xml"/>
+  </patch>
+  <patch id="lineak_kde-2361">
+    <checksum type="sha">7fb791963114836621873280c7edd58887b91784</checksum>
+    <location href="repodata/patch-lineak_kde-2361.xml"/>
+  </patch>
+  <patch id="evince-2362">
+    <checksum type="sha">28f0c33aa4a34830a4b7b8a795db609bfd3d7531</checksum>
+    <location href="repodata/patch-evince-2362.xml"/>
+  </patch>
+  <patch id="capi4hylafax-2366">
+    <checksum type="sha">98c2d98fe33439563047eef97c6db065c8361bbe</checksum>
+    <location href="repodata/patch-capi4hylafax-2366.xml"/>
+  </patch>
+  <patch id="resmgr-2371">
+    <checksum type="sha">f449fabf0a7bedfc9f561ea5d04e160c521a5186</checksum>
+    <location href="repodata/patch-resmgr-2371.xml"/>
+  </patch>
+  <patch id="release-notes-2374">
+    <checksum type="sha">e1dd5a93ce9bb796348557d7d8e16ef54fb8d90d</checksum>
+    <location href="repodata/patch-release-notes-2374.xml"/>
+  </patch>
+  <patch id="privoxy-2375">
+    <checksum type="sha">a73445a3630084c867d63868d9d064625a84ea6d</checksum>
+    <location href="repodata/patch-privoxy-2375.xml"/>
+  </patch>
+  <patch id="kdeaddons3-konqueror-2383">
+    <checksum type="sha">792bad7440845613b608c771c2d948607e02759d</checksum>
+    <location href="repodata/patch-kdeaddons3-konqueror-2383.xml"/>
+  </patch>
+  <patch id="krusader-2386">
+    <checksum type="sha">de603b359e148a7666b5b695b76602c4689ff193</checksum>
+    <location href="repodata/patch-krusader-2386.xml"/>
+  </patch>
+  <patch id="gdm-2387">
+    <checksum type="sha">6dff2c1367613b7eae36db090233bfbf31a92920</checksum>
+    <location href="repodata/patch-gdm-2387.xml"/>
+  </patch>
+  <patch id="gpg-2388">
+    <checksum type="sha">3266d24fa2fb7aeb4ba303ccd77fbe8b27f75ad0</checksum>
+    <location href="repodata/patch-gpg-2388.xml"/>
+  </patch>
+  <patch id="clamav-2391">
+    <checksum type="sha">394bec89d3f5f976a9f7ae074a0e4c0d5582139a</checksum>
+    <location href="repodata/patch-clamav-2391.xml"/>
+  </patch>
+  <patch id="mono-core-2392">
+    <checksum type="sha">6630055daac876bcb0a6ebf36f63d28341b1ff6e</checksum>
+    <location href="repodata/patch-mono-core-2392.xml"/>
+  </patch>
+  <patch id="sysstat-2401">
+    <checksum type="sha">d3de77e29165bdbce88310a6ce9f97f3a5368377</checksum>
+    <location href="repodata/patch-sysstat-2401.xml"/>
+  </patch>
+  <patch id="xorg-x11-server-2403">
+    <checksum type="sha">905a98399c20efb91592d2767ec187823c7b154d</checksum>
+    <location href="repodata/patch-xorg-x11-server-2403.xml"/>
+  </patch>
+  <patch id="dazuko-2404">
+    <checksum type="sha">5314e6c2d4e21a38dd2ea05ee0495d7dd27f0931</checksum>
+    <location href="repodata/patch-dazuko-2404.xml"/>
+  </patch>
+  <patch id="cups-2406">
+    <checksum type="sha">329b6083dfe59ddb7d50cb9e92686eb7ecc48d6d</checksum>
+    <location href="repodata/patch-cups-2406.xml"/>
+  </patch>
+  <patch id="squirrelmail-2409">
+    <checksum type="sha">b5ec61bcf901f4270fc3ba6a18926c20102c94c9</checksum>
+    <location href="repodata/patch-squirrelmail-2409.xml"/>
+  </patch>
+  <patch id="wxGTK-2411">
+    <checksum type="sha">c38d7746f8aca8d40347879bfba5d977ca8fc03f</checksum>
+    <location href="repodata/patch-wxGTK-2411.xml"/>
+  </patch>
+  <patch id="ImageMagick-2413">
+    <checksum type="sha">c1dca99b03a2b012c141a64c8fcc494d957baed2</checksum>
+    <location href="repodata/patch-ImageMagick-2413.xml"/>
+  </patch>
+  <patch id="squirrelmail-2417">
+    <checksum type="sha">f239f615760d15515bd4f0316cc99e40d25a6117</checksum>
+    <location href="repodata/patch-squirrelmail-2417.xml"/>
+  </patch>
+  <patch id="MozillaFirefox-2418">
+    <checksum type="sha">f30517b5083e9bc007adf9cc974a36fc7bba3f0b</checksum>
+    <location href="repodata/patch-MozillaFirefox-2418.xml"/>
+  </patch>
+  <patch id="liboil-2419">
+    <checksum type="sha">24769a5bbd9e57cd59806b186088d5f0dffa5e30</checksum>
+    <location href="repodata/patch-liboil-2419.xml"/>
+  </patch>
+  <patch id="MozillaThunderbird-2421">
+    <checksum type="sha">6c2aa38c513f7eeda2b38555182b8200c08f6468</checksum>
+    <location href="repodata/patch-MozillaThunderbird-2421.xml"/>
+  </patch>
+  <patch id="pm-utils-2422">
+    <checksum type="sha">c0b54062cb5b16cbb18d383beb721c60b2a36f72</checksum>
+    <location href="repodata/patch-pm-utils-2422.xml"/>
+  </patch>
+  <patch id="sysvinit-2424">
+    <checksum type="sha">640a5b7ca790a3becc9c26d8434adfac560a14c5</checksum>
+    <location href="repodata/patch-sysvinit-2424.xml"/>
+  </patch>
+  <patch id="java-1_4_2-sun-2425">
+    <checksum type="sha">94c690e13540511db05ee397e9ab5fc9905b086b</checksum>
+    <location href="repodata/patch-java-1_4_2-sun-2425.xml"/>
+  </patch>
+  <patch id="java-1_5_0-sun-2427">
+    <checksum type="sha">fb1786ca30124e82b24e57b694a58da9f920e6b3</checksum>
+    <location href="repodata/patch-java-1_5_0-sun-2427.xml"/>
+  </patch>
+  <patch id="cups-2430">
+    <checksum type="sha">bb6aea44ad2e2ddb0f50842b15f791b7c0a0b346</checksum>
+    <location href="repodata/patch-cups-2430.xml"/>
+  </patch>
+  <patch id="w3m-2433">
+    <checksum type="sha">d9ba43f21238b71a7b8cfb6d9256e404cffb7cc0</checksum>
+    <location href="repodata/patch-w3m-2433.xml"/>
+  </patch>
+  <patch id="unison-2436">
+    <checksum type="sha">42e904686825e904752238b85085771ee51965c7</checksum>
+    <location href="repodata/patch-unison-2436.xml"/>
+  </patch>
+  <patch id="lvm2-2438">
+    <checksum type="sha">cd88e95ae02706069fe6f3031178bf26e9e06d3a</checksum>
+    <location href="repodata/patch-lvm2-2438.xml"/>
+  </patch>
+  <patch id="yast2-sudo-2441">
+    <checksum type="sha">ab7741ef2411c11a7ec941fc8736feebfca73398</checksum>
+    <location href="repodata/patch-yast2-sudo-2441.xml"/>
+  </patch>
+  <patch id="krb5-2442">
+    <checksum type="sha">2f8c1cd5d147c89b06c0898d4392f1196098236f</checksum>
+    <location href="repodata/patch-krb5-2442.xml"/>
+  </patch>
+  <patch id="xorg-x11-server-2444">
+    <checksum type="sha">a26f9f36ed0a0f6a45cb5369234ff44d6c535310</checksum>
+    <location href="repodata/patch-xorg-x11-server-2444.xml"/>
+  </patch>
+  <patch id="sax2-2445">
+    <checksum type="sha">61a24339218f1fd480085fe0e3f754baa8820cb3</checksum>
+    <location href="repodata/patch-sax2-2445.xml"/>
+  </patch>
+  <patch id="python-2446">
+    <checksum type="sha">c37bf9a8ff3810e2c9a674bd9aadfed24f8c2ab2</checksum>
+    <location href="repodata/patch-python-2446.xml"/>
+  </patch>
+  <patch id="cacti-2447">
+    <checksum type="sha">2cdfac93ac338ccf87abe8049f13bb415428a279</checksum>
+    <location href="repodata/patch-cacti-2447.xml"/>
+  </patch>
+  <patch id="kdelibs3-2448">
+    <checksum type="sha">9adf70721a91e7666c40c45c1c2270bd673d2ac4</checksum>
+    <location href="repodata/patch-kdelibs3-2448.xml"/>
+  </patch>
+  <patch id="kdeutils3-extra-2450">
+    <checksum type="sha">d0a21646dc9d1c244e7e35888a0fb52aaeb1b5e2</checksum>
+    <location href="repodata/patch-kdeutils3-extra-2450.xml"/>
+  </patch>
+  <patch id="jarnal-2451">
+    <checksum type="sha">8daf1728afb47779fd28e0021ea6ff55f18f1149</checksum>
+    <location href="repodata/patch-jarnal-2451.xml"/>
+  </patch>
+  <patch id="kdelibs3-2452">
+    <checksum type="sha">a515c1a96ac1b55f50852cfda93a49004ce4c79a</checksum>
+    <location href="repodata/patch-kdelibs3-2452.xml"/>
+  </patch>
+  <patch id="xorg-x11-server-2453">
+    <checksum type="sha">a3f5b8927b7f1f47f347e7ee3ab821995dca00b7</checksum>
+    <location href="repodata/patch-xorg-x11-server-2453.xml"/>
+  </patch>
+  <patch id="cyrus-imapd-2454">
+    <checksum type="sha">4fdf8c67eaa488edbe668ead3d995caa38a1936b</checksum>
+    <location href="repodata/patch-cyrus-imapd-2454.xml"/>
+  </patch>
+  <patch id="opera-2456">
+    <checksum type="sha">5209e7b0a7f89ec7c225de065d4cbc4fbefcbddb</checksum>
+    <location href="repodata/patch-opera-2456.xml"/>
+  </patch>
+  <patch id="mediawiki-2457">
+    <checksum type="sha">669837ce13f07df59ac79ce6201c176342ed27c4</checksum>
+    <location href="repodata/patch-mediawiki-2457.xml"/>
+  </patch>
+  <patch id="compiz-2458">
+    <checksum type="sha">a41806bf5d57c36825ad832be3d0f13d1695d70a</checksum>
+    <location href="repodata/patch-compiz-2458.xml"/>
+  </patch>
+  <patch id="libzypp-2460">
+    <checksum type="sha">bdbbba1841bebd9822458f3a3fb1c88141e78281</checksum>
+    <location href="repodata/patch-libzypp-2460.xml"/>
+  </patch>
+  <patch id="bzip2-2465">
+    <checksum type="sha">1b84ea4aa922b21a7039330c0fdbf2775dd13444</checksum>
+    <location href="repodata/patch-bzip2-2465.xml"/>
+  </patch>
+  <patch id="autoyast2-2466">
+    <checksum type="sha">c7266496955cee3242ae100ffd731ce4323d6b89</checksum>
+    <location href="repodata/patch-autoyast2-2466.xml"/>
+  </patch>
+  <patch id="squid-2467">
+    <checksum type="sha">883f737c6ecedd05670bad4784a37e2c67a8c5da</checksum>
+    <location href="repodata/patch-squid-2467.xml"/>
+  </patch>
+  <patch id="totem-2468">
+    <checksum type="sha">538c771158fa860793ed285990641b1ed04330b5</checksum>
+    <location href="repodata/patch-totem-2468.xml"/>
+  </patch>
+  <patch id="java-1_4_2-sun-demo-2469">
+    <checksum type="sha">32ae23cb16bff2f8e6ec4ba168e6a75d541efc8c</checksum>
+    <location href="repodata/patch-java-1_4_2-sun-demo-2469.xml"/>
+  </patch>
+  <patch id="ulogd-2470">
+    <checksum type="sha">cea742686fabc65af02a9dd7bea89b39cbdb62c1</checksum>
+    <location href="repodata/patch-ulogd-2470.xml"/>
+  </patch>
+  <patch id="xpdf-tools-2472">
+    <checksum type="sha">8f021c3fec51df6155c44276b87260bbf074c0a2</checksum>
+    <location href="repodata/patch-xpdf-tools-2472.xml"/>
+  </patch>
+  <patch id="xpdf-2473">
+    <checksum type="sha">73e3b82edf6537e759d3c7fe103654baad9155cf</checksum>
+    <location href="repodata/patch-xpdf-2473.xml"/>
+  </patch>
+  <patch id="neon-2476">
+    <checksum type="sha">9b83d8d408138a521c7926be0c569047eb5d866a</checksum>
+    <location href="repodata/patch-neon-2476.xml"/>
+  </patch>
+  <patch id="libzypp-2477">
+    <checksum type="sha">837cb40fa331f256281577e8ca81fe73b9c27e2a</checksum>
+    <location href="repodata/patch-libzypp-2477.xml"/>
+  </patch>
+  <patch id="gtk2-2479">
+    <checksum type="sha">050a31137f1ff1ca986c27addaf894deaa10b6ad</checksum>
+    <location href="repodata/patch-gtk2-2479.xml"/>
+  </patch>
+  <patch id="nss_ldap-2480">
+    <checksum type="sha">cd51f150dda049a82acca2ef4b0524465947ddc2</checksum>
+    <location href="repodata/patch-nss_ldap-2480.xml"/>
+  </patch>
+  <patch id="cups-2481">
+    <checksum type="sha">ec3095ea0c8b996b49061d79f7afb780ab4ea84b</checksum>
+    <location href="repodata/patch-cups-2481.xml"/>
+  </patch>
+  <patch id="bluez-utils-2482">
+    <checksum type="sha">c53aeb36ef68f4f1b6e6ac0f38df63c22f22359d</checksum>
+    <location href="repodata/patch-bluez-utils-2482.xml"/>
+  </patch>
+  <patch id="xine-lib-2487">
+    <checksum type="sha">31c74fc5d4ba42bf3301fa95b806caa978659127</checksum>
+    <location href="repodata/patch-xine-lib-2487.xml"/>
+  </patch>
+  <patch id="kdegraphics3-2489">
+    <checksum type="sha">7cbcd6c1064568aeb0e523d2587336e742e44fcb</checksum>
+    <location href="repodata/patch-kdegraphics3-2489.xml"/>
+  </patch>
+  <patch id="xorg-x11-Xvnc-2491">
+    <checksum type="sha">4f331f6ea3e5c5a90f66a871162114ca94f92f76</checksum>
+    <location href="repodata/patch-xorg-x11-Xvnc-2491.xml"/>
+  </patch>
+  <patch id="bluez-utils-2492">
+    <checksum type="sha">b5a4c65f2356613b4de6ad1bc2dd0582e77b0ee6</checksum>
+    <location href="repodata/patch-bluez-utils-2492.xml"/>
+  </patch>
+  <patch id="koffice-2495">
+    <checksum type="sha">18e4e361c7688a57f7b7963c359fee4f58d29b08</checksum>
+    <location href="repodata/patch-koffice-2495.xml"/>
+  </patch>
+  <patch id="gtk2-2499">
+    <checksum type="sha">f812f28e10039b5bfd249e3902b905e92a7eb769</checksum>
+    <location href="repodata/patch-gtk2-2499.xml"/>
+  </patch>
+  <patch id="hal-2500">
+    <checksum type="sha">90c4516d8073322a05f4235dbe1f7c4770c6e4e0</checksum>
+    <location href="repodata/patch-hal-2500.xml"/>
+  </patch>
+  <patch id="libsoup-2503">
+    <checksum type="sha">7d9f7a7fdd96843f5d962aef984cc0c5c2534f30</checksum>
+    <location href="repodata/patch-libsoup-2503.xml"/>
+  </patch>
+  <patch id="squid-2504">
+    <checksum type="sha">fffd63b332907c46b926499e27b6ec0ad9025ddb</checksum>
+    <location href="repodata/patch-squid-2504.xml"/>
+  </patch>
+  <patch id="acroread-2506">
+    <checksum type="sha">b0bbd764da5ec2651bf030e7051d045341ddbdd7</checksum>
+    <location href="repodata/patch-acroread-2506.xml"/>
+  </patch>
+  <patch id="flash-player-2509">
+    <checksum type="sha">5e2c95f36ceb9c7d94c4a2df8ec60aaf36dde810</checksum>
+    <location href="repodata/patch-flash-player-2509.xml"/>
+  </patch>
+  <patch id="compat-g77-2510">
+    <checksum type="sha">11a15f477e35a5e4879a8883074c13e6ab321141</checksum>
+    <location href="repodata/patch-compat-g77-2510.xml"/>
+  </patch>
+  <patch id="libgtop-2512">
+    <checksum type="sha">c71c5d298bf5b11fcc0c0865b1302a7ba9970c0c</checksum>
+    <location href="repodata/patch-libgtop-2512.xml"/>
+  </patch>
+  <patch id="zypper-2513">
+    <checksum type="sha">fb916bb899aa6d2b1ae8d211ee84ceb0a66f1fd8</checksum>
+    <location href="repodata/patch-zypper-2513.xml"/>
+  </patch>
+  <patch id="smb4k-2514">
+    <checksum type="sha">558c89fe1d04efa2d7c5988be9344376f2eacace</checksum>
+    <location href="repodata/patch-smb4k-2514.xml"/>
+  </patch>
+  <patch id="amarok-2516">
+    <checksum type="sha">18473edb5c4b6853aea94db3883bcfe496b5b6ee</checksum>
+    <location href="repodata/patch-amarok-2516.xml"/>
+  </patch>
+  <patch id="yast2-trans-de-2518">
+    <checksum type="sha">4c3d0446ffd36ce86b40da43135913a18349ef44</checksum>
+    <location href="repodata/patch-yast2-trans-de-2518.xml"/>
+  </patch>
+  <patch id="compiz-2519">
+    <checksum type="sha">188b82cfb54dd61f0fe0e5beea054d5a9591e0a5</checksum>
+    <location href="repodata/patch-compiz-2519.xml"/>
+  </patch>
+  <patch id="fetchmail-2520">
+    <checksum type="sha">85a08c9bc4eebac24854a9eb7da257a3fe8f5b6d</checksum>
+    <location href="repodata/patch-fetchmail-2520.xml"/>
+  </patch>
+  <patch id="spamassassin-2523">
+    <checksum type="sha">e711370f446cf3538fbaf64c9faaf0eadaedb580</checksum>
+    <location href="repodata/patch-spamassassin-2523.xml"/>
+  </patch>
+  <patch id="libzypp-2524">
+    <checksum type="sha">16921f0179c7c3dcb504b9ffb82abd32e84086ac</checksum>
+    <location href="repodata/patch-libzypp-2524.xml"/>
+  </patch>
+  <patch id="yast2-printer-2525">
+    <checksum type="sha">147a7b5f084395bd427c273ba8f25f312cc87193</checksum>
+    <location href="repodata/patch-yast2-printer-2525.xml"/>
+  </patch>
+  <patch id="powersave-2526">
+    <checksum type="sha">afd07ae38e0585e615022a7df3cd469c2c99fdbb</checksum>
+    <location href="repodata/patch-powersave-2526.xml"/>
+  </patch>
+  <patch id="cups-2527">
+    <checksum type="sha">5b9d9747bffc3368ea2f7c8c7b3ff8e0cc4605f7</checksum>
+    <location href="repodata/patch-cups-2527.xml"/>
+  </patch>
+  <patch id="bind-2529">
+    <checksum type="sha">1b1c1fdb9ad132cd6504c8b437cfe519bc9c0dfa</checksum>
+    <location href="repodata/patch-bind-2529.xml"/>
+  </patch>
+  <patch id="libzypp-2533">
+    <checksum type="sha">2af5b888f4f4c9b9be8b9bc2aaae02efd436b924</checksum>
+    <location href="repodata/patch-libzypp-2533.xml"/>
+  </patch>
+  <patch id="chmlib-2536">
+    <checksum type="sha">664ef6d8c867824f20b15a9af4d2957fd79897bd</checksum>
+    <location href="repodata/patch-chmlib-2536.xml"/>
+  </patch>
+  <patch id="kchmviewer-2539">
+    <checksum type="sha">ad29afc8f5dd7ca1f4faa4a2aaed644c80d844b6</checksum>
+    <location href="repodata/patch-kchmviewer-2539.xml"/>
+  </patch>
+  <patch id="rrdtool-2540">
+    <checksum type="sha">8cc33adb78b9fccccc7f2fc03d5df13f8a79eae5</checksum>
+    <location href="repodata/patch-rrdtool-2540.xml"/>
+  </patch>
+  <patch id="yast2-sound-2541">
+    <checksum type="sha">ccece044c2ba608c8233d70a4aa68c466c1862f1</checksum>
+    <location href="repodata/patch-yast2-sound-2541.xml"/>
+  </patch>
+  <patch id="fetchmail-2542">
+    <checksum type="sha">339f1a841955652ccaccd9c9e936c21abe85a2f2</checksum>
+    <location href="repodata/patch-fetchmail-2542.xml"/>
+  </patch>
+  <patch id="kdenetwork3-InstantMessenger-2547">
+    <checksum type="sha">3ef0e4f08055c2f458c0efa5dfdd4d7aee384122</checksum>
+    <location href="repodata/patch-kdenetwork3-InstantMessenger-2547.xml"/>
+  </patch>
+  <patch id="openssl-2548">
+    <checksum type="sha">67602c57de3f98ffd2c7434b6952f79481feceb8</checksum>
+    <location href="repodata/patch-openssl-2548.xml"/>
+  </patch>
+  <patch id="autofs-2549">
+    <checksum type="sha">4da7e1958d56142f48d99b9ab1b16425aeb0cd96</checksum>
+    <location href="repodata/patch-autofs-2549.xml"/>
+  </patch>
+  <patch id="qemu-2550">
+    <checksum type="sha">940c331de3e40be9f570e012b8ca9bbab6d594d2</checksum>
+    <location href="repodata/patch-qemu-2550.xml"/>
+  </patch>
+  <patch id="cross-avr-binutils-2551">
+    <checksum type="sha">da26f814db1c2f04cd8fc27495aecdbd66f73daf</checksum>
+    <location href="repodata/patch-cross-avr-binutils-2551.xml"/>
+  </patch>
+  <patch id="digikam-2552">
+    <checksum type="sha">299f0dff3447555cea60a63fdf97e81e1bd56096</checksum>
+    <location href="repodata/patch-digikam-2552.xml"/>
+  </patch>
+  <patch id="samba-2555">
+    <checksum type="sha">a7a501a1465dfcb3e125d14c1a67efd2638be166</checksum>
+    <location href="repodata/patch-samba-2555.xml"/>
+  </patch>
+  <patch id="fetchmail-2563">
+    <checksum type="sha">10b87459f9209a43097bebd31f9ec8cff4354c11</checksum>
+    <location href="repodata/patch-fetchmail-2563.xml"/>
+  </patch>
+  <patch id="kdegraphics3-pdf-2565">
+    <checksum type="sha">8d0d0b963c5fa85c92d5a9b5c86c88063b2faf8e</checksum>
+    <location href="repodata/patch-kdegraphics3-pdf-2565.xml"/>
+  </patch>
+  <patch id="koffice-wordprocessing-2577">
+    <checksum type="sha">5f43e01206494f0d0cbab846bfbb157285e22efc</checksum>
+    <location href="repodata/patch-koffice-wordprocessing-2577.xml"/>
+  </patch>
+  <patch id="samba-2584">
+    <checksum type="sha">5e86626239c4798708e7d2432708cf98d6a001d2</checksum>
+    <location href="repodata/patch-samba-2584.xml"/>
+  </patch>
+  <patch id="ImageMagick-2585">
+    <checksum type="sha">5af58df93d8f36428726d0cf55fc5bb6a18a8bfd</checksum>
+    <location href="repodata/patch-ImageMagick-2585.xml"/>
+  </patch>
+  <patch id="poppler-2590">
+    <checksum type="sha">924e7f989e3d6f159e717e2d76b21de572ce7ec2</checksum>
+    <location href="repodata/patch-poppler-2590.xml"/>
+  </patch>
+  <patch id="NetworkManager-kde-2591">
+    <checksum type="sha">93a54883e5b9170839fc81a72c534f76e7987c19</checksum>
+    <location href="repodata/patch-NetworkManager-kde-2591.xml"/>
+  </patch>
+  <patch id="GraphicsMagick-2593">
+    <checksum type="sha">64a57f29d835e46f7f00e939d4b505cb546d0654</checksum>
+    <location href="repodata/patch-GraphicsMagick-2593.xml"/>
+  </patch>
+  <patch id="chmlib-2595">
+    <checksum type="sha">88ee8256ac3875e2b087ab9723bb13f071f9de73</checksum>
+    <location href="repodata/patch-chmlib-2595.xml"/>
+  </patch>
+  <patch id="gpdf-2596">
+    <checksum type="sha">115f31bcb61f37f264daa4b0ef8220816be05de7</checksum>
+    <location href="repodata/patch-gpdf-2596.xml"/>
+  </patch>
+  <patch id="kdenetwork3-InstantMessenger-2599">
+    <checksum type="sha">c5a5fd01f5266d3f30fe33b5fc1679b35af3d4d4</checksum>
+    <location href="repodata/patch-kdenetwork3-InstantMessenger-2599.xml"/>
+  </patch>
+  <patch id="kdebase3-2600">
+    <checksum type="sha">b6654a37408c21d66e60cf7a78a1cddb991b797a</checksum>
+    <location href="repodata/patch-kdebase3-2600.xml"/>
+  </patch>
+  <patch id="pam-2601">
+    <checksum type="sha">fd76c123487f884cf10b161c87afe5b7bc9f05f8</checksum>
+    <location href="repodata/patch-pam-2601.xml"/>
+  </patch>
+  <patch id="fetchmail-2602">
+    <checksum type="sha">d3533c47354977c9f821eb4e579c43ea919d4488</checksum>
+    <location href="repodata/patch-fetchmail-2602.xml"/>
+  </patch>
+  <patch id="novfs-kmp-bigsmp-2630">
+    <checksum type="sha">06f48c215e805213e339fa581670fb35e2151786</checksum>
+    <location href="repodata/patch-novfs-kmp-bigsmp-2630.xml"/>
+  </patch>
+  <patch id="clamav-2632">
+    <checksum type="sha">b5217b10a1a3e513085665d994dded954a72e884</checksum>
+    <location href="repodata/patch-clamav-2632.xml"/>
+  </patch>
+  <patch id="timezone-2634">
+    <checksum type="sha">e2d31e95095a1c7dd242c577f3a394a39d7e75be</checksum>
+    <location href="repodata/patch-timezone-2634.xml"/>
+  </patch>
+  <patch id="gwenview-2637">
+    <checksum type="sha">b6f7e30d0d8f3e699e8593a178b5fd63c865ad0e</checksum>
+    <location href="repodata/patch-gwenview-2637.xml"/>
+  </patch>
+  <patch id="wireshark-2638">
+    <checksum type="sha">7ebc8745836d63bdfee712bca5c71ba328877047</checksum>
+    <location href="repodata/patch-wireshark-2638.xml"/>
+  </patch>
+  <patch id="hal-resmgr-2639">
+    <checksum type="sha">4eb206f1dba689554bc3e113fd87ad2a4fcdbf78</checksum>
+    <location href="repodata/patch-hal-resmgr-2639.xml"/>
+  </patch>
+  <patch id="klamav-2640">
+    <checksum type="sha">505cc81dc924efb58154806700b1d261cc33fbb9</checksum>
+    <location href="repodata/patch-klamav-2640.xml"/>
+  </patch>
+  <patch id="ekiga-2641">
+    <checksum type="sha">3d960c76d7dd38393c631c63212f0a8c4f08ad98</checksum>
+    <location href="repodata/patch-ekiga-2641.xml"/>
+  </patch>
+  <patch id="libwpd-2642">
+    <checksum type="sha">b31ced09fece6d2f9fad37653acfda9eb9e7ba2b</checksum>
+    <location href="repodata/patch-libwpd-2642.xml"/>
+  </patch>
+  <patch id="rubygems-2644">
+    <checksum type="sha">26a5b6e1a3c77afa7fd77171bcbef2d78f7df11f</checksum>
+    <location href="repodata/patch-rubygems-2644.xml"/>
+  </patch>
+  <patch id="MozillaFirefox-2647">
+    <checksum type="sha">420d992e8b4436cc7ebc0609183ba040db340dc5</checksum>
+    <location href="repodata/patch-MozillaFirefox-2647.xml"/>
+  </patch>
+  <patch id="koffice-wordprocessing-2648">
+    <checksum type="sha">bf0b411e4eb5494a27ebef8d5b9cd923da4fe2c8</checksum>
+    <location href="repodata/patch-koffice-wordprocessing-2648.xml"/>
+  </patch>
+  <patch id="agfa-fonts-2650">
+    <checksum type="sha">19126ca4e55852b98ffa69c6c3fcff12743af49c</checksum>
+    <location href="repodata/patch-agfa-fonts-2650.xml"/>
+  </patch>
+  <patch id="OpenOffice_org-2652">
+    <checksum type="sha">0bbcdeab9294272a677f9b4a7b57e13701244010</checksum>
+    <location href="repodata/patch-OpenOffice_org-2652.xml"/>
+  </patch>
+  <patch id="gdm-2653">
+    <checksum type="sha">df03ccdfcb0f188ac3072c5086ab86b425a66f8e</checksum>
+    <location href="repodata/patch-gdm-2653.xml"/>
+  </patch>
+  <patch id="ruby-2655">
+    <checksum type="sha">8ffefa033ac19a70938a7af71854184d6b902e45</checksum>
+    <location href="repodata/patch-ruby-2655.xml"/>
+  </patch>
+  <patch id="sylpheed-claws-2685">
+    <checksum type="sha">df121b6b7cc82715a4544f491a0104b50a28eb20</checksum>
+    <location href="repodata/patch-sylpheed-claws-2685.xml"/>
+  </patch>
+  <patch id="php5-2687">
+    <checksum type="sha">f34eb5a2f4399578c78e6dca5dd165077787c806</checksum>
+    <location href="repodata/patch-php5-2687.xml"/>
+  </patch>
+  <patch id="klamav-2688">
+    <checksum type="sha">da442e32fe4401897261051bf817e1a029e804c3</checksum>
+    <location href="repodata/patch-klamav-2688.xml"/>
+  </patch>
+  <patch id="clamav-2690">
+    <checksum type="sha">ff002a87486fbd2c68b445e58b6ac0d307e8dbad</checksum>
+    <location href="repodata/patch-clamav-2690.xml"/>
+  </patch>
+  <patch id="seamonkey-2691">
+    <checksum type="sha">fd8198e09f073515c67cf90a879ff57bfde0cfa9</checksum>
+    <location href="repodata/patch-seamonkey-2691.xml"/>
+  </patch>
+  <patch id="gnokii-2689">
+    <checksum type="sha">2453275e874a7d65c9f21cc50885f50202d617bf</checksum>
+    <location href="repodata/patch-gnokii-2689.xml"/>
+  </patch>
+  <patch id="doxygen-2694">
+    <checksum type="sha">fad24b42e3d7d8c35e410017ca85dd375f2b7f1d</checksum>
+    <location href="repodata/patch-doxygen-2694.xml"/>
+  </patch>
+  <patch id="enlightenment-2695">
+    <checksum type="sha">e74c7b27498161c9ba056869ba1813658f4ef4e0</checksum>
+    <location href="repodata/patch-enlightenment-2695.xml"/>
+  </patch>
+  <patch id="gnome-terminal-2696">
+    <checksum type="sha">36f9b9e6ff739fe7d82a58b59fcb74be6581a752</checksum>
+    <location href="repodata/patch-gnome-terminal-2696.xml"/>
+  </patch>
+  <patch id="kpowersave-2698">
+    <checksum type="sha">c1e569e948b46ddcd33369c9479e6ad92fb8c7ec</checksum>
+    <location href="repodata/patch-kpowersave-2698.xml"/>
+  </patch>
+  <patch id="clamav-2700">
+    <checksum type="sha">3dd423deeb41c58b11f54184d59367d152d78f24</checksum>
+    <location href="repodata/patch-clamav-2700.xml"/>
+  </patch>
+  <patch id="autofs-2703">
+    <checksum type="sha">678cae267889c434cbdd81654066244270a341b6</checksum>
+    <location href="repodata/patch-autofs-2703.xml"/>
+  </patch>
+  <patch id="ivtv-kmp-bigsmp-2704">
+    <checksum type="sha">9539aa93b99854e21cc69248bd82249f75a6f6e0</checksum>
+    <location href="repodata/patch-ivtv-kmp-bigsmp-2704.xml"/>
+  </patch>
+  <patch id="kernel-2705">
+    <checksum type="sha">fedf719a3be03d6c93fc04f78b50ce7be1465606</checksum>
+    <location href="repodata/patch-kernel-2705.xml"/>
+  </patch>
+  <patch id="yast2-printer-2706">
+    <checksum type="sha">b95d82b3c1e22c1ce2ec2ea96ffb9ba94e1a2adb</checksum>
+    <location href="repodata/patch-yast2-printer-2706.xml"/>
+  </patch>
+  <patch id="MozillaThunderbird-2734">
+    <checksum type="sha">be5018a378fbce22ad2f1f94266f251dfb0fecc2</checksum>
+    <location href="repodata/patch-MozillaThunderbird-2734.xml"/>
+  </patch>
+  <patch id="gstreamer010-plugins-base-2737">
+    <checksum type="sha">93496c003d8a1565e88fa37bc28b62b685b86223</checksum>
+    <location href="repodata/patch-gstreamer010-plugins-base-2737.xml"/>
+  </patch>
+  <patch id="gstreamer010-plugins-base-2805">
+    <checksum type="sha">40b9fd23e3eb4141d8b28794c40aeebdee7dcc1d</checksum>
+    <location href="repodata/patch-gstreamer010-plugins-base-2805.xml"/>
+  </patch>
+  <patch id="evolution-2824">
+    <checksum type="sha">7d6301a829eda6ff7ab006302c38943693defd44</checksum>
+    <location href="repodata/patch-evolution-2824.xml"/>
+  </patch>
+  <patch id="avahi-2982">
+    <checksum type="sha">c338bfb439e94dc27774a76d860285577b9ade19</checksum>
+    <location href="repodata/patch-avahi-2982.xml"/>
+  </patch>
+  <patch id="syslog-ng-2984">
+    <checksum type="sha">eb0c412124838a4ff2ff884642a88fac1578da8f</checksum>
+    <location href="repodata/patch-syslog-ng-2984.xml"/>
+  </patch>
+  <patch id="tomcat5-2985">
+    <checksum type="sha">95ce884347ae5fb4f8b030f9e302f75dec58cefd</checksum>
+    <location href="repodata/patch-tomcat5-2985.xml"/>
+  </patch>
+  <patch id="xine-lib-2989">
+    <checksum type="sha">91dba1fffdd033eb289fec17dd2a288658dbb586</checksum>
+    <location href="repodata/patch-xine-lib-2989.xml"/>
+  </patch>
+  <patch id="xorg-x11-libs-3070">
+    <checksum type="sha">069398c5d7b94dc32c6102243b478584a76b84bb</checksum>
+    <location href="repodata/patch-xorg-x11-libs-3070.xml"/>
+  </patch>
+  <patch id="rekall-2991">
+    <checksum type="sha">2c534ad1e2a36b668f7d22f37b5e0457f5892359</checksum>
+    <location href="repodata/patch-rekall-2991.xml"/>
+  </patch>
+  <patch id="gpg-2995">
+    <checksum type="sha">bce7d390d3db0bdedf63304a9b739645e7c10eb8</checksum>
+    <location href="repodata/patch-gpg-2995.xml"/>
+  </patch>
+  <patch id="gstreamer010-plugins-base-2992">
+    <checksum type="sha">5d0fa6b2d1a729ed86d6bdc54bb3eeabb1bfea38</checksum>
+    <location href="repodata/patch-gstreamer010-plugins-base-2992.xml"/>
+  </patch>
+  <patch id="unrar-2996">
+    <checksum type="sha">c1e7abcd4dc9ffda6f97dc8c98e68d36f0fa51e5</checksum>
+    <location href="repodata/patch-unrar-2996.xml"/>
+  </patch>
+  <patch id="ktorrent-2998">
+    <checksum type="sha">ff9e274c83ef488d9eed486c7eae9f2952d4c29e</checksum>
+    <location href="repodata/patch-ktorrent-2998.xml"/>
+  </patch>
+  <patch id="ekiga-3023">
+    <checksum type="sha">2dd405d9651c0f9b87412b07488d4b03796390e9</checksum>
+    <location href="repodata/patch-ekiga-3023.xml"/>
+  </patch>
+  <patch id="krb5-apps-servers-3021">
+    <checksum type="sha">f4b8a55a8b737a513c4531487888ff231126cce9</checksum>
+    <location href="repodata/patch-krb5-apps-servers-3021.xml"/>
+  </patch>
+  <patch id="pam_ssh-3024">
+    <checksum type="sha">5413228a54cf99ce20ad08e6917605f7a9ceeabe</checksum>
+    <location href="repodata/patch-pam_ssh-3024.xml"/>
+  </patch>
+  <patch id="TeXmacs-3030">
+    <checksum type="sha">a17b3f3406caf33fb9160d2464b0945bc2b0a8ad</checksum>
+    <location href="repodata/patch-TeXmacs-3030.xml"/>
+  </patch>
+  <patch id="perl-Bootloader-3029">
+    <checksum type="sha">2ec2f7e3abf84e60ce2a2e3935e3373242f97d27</checksum>
+    <location href="repodata/patch-perl-Bootloader-3029.xml"/>
+  </patch>
+  <patch id="kernel-3032">
+    <checksum type="sha">b83c95c8f2337d42ea1b8b1e606ca83c8acc17b8</checksum>
+    <location href="repodata/patch-kernel-3032.xml"/>
+  </patch>
+  <patch id="NetworkManager-3031">
+    <checksum type="sha">89d9e49eb0c49803ab2bd5d51570f8e688f93196</checksum>
+    <location href="repodata/patch-NetworkManager-3031.xml"/>
+  </patch>
+  <patch id="file-3033">
+    <checksum type="sha">9501fa94de1e6e9a896cc2e29d66424e17d429e4</checksum>
+    <location href="repodata/patch-file-3033.xml"/>
+  </patch>
+  <patch id="squid-3036">
+    <checksum type="sha">882e3dad4edfc0e2e923d4fc49c78341acea368d</checksum>
+    <location href="repodata/patch-squid-3036.xml"/>
+  </patch>
+  <patch id="libwpd-3038">
+    <checksum type="sha">03671597239a76a8716da20528d4a71d24e44825</checksum>
+    <location href="repodata/patch-libwpd-3038.xml"/>
+  </patch>
+  <patch id="opensuse-updater-3037">
+    <checksum type="sha">f6a3e069f70f9250d7ae0c5f7497999714552ddc</checksum>
+    <location href="repodata/patch-opensuse-updater-3037.xml"/>
+  </patch>
+  <patch id="krb5-3045">
+    <checksum type="sha">a7a9803370986ec4de2913c416db11a0c207d8a1</checksum>
+    <location href="repodata/patch-krb5-3045.xml"/>
+  </patch>
+  <patch id="qt3-3048">
+    <checksum type="sha">4b3f9bbb0f413a4b70392219f501ad782e5c647a</checksum>
+    <location href="repodata/patch-qt3-3048.xml"/>
+  </patch>
+  <patch id="gwenview-3055">
+    <checksum type="sha">7f65de9106151c59e1b91eab1c195e5765445894</checksum>
+    <location href="repodata/patch-gwenview-3055.xml"/>
+  </patch>
+  <patch id="libqt4-3056">
+    <checksum type="sha">1c801d3a719e843a50f43faceff0bd2bf77f3e82</checksum>
+    <location href="repodata/patch-libqt4-3056.xml"/>
+  </patch>
+  <patch id="ktorrent-3057">
+    <checksum type="sha">06cd8e287e5cc94bf936726e9a8540283ad42af5</checksum>
+    <location href="repodata/patch-ktorrent-3057.xml"/>
+  </patch>
+  <patch id="kdelibs3-3058">
+    <checksum type="sha">555d3279e2485cae5feb3a7c57b0ee0f16ae8ba6</checksum>
+    <location href="repodata/patch-kdelibs3-3058.xml"/>
+  </patch>
+  <patch id="inkscape-3062">
+    <checksum type="sha">cd767b5b8690e08ec5b1f231641cd6a4a7d1dec4</checksum>
+    <location href="repodata/patch-inkscape-3062.xml"/>
+  </patch>
+  <patch id="perl-Bootloader-3059">
+    <checksum type="sha">24e57eaa5bc4c6080577f7555b9b28ba30c298e6</checksum>
+    <location href="repodata/patch-perl-Bootloader-3059.xml"/>
+  </patch>
+  <patch id="scpm-3064">
+    <checksum type="sha">262095e2c3ad96a3c892c0e06baad6c995ac52b9</checksum>
+    <location href="repodata/patch-scpm-3064.xml"/>
+  </patch>
+  <patch id="mediawiki-3065">
+    <checksum type="sha">beba36db67a885e7a43a2b8d0f5b6a1e394fad39</checksum>
+    <location href="repodata/patch-mediawiki-3065.xml"/>
+  </patch>
+  <patch id="freetype2-3066">
+    <checksum type="sha">079e72f31dcd4e20a786501b6670d581480cb249</checksum>
+    <location href="repodata/patch-freetype2-3066.xml"/>
+  </patch>
+  <patch id="xorg-x11-libX11-3069">
+    <checksum type="sha">b7bf854b78d3ba9488e4a9c46bf48d6e7501ead5</checksum>
+    <location href="repodata/patch-xorg-x11-libX11-3069.xml"/>
+  </patch>
+  <patch id="xorg-x11-server-3071">
+    <checksum type="sha">8dd539e1cb8196a3bde56ec0e5d9717bf74d3696</checksum>
+    <location href="repodata/patch-xorg-x11-server-3071.xml"/>
+  </patch>
+  <patch id="xmms-3073">
+    <checksum type="sha">f96cebcb74f4e9067976240f8da5dff3dbc954e3</checksum>
+    <location href="repodata/patch-xmms-3073.xml"/>
+  </patch>
+  <patch id="spamassassin-3077">
+    <checksum type="sha">5d11f1e0772a8eae3d0276c2fc0c36fd024ff969</checksum>
+    <location href="repodata/patch-spamassassin-3077.xml"/>
+  </patch>
+</patches>
diff --git a/tests/parser/yum/data/patches-1.xml.solution b/tests/parser/yum/data/patches-1.xml.solution
new file mode 100644 (file)
index 0000000..6df7288
--- /dev/null
@@ -0,0 +1,760 @@
+fetchmsttfonts.sh-2333
+sha
+6b72b4f3617d0d51af28399c0f5e0af401440245
+repodata/patch-fetchmsttfonts.sh-2333.xml
+glabels-2348
+sha
+b02ba598d8ed5f8a31859d3b34e72e1ddecbe894
+repodata/patch-glabels-2348.xml
+openssl-2349
+sha
+321ee41de68be4e83dfb74559c14300a59b85ccf
+repodata/patch-openssl-2349.xml
+gv-2350
+sha
+ec9e8a3f3ce2588cecd84ab95ec910d41db5d74b
+repodata/patch-gv-2350.xml
+tar-2351
+sha
+d803372cd5d844ee01ab6fb3d1b4332391fa1206
+repodata/patch-tar-2351.xml
+flash-player-2359
+sha
+c2de5dd35ec2dcccc118d9d7f539e768dfd5ec50
+repodata/patch-flash-player-2359.xml
+lineak_kde-2361
+sha
+7fb791963114836621873280c7edd58887b91784
+repodata/patch-lineak_kde-2361.xml
+evince-2362
+sha
+28f0c33aa4a34830a4b7b8a795db609bfd3d7531
+repodata/patch-evince-2362.xml
+capi4hylafax-2366
+sha
+98c2d98fe33439563047eef97c6db065c8361bbe
+repodata/patch-capi4hylafax-2366.xml
+resmgr-2371
+sha
+f449fabf0a7bedfc9f561ea5d04e160c521a5186
+repodata/patch-resmgr-2371.xml
+release-notes-2374
+sha
+e1dd5a93ce9bb796348557d7d8e16ef54fb8d90d
+repodata/patch-release-notes-2374.xml
+privoxy-2375
+sha
+a73445a3630084c867d63868d9d064625a84ea6d
+repodata/patch-privoxy-2375.xml
+kdeaddons3-konqueror-2383
+sha
+792bad7440845613b608c771c2d948607e02759d
+repodata/patch-kdeaddons3-konqueror-2383.xml
+krusader-2386
+sha
+de603b359e148a7666b5b695b76602c4689ff193
+repodata/patch-krusader-2386.xml
+gdm-2387
+sha
+6dff2c1367613b7eae36db090233bfbf31a92920
+repodata/patch-gdm-2387.xml
+gpg-2388
+sha
+3266d24fa2fb7aeb4ba303ccd77fbe8b27f75ad0
+repodata/patch-gpg-2388.xml
+clamav-2391
+sha
+394bec89d3f5f976a9f7ae074a0e4c0d5582139a
+repodata/patch-clamav-2391.xml
+mono-core-2392
+sha
+6630055daac876bcb0a6ebf36f63d28341b1ff6e
+repodata/patch-mono-core-2392.xml
+sysstat-2401
+sha
+d3de77e29165bdbce88310a6ce9f97f3a5368377
+repodata/patch-sysstat-2401.xml
+xorg-x11-server-2403
+sha
+905a98399c20efb91592d2767ec187823c7b154d
+repodata/patch-xorg-x11-server-2403.xml
+dazuko-2404
+sha
+5314e6c2d4e21a38dd2ea05ee0495d7dd27f0931
+repodata/patch-dazuko-2404.xml
+cups-2406
+sha
+329b6083dfe59ddb7d50cb9e92686eb7ecc48d6d
+repodata/patch-cups-2406.xml
+squirrelmail-2409
+sha
+b5ec61bcf901f4270fc3ba6a18926c20102c94c9
+repodata/patch-squirrelmail-2409.xml
+wxGTK-2411
+sha
+c38d7746f8aca8d40347879bfba5d977ca8fc03f
+repodata/patch-wxGTK-2411.xml
+ImageMagick-2413
+sha
+c1dca99b03a2b012c141a64c8fcc494d957baed2
+repodata/patch-ImageMagick-2413.xml
+squirrelmail-2417
+sha
+f239f615760d15515bd4f0316cc99e40d25a6117
+repodata/patch-squirrelmail-2417.xml
+MozillaFirefox-2418
+sha
+f30517b5083e9bc007adf9cc974a36fc7bba3f0b
+repodata/patch-MozillaFirefox-2418.xml
+liboil-2419
+sha
+24769a5bbd9e57cd59806b186088d5f0dffa5e30
+repodata/patch-liboil-2419.xml
+MozillaThunderbird-2421
+sha
+6c2aa38c513f7eeda2b38555182b8200c08f6468
+repodata/patch-MozillaThunderbird-2421.xml
+pm-utils-2422
+sha
+c0b54062cb5b16cbb18d383beb721c60b2a36f72
+repodata/patch-pm-utils-2422.xml
+sysvinit-2424
+sha
+640a5b7ca790a3becc9c26d8434adfac560a14c5
+repodata/patch-sysvinit-2424.xml
+java-1_4_2-sun-2425
+sha
+94c690e13540511db05ee397e9ab5fc9905b086b
+repodata/patch-java-1_4_2-sun-2425.xml
+java-1_5_0-sun-2427
+sha
+fb1786ca30124e82b24e57b694a58da9f920e6b3
+repodata/patch-java-1_5_0-sun-2427.xml
+cups-2430
+sha
+bb6aea44ad2e2ddb0f50842b15f791b7c0a0b346
+repodata/patch-cups-2430.xml
+w3m-2433
+sha
+d9ba43f21238b71a7b8cfb6d9256e404cffb7cc0
+repodata/patch-w3m-2433.xml
+unison-2436
+sha
+42e904686825e904752238b85085771ee51965c7
+repodata/patch-unison-2436.xml
+lvm2-2438
+sha
+cd88e95ae02706069fe6f3031178bf26e9e06d3a
+repodata/patch-lvm2-2438.xml
+yast2-sudo-2441
+sha
+ab7741ef2411c11a7ec941fc8736feebfca73398
+repodata/patch-yast2-sudo-2441.xml
+krb5-2442
+sha
+2f8c1cd5d147c89b06c0898d4392f1196098236f
+repodata/patch-krb5-2442.xml
+xorg-x11-server-2444
+sha
+a26f9f36ed0a0f6a45cb5369234ff44d6c535310
+repodata/patch-xorg-x11-server-2444.xml
+sax2-2445
+sha
+61a24339218f1fd480085fe0e3f754baa8820cb3
+repodata/patch-sax2-2445.xml
+python-2446
+sha
+c37bf9a8ff3810e2c9a674bd9aadfed24f8c2ab2
+repodata/patch-python-2446.xml
+cacti-2447
+sha
+2cdfac93ac338ccf87abe8049f13bb415428a279
+repodata/patch-cacti-2447.xml
+kdelibs3-2448
+sha
+9adf70721a91e7666c40c45c1c2270bd673d2ac4
+repodata/patch-kdelibs3-2448.xml
+kdeutils3-extra-2450
+sha
+d0a21646dc9d1c244e7e35888a0fb52aaeb1b5e2
+repodata/patch-kdeutils3-extra-2450.xml
+jarnal-2451
+sha
+8daf1728afb47779fd28e0021ea6ff55f18f1149
+repodata/patch-jarnal-2451.xml
+kdelibs3-2452
+sha
+a515c1a96ac1b55f50852cfda93a49004ce4c79a
+repodata/patch-kdelibs3-2452.xml
+xorg-x11-server-2453
+sha
+a3f5b8927b7f1f47f347e7ee3ab821995dca00b7
+repodata/patch-xorg-x11-server-2453.xml
+cyrus-imapd-2454
+sha
+4fdf8c67eaa488edbe668ead3d995caa38a1936b
+repodata/patch-cyrus-imapd-2454.xml
+opera-2456
+sha
+5209e7b0a7f89ec7c225de065d4cbc4fbefcbddb
+repodata/patch-opera-2456.xml
+mediawiki-2457
+sha
+669837ce13f07df59ac79ce6201c176342ed27c4
+repodata/patch-mediawiki-2457.xml
+compiz-2458
+sha
+a41806bf5d57c36825ad832be3d0f13d1695d70a
+repodata/patch-compiz-2458.xml
+libzypp-2460
+sha
+bdbbba1841bebd9822458f3a3fb1c88141e78281
+repodata/patch-libzypp-2460.xml
+bzip2-2465
+sha
+1b84ea4aa922b21a7039330c0fdbf2775dd13444
+repodata/patch-bzip2-2465.xml
+autoyast2-2466
+sha
+c7266496955cee3242ae100ffd731ce4323d6b89
+repodata/patch-autoyast2-2466.xml
+squid-2467
+sha
+883f737c6ecedd05670bad4784a37e2c67a8c5da
+repodata/patch-squid-2467.xml
+totem-2468
+sha
+538c771158fa860793ed285990641b1ed04330b5
+repodata/patch-totem-2468.xml
+java-1_4_2-sun-demo-2469
+sha
+32ae23cb16bff2f8e6ec4ba168e6a75d541efc8c
+repodata/patch-java-1_4_2-sun-demo-2469.xml
+ulogd-2470
+sha
+cea742686fabc65af02a9dd7bea89b39cbdb62c1
+repodata/patch-ulogd-2470.xml
+xpdf-tools-2472
+sha
+8f021c3fec51df6155c44276b87260bbf074c0a2
+repodata/patch-xpdf-tools-2472.xml
+xpdf-2473
+sha
+73e3b82edf6537e759d3c7fe103654baad9155cf
+repodata/patch-xpdf-2473.xml
+neon-2476
+sha
+9b83d8d408138a521c7926be0c569047eb5d866a
+repodata/patch-neon-2476.xml
+libzypp-2477
+sha
+837cb40fa331f256281577e8ca81fe73b9c27e2a
+repodata/patch-libzypp-2477.xml
+gtk2-2479
+sha
+050a31137f1ff1ca986c27addaf894deaa10b6ad
+repodata/patch-gtk2-2479.xml
+nss_ldap-2480
+sha
+cd51f150dda049a82acca2ef4b0524465947ddc2
+repodata/patch-nss_ldap-2480.xml
+cups-2481
+sha
+ec3095ea0c8b996b49061d79f7afb780ab4ea84b
+repodata/patch-cups-2481.xml
+bluez-utils-2482
+sha
+c53aeb36ef68f4f1b6e6ac0f38df63c22f22359d
+repodata/patch-bluez-utils-2482.xml
+xine-lib-2487
+sha
+31c74fc5d4ba42bf3301fa95b806caa978659127
+repodata/patch-xine-lib-2487.xml
+kdegraphics3-2489
+sha
+7cbcd6c1064568aeb0e523d2587336e742e44fcb
+repodata/patch-kdegraphics3-2489.xml
+xorg-x11-Xvnc-2491
+sha
+4f331f6ea3e5c5a90f66a871162114ca94f92f76
+repodata/patch-xorg-x11-Xvnc-2491.xml
+bluez-utils-2492
+sha
+b5a4c65f2356613b4de6ad1bc2dd0582e77b0ee6
+repodata/patch-bluez-utils-2492.xml
+koffice-2495
+sha
+18e4e361c7688a57f7b7963c359fee4f58d29b08
+repodata/patch-koffice-2495.xml
+gtk2-2499
+sha
+f812f28e10039b5bfd249e3902b905e92a7eb769
+repodata/patch-gtk2-2499.xml
+hal-2500
+sha
+90c4516d8073322a05f4235dbe1f7c4770c6e4e0
+repodata/patch-hal-2500.xml
+libsoup-2503
+sha
+7d9f7a7fdd96843f5d962aef984cc0c5c2534f30
+repodata/patch-libsoup-2503.xml
+squid-2504
+sha
+fffd63b332907c46b926499e27b6ec0ad9025ddb
+repodata/patch-squid-2504.xml
+acroread-2506
+sha
+b0bbd764da5ec2651bf030e7051d045341ddbdd7
+repodata/patch-acroread-2506.xml
+flash-player-2509
+sha
+5e2c95f36ceb9c7d94c4a2df8ec60aaf36dde810
+repodata/patch-flash-player-2509.xml
+compat-g77-2510
+sha
+11a15f477e35a5e4879a8883074c13e6ab321141
+repodata/patch-compat-g77-2510.xml
+libgtop-2512
+sha
+c71c5d298bf5b11fcc0c0865b1302a7ba9970c0c
+repodata/patch-libgtop-2512.xml
+zypper-2513
+sha
+fb916bb899aa6d2b1ae8d211ee84ceb0a66f1fd8
+repodata/patch-zypper-2513.xml
+smb4k-2514
+sha
+558c89fe1d04efa2d7c5988be9344376f2eacace
+repodata/patch-smb4k-2514.xml
+amarok-2516
+sha
+18473edb5c4b6853aea94db3883bcfe496b5b6ee
+repodata/patch-amarok-2516.xml
+yast2-trans-de-2518
+sha
+4c3d0446ffd36ce86b40da43135913a18349ef44
+repodata/patch-yast2-trans-de-2518.xml
+compiz-2519
+sha
+188b82cfb54dd61f0fe0e5beea054d5a9591e0a5
+repodata/patch-compiz-2519.xml
+fetchmail-2520
+sha
+85a08c9bc4eebac24854a9eb7da257a3fe8f5b6d
+repodata/patch-fetchmail-2520.xml
+spamassassin-2523
+sha
+e711370f446cf3538fbaf64c9faaf0eadaedb580
+repodata/patch-spamassassin-2523.xml
+libzypp-2524
+sha
+16921f0179c7c3dcb504b9ffb82abd32e84086ac
+repodata/patch-libzypp-2524.xml
+yast2-printer-2525
+sha
+147a7b5f084395bd427c273ba8f25f312cc87193
+repodata/patch-yast2-printer-2525.xml
+powersave-2526
+sha
+afd07ae38e0585e615022a7df3cd469c2c99fdbb
+repodata/patch-powersave-2526.xml
+cups-2527
+sha
+5b9d9747bffc3368ea2f7c8c7b3ff8e0cc4605f7
+repodata/patch-cups-2527.xml
+bind-2529
+sha
+1b1c1fdb9ad132cd6504c8b437cfe519bc9c0dfa
+repodata/patch-bind-2529.xml
+libzypp-2533
+sha
+2af5b888f4f4c9b9be8b9bc2aaae02efd436b924
+repodata/patch-libzypp-2533.xml
+chmlib-2536
+sha
+664ef6d8c867824f20b15a9af4d2957fd79897bd
+repodata/patch-chmlib-2536.xml
+kchmviewer-2539
+sha
+ad29afc8f5dd7ca1f4faa4a2aaed644c80d844b6
+repodata/patch-kchmviewer-2539.xml
+rrdtool-2540
+sha
+8cc33adb78b9fccccc7f2fc03d5df13f8a79eae5
+repodata/patch-rrdtool-2540.xml
+yast2-sound-2541
+sha
+ccece044c2ba608c8233d70a4aa68c466c1862f1
+repodata/patch-yast2-sound-2541.xml
+fetchmail-2542
+sha
+339f1a841955652ccaccd9c9e936c21abe85a2f2
+repodata/patch-fetchmail-2542.xml
+kdenetwork3-InstantMessenger-2547
+sha
+3ef0e4f08055c2f458c0efa5dfdd4d7aee384122
+repodata/patch-kdenetwork3-InstantMessenger-2547.xml
+openssl-2548
+sha
+67602c57de3f98ffd2c7434b6952f79481feceb8
+repodata/patch-openssl-2548.xml
+autofs-2549
+sha
+4da7e1958d56142f48d99b9ab1b16425aeb0cd96
+repodata/patch-autofs-2549.xml
+qemu-2550
+sha
+940c331de3e40be9f570e012b8ca9bbab6d594d2
+repodata/patch-qemu-2550.xml
+cross-avr-binutils-2551
+sha
+da26f814db1c2f04cd8fc27495aecdbd66f73daf
+repodata/patch-cross-avr-binutils-2551.xml
+digikam-2552
+sha
+299f0dff3447555cea60a63fdf97e81e1bd56096
+repodata/patch-digikam-2552.xml
+samba-2555
+sha
+a7a501a1465dfcb3e125d14c1a67efd2638be166
+repodata/patch-samba-2555.xml
+fetchmail-2563
+sha
+10b87459f9209a43097bebd31f9ec8cff4354c11
+repodata/patch-fetchmail-2563.xml
+kdegraphics3-pdf-2565
+sha
+8d0d0b963c5fa85c92d5a9b5c86c88063b2faf8e
+repodata/patch-kdegraphics3-pdf-2565.xml
+koffice-wordprocessing-2577
+sha
+5f43e01206494f0d0cbab846bfbb157285e22efc
+repodata/patch-koffice-wordprocessing-2577.xml
+samba-2584
+sha
+5e86626239c4798708e7d2432708cf98d6a001d2
+repodata/patch-samba-2584.xml
+ImageMagick-2585
+sha
+5af58df93d8f36428726d0cf55fc5bb6a18a8bfd
+repodata/patch-ImageMagick-2585.xml
+poppler-2590
+sha
+924e7f989e3d6f159e717e2d76b21de572ce7ec2
+repodata/patch-poppler-2590.xml
+NetworkManager-kde-2591
+sha
+93a54883e5b9170839fc81a72c534f76e7987c19
+repodata/patch-NetworkManager-kde-2591.xml
+GraphicsMagick-2593
+sha
+64a57f29d835e46f7f00e939d4b505cb546d0654
+repodata/patch-GraphicsMagick-2593.xml
+chmlib-2595
+sha
+88ee8256ac3875e2b087ab9723bb13f071f9de73
+repodata/patch-chmlib-2595.xml
+gpdf-2596
+sha
+115f31bcb61f37f264daa4b0ef8220816be05de7
+repodata/patch-gpdf-2596.xml
+kdenetwork3-InstantMessenger-2599
+sha
+c5a5fd01f5266d3f30fe33b5fc1679b35af3d4d4
+repodata/patch-kdenetwork3-InstantMessenger-2599.xml
+kdebase3-2600
+sha
+b6654a37408c21d66e60cf7a78a1cddb991b797a
+repodata/patch-kdebase3-2600.xml
+pam-2601
+sha
+fd76c123487f884cf10b161c87afe5b7bc9f05f8
+repodata/patch-pam-2601.xml
+fetchmail-2602
+sha
+d3533c47354977c9f821eb4e579c43ea919d4488
+repodata/patch-fetchmail-2602.xml
+novfs-kmp-bigsmp-2630
+sha
+06f48c215e805213e339fa581670fb35e2151786
+repodata/patch-novfs-kmp-bigsmp-2630.xml
+clamav-2632
+sha
+b5217b10a1a3e513085665d994dded954a72e884
+repodata/patch-clamav-2632.xml
+timezone-2634
+sha
+e2d31e95095a1c7dd242c577f3a394a39d7e75be
+repodata/patch-timezone-2634.xml
+gwenview-2637
+sha
+b6f7e30d0d8f3e699e8593a178b5fd63c865ad0e
+repodata/patch-gwenview-2637.xml
+wireshark-2638
+sha
+7ebc8745836d63bdfee712bca5c71ba328877047
+repodata/patch-wireshark-2638.xml
+hal-resmgr-2639
+sha
+4eb206f1dba689554bc3e113fd87ad2a4fcdbf78
+repodata/patch-hal-resmgr-2639.xml
+klamav-2640
+sha
+505cc81dc924efb58154806700b1d261cc33fbb9
+repodata/patch-klamav-2640.xml
+ekiga-2641
+sha
+3d960c76d7dd38393c631c63212f0a8c4f08ad98
+repodata/patch-ekiga-2641.xml
+libwpd-2642
+sha
+b31ced09fece6d2f9fad37653acfda9eb9e7ba2b
+repodata/patch-libwpd-2642.xml
+rubygems-2644
+sha
+26a5b6e1a3c77afa7fd77171bcbef2d78f7df11f
+repodata/patch-rubygems-2644.xml
+MozillaFirefox-2647
+sha
+420d992e8b4436cc7ebc0609183ba040db340dc5
+repodata/patch-MozillaFirefox-2647.xml
+koffice-wordprocessing-2648
+sha
+bf0b411e4eb5494a27ebef8d5b9cd923da4fe2c8
+repodata/patch-koffice-wordprocessing-2648.xml
+agfa-fonts-2650
+sha
+19126ca4e55852b98ffa69c6c3fcff12743af49c
+repodata/patch-agfa-fonts-2650.xml
+OpenOffice_org-2652
+sha
+0bbcdeab9294272a677f9b4a7b57e13701244010
+repodata/patch-OpenOffice_org-2652.xml
+gdm-2653
+sha
+df03ccdfcb0f188ac3072c5086ab86b425a66f8e
+repodata/patch-gdm-2653.xml
+ruby-2655
+sha
+8ffefa033ac19a70938a7af71854184d6b902e45
+repodata/patch-ruby-2655.xml
+sylpheed-claws-2685
+sha
+df121b6b7cc82715a4544f491a0104b50a28eb20
+repodata/patch-sylpheed-claws-2685.xml
+php5-2687
+sha
+f34eb5a2f4399578c78e6dca5dd165077787c806
+repodata/patch-php5-2687.xml
+klamav-2688
+sha
+da442e32fe4401897261051bf817e1a029e804c3
+repodata/patch-klamav-2688.xml
+clamav-2690
+sha
+ff002a87486fbd2c68b445e58b6ac0d307e8dbad
+repodata/patch-clamav-2690.xml
+seamonkey-2691
+sha
+fd8198e09f073515c67cf90a879ff57bfde0cfa9
+repodata/patch-seamonkey-2691.xml
+gnokii-2689
+sha
+2453275e874a7d65c9f21cc50885f50202d617bf
+repodata/patch-gnokii-2689.xml
+doxygen-2694
+sha
+fad24b42e3d7d8c35e410017ca85dd375f2b7f1d
+repodata/patch-doxygen-2694.xml
+enlightenment-2695
+sha
+e74c7b27498161c9ba056869ba1813658f4ef4e0
+repodata/patch-enlightenment-2695.xml
+gnome-terminal-2696
+sha
+36f9b9e6ff739fe7d82a58b59fcb74be6581a752
+repodata/patch-gnome-terminal-2696.xml
+kpowersave-2698
+sha
+c1e569e948b46ddcd33369c9479e6ad92fb8c7ec
+repodata/patch-kpowersave-2698.xml
+clamav-2700
+sha
+3dd423deeb41c58b11f54184d59367d152d78f24
+repodata/patch-clamav-2700.xml
+autofs-2703
+sha
+678cae267889c434cbdd81654066244270a341b6
+repodata/patch-autofs-2703.xml
+ivtv-kmp-bigsmp-2704
+sha
+9539aa93b99854e21cc69248bd82249f75a6f6e0
+repodata/patch-ivtv-kmp-bigsmp-2704.xml
+kernel-2705
+sha
+fedf719a3be03d6c93fc04f78b50ce7be1465606
+repodata/patch-kernel-2705.xml
+yast2-printer-2706
+sha
+b95d82b3c1e22c1ce2ec2ea96ffb9ba94e1a2adb
+repodata/patch-yast2-printer-2706.xml
+MozillaThunderbird-2734
+sha
+be5018a378fbce22ad2f1f94266f251dfb0fecc2
+repodata/patch-MozillaThunderbird-2734.xml
+gstreamer010-plugins-base-2737
+sha
+93496c003d8a1565e88fa37bc28b62b685b86223
+repodata/patch-gstreamer010-plugins-base-2737.xml
+gstreamer010-plugins-base-2805
+sha
+40b9fd23e3eb4141d8b28794c40aeebdee7dcc1d
+repodata/patch-gstreamer010-plugins-base-2805.xml
+evolution-2824
+sha
+7d6301a829eda6ff7ab006302c38943693defd44
+repodata/patch-evolution-2824.xml
+avahi-2982
+sha
+c338bfb439e94dc27774a76d860285577b9ade19
+repodata/patch-avahi-2982.xml
+syslog-ng-2984
+sha
+eb0c412124838a4ff2ff884642a88fac1578da8f
+repodata/patch-syslog-ng-2984.xml
+tomcat5-2985
+sha
+95ce884347ae5fb4f8b030f9e302f75dec58cefd
+repodata/patch-tomcat5-2985.xml
+xine-lib-2989
+sha
+91dba1fffdd033eb289fec17dd2a288658dbb586
+repodata/patch-xine-lib-2989.xml
+xorg-x11-libs-3070
+sha
+069398c5d7b94dc32c6102243b478584a76b84bb
+repodata/patch-xorg-x11-libs-3070.xml
+rekall-2991
+sha
+2c534ad1e2a36b668f7d22f37b5e0457f5892359
+repodata/patch-rekall-2991.xml
+gpg-2995
+sha
+bce7d390d3db0bdedf63304a9b739645e7c10eb8
+repodata/patch-gpg-2995.xml
+gstreamer010-plugins-base-2992
+sha
+5d0fa6b2d1a729ed86d6bdc54bb3eeabb1bfea38
+repodata/patch-gstreamer010-plugins-base-2992.xml
+unrar-2996
+sha
+c1e7abcd4dc9ffda6f97dc8c98e68d36f0fa51e5
+repodata/patch-unrar-2996.xml
+ktorrent-2998
+sha
+ff9e274c83ef488d9eed486c7eae9f2952d4c29e
+repodata/patch-ktorrent-2998.xml
+ekiga-3023
+sha
+2dd405d9651c0f9b87412b07488d4b03796390e9
+repodata/patch-ekiga-3023.xml
+krb5-apps-servers-3021
+sha
+f4b8a55a8b737a513c4531487888ff231126cce9
+repodata/patch-krb5-apps-servers-3021.xml
+pam_ssh-3024
+sha
+5413228a54cf99ce20ad08e6917605f7a9ceeabe
+repodata/patch-pam_ssh-3024.xml
+TeXmacs-3030
+sha
+a17b3f3406caf33fb9160d2464b0945bc2b0a8ad
+repodata/patch-TeXmacs-3030.xml
+perl-Bootloader-3029
+sha
+2ec2f7e3abf84e60ce2a2e3935e3373242f97d27
+repodata/patch-perl-Bootloader-3029.xml
+kernel-3032
+sha
+b83c95c8f2337d42ea1b8b1e606ca83c8acc17b8
+repodata/patch-kernel-3032.xml
+NetworkManager-3031
+sha
+89d9e49eb0c49803ab2bd5d51570f8e688f93196
+repodata/patch-NetworkManager-3031.xml
+file-3033
+sha
+9501fa94de1e6e9a896cc2e29d66424e17d429e4
+repodata/patch-file-3033.xml
+squid-3036
+sha
+882e3dad4edfc0e2e923d4fc49c78341acea368d
+repodata/patch-squid-3036.xml
+libwpd-3038
+sha
+03671597239a76a8716da20528d4a71d24e44825
+repodata/patch-libwpd-3038.xml
+opensuse-updater-3037
+sha
+f6a3e069f70f9250d7ae0c5f7497999714552ddc
+repodata/patch-opensuse-updater-3037.xml
+krb5-3045
+sha
+a7a9803370986ec4de2913c416db11a0c207d8a1
+repodata/patch-krb5-3045.xml
+qt3-3048
+sha
+4b3f9bbb0f413a4b70392219f501ad782e5c647a
+repodata/patch-qt3-3048.xml
+gwenview-3055
+sha
+7f65de9106151c59e1b91eab1c195e5765445894
+repodata/patch-gwenview-3055.xml
+libqt4-3056
+sha
+1c801d3a719e843a50f43faceff0bd2bf77f3e82
+repodata/patch-libqt4-3056.xml
+ktorrent-3057
+sha
+06cd8e287e5cc94bf936726e9a8540283ad42af5
+repodata/patch-ktorrent-3057.xml
+kdelibs3-3058
+sha
+555d3279e2485cae5feb3a7c57b0ee0f16ae8ba6
+repodata/patch-kdelibs3-3058.xml
+inkscape-3062
+sha
+cd767b5b8690e08ec5b1f231641cd6a4a7d1dec4
+repodata/patch-inkscape-3062.xml
+perl-Bootloader-3059
+sha
+24e57eaa5bc4c6080577f7555b9b28ba30c298e6
+repodata/patch-perl-Bootloader-3059.xml
+scpm-3064
+sha
+262095e2c3ad96a3c892c0e06baad6c995ac52b9
+repodata/patch-scpm-3064.xml
+mediawiki-3065
+sha
+beba36db67a885e7a43a2b8d0f5b6a1e394fad39
+repodata/patch-mediawiki-3065.xml
+freetype2-3066
+sha
+079e72f31dcd4e20a786501b6670d581480cb249
+repodata/patch-freetype2-3066.xml
+xorg-x11-libX11-3069
+sha
+b7bf854b78d3ba9488e4a9c46bf48d6e7501ead5
+repodata/patch-xorg-x11-libX11-3069.xml
+xorg-x11-server-3071
+sha
+8dd539e1cb8196a3bde56ec0e5d9717bf74d3696
+repodata/patch-xorg-x11-server-3071.xml
+xmms-3073
+sha
+f96cebcb74f4e9067976240f8da5dff3dbc954e3
+repodata/patch-xmms-3073.xml
+spamassassin-3077
+sha
+5d11f1e0772a8eae3d0276c2fc0c36fd024ff969
+repodata/patch-spamassassin-3077.xml
diff --git a/tests/parser/yum/data/patches.xsl b/tests/parser/yum/data/patches.xsl
new file mode 100644 (file)
index 0000000..10b4cc2
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Edited with XML Spy v2007 (http://www.altova.com) -->
+<xsl:stylesheet xmlns:suse="http://novell.com/package/metadata/suse/patches" version="1.0"
+xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:template match="/">  
+  <xsl:for-each select="patches/patch">
+    <xsl:value-of select="@id"/>    <br/>
+    <xsl:value-of select="checksum/@type"/><br/>
+    <xsl:value-of select="checksum"/><br/>
+    <xsl:value-of select="location/@href"/><br/>
+  </xsl:for-each>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/tests/parser/yum/data/repomd-1.xml b/tests/parser/yum/data/repomd-1.xml
new file mode 100644 (file)
index 0000000..26b7231
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="patches">
+    <location href="repodata/patches.xml"/>
+    <checksum type="sha">63d86413540fbb2647e5b5de556d4e4b53f032d9</checksum>
+    <timestamp>1176225512</timestamp>
+    <open-checksum type="sha">63d86413540fbb2647e5b5de556d4e4b53f032d9</open-checksum>
+  </data>
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">f76cb1f9f1963edc614848944b99e2664eba4b4a</checksum>
+    <timestamp>1176225558</timestamp>
+    <open-checksum type="sha">f9cf4f4520ebe75e4bd66c1572521b414dfea564</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">fd1ea55f143820333b702cda4f97b286daf65171</checksum>
+    <timestamp>1176225549</timestamp>
+    <open-checksum type="sha">3bfb8413a4698b86f632bb914f6dc8a4e3ac9660</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">689fecabbb0907f51f5bb7211048e091c6f2bc84</checksum>
+    <timestamp>1176225550</timestamp>
+    <open-checksum type="sha">ce38366eaded03cb8b3fdc64bb31ea5304e9901c</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/parser/yum/data/repomd-1.xml.solution b/tests/parser/yum/data/repomd-1.xml.solution
new file mode 100644 (file)
index 0000000..fbd5911
--- /dev/null
@@ -0,0 +1,17 @@
+patches
+sha1
+63d86413540fbb2647e5b5de556d4e4b53f032d9
+repodata/patches.xml
+other
+sha1
+f76cb1f9f1963edc614848944b99e2664eba4b4a
+repodata/other.xml.gz
+primary
+sha1
+fd1ea55f143820333b702cda4f97b286daf65171
+repodata/primary.xml.gz
+filelists
+sha1
+689fecabbb0907f51f5bb7211048e091c6f2bc84
+repodata/filelists.xml.gz
+
diff --git a/tests/repo/CMakeLists.txt b/tests/repo/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3f06bc0
--- /dev/null
@@ -0,0 +1,7 @@
+ADD_SUBDIRECTORY( yum )
+ADD_SUBDIRECTORY( susetags )
+
+# to find the KeyRingTest receiver
+INCLUDE_DIRECTORIES( ${LIBZYPP_SOURCE_DIR}/tests/zypp )
+
+ADD_TESTS(RepoVariables ExtendedMetadata PluginServices MirrorList)
diff --git a/tests/repo/ExtendedMetadata_test.cc b/tests/repo/ExtendedMetadata_test.cc
new file mode 100644 (file)
index 0000000..6160dd5
--- /dev/null
@@ -0,0 +1,144 @@
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Package.h"
+#include "zypp/RepoManager.h"
+#include "zypp/sat/Pool.h"
+#include "KeyRingTestReceiver.h"
+
+#include "TestSetup.h"
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+using namespace zypp::filesystem;
+
+#define TEST_DIR TESTS_SRC_DIR "/repo/yum/data/extensions"
+
+BOOST_AUTO_TEST_CASE(extended_metadata)
+{
+  KeyRingTestReceiver rec;
+  //rec.answerAcceptUnknownKey(true);
+  rec.answerAcceptUnsignedFile(true);
+  
+
+//  rec.answerImportKey(true);
+  Pathname repodir(TEST_DIR );
+
+  sat::Pool pool(sat::Pool::instance());
+
+  TestSetup test( Arch_x86_64 );
+  test.loadRepo(Url(string("dir:") + repodir.absolutename().asString()), "updates");
+
+  Repository repo = pool.reposFind("updates");
+  
+  BOOST_CHECK_EQUAL( repo.generatedTimestamp(), Date(1227279057) );
+  BOOST_CHECK_EQUAL( repo.suggestedExpirationTimestamp(), Date(1227279057 + 3600) );  
+
+  // check that the attributes of product compatibility are ok
+  int count = 0;
+  vector<string> cpeids;
+  vector<string> labels;
+
+  for_( it,
+        repo.compatibleWithProductBegin(),
+        repo.compatibleWithProductEnd() )
+  {
+      cpeids.push_back(it.cpeId());
+      labels.push_back(it.label());
+      count++;
+  }
+
+  // there were 2 compatible products
+  BOOST_CHECK_EQUAL( count, 2 );
+  BOOST_CHECK_EQUAL( cpeids[0], "cpe://o:opensuse" );
+  BOOST_CHECK_EQUAL( cpeids[1], "cpe://o:sle" );
+
+  BOOST_CHECK_EQUAL( labels[0], "openSUSE 11.0" );
+  BOOST_CHECK_EQUAL( labels[1], "SLE 11.0" );
+
+  cpeids.clear();
+  labels.clear();
+  count = 0;
+  
+  for_( it,
+        repo.updatesProductBegin(),
+        repo.updatesProductEnd() )
+  {
+      cpeids.push_back(it.cpeId());
+      labels.push_back(it.label());
+      count++;
+  }
+
+  // the repo updates one product
+  BOOST_CHECK_EQUAL( count, 1 );
+  BOOST_CHECK_EQUAL( cpeids[0], "cpe://o:sle" );
+  BOOST_CHECK_EQUAL( labels[0], "SLE 11.0" );
+
+  // because this product updates something, it _is_ an update repo
+  BOOST_CHECK( repo.isUpdateRepo() );
+
+  BOOST_CHECK( repo.providesUpdatesFor("cpe://o:sle") );
+  BOOST_CHECK( ! repo.providesUpdatesFor("cpe://o:windows") );
+
+  // reuse to count solvables
+  count = 0;
+  
+  /**
+   * Now check for the extended metadata of the packages
+   */
+  for_( it, repo.solvablesBegin(), repo.solvablesEnd() )
+  {
+      sat::Solvable s = *it;
+      MIL << s << endl;
+      MIL << s.kind() << endl;
+      if ( s.ident() == "wt" )
+      {
+          count++;
+          Package::Ptr p = asKind<Package>(makeResObject(s));
+          BOOST_CHECK(p);
+          BOOST_CHECK(p->maybeUnsupported() );
+          BOOST_CHECK_EQUAL(p->vendorSupport(), VendorSupportUnknown );
+          
+      }
+      else if ( s.ident() == "foobar" )
+      {
+            count++;            
+            Package::Ptr p = asKind<Package>(makeResObject(s));
+            BOOST_CHECK(p);
+            BOOST_CHECK_EQUAL(p->vendorSupport(), VendorSupportUnsupported ); 
+            BOOST_CHECK(p->maybeUnsupported() );
+      }
+      else if ( s.ident() == "foofoo" )
+      {
+          count++;
+          Package::Ptr p = asKind<Package>(makeResObject(s));
+          BOOST_CHECK(p);
+          // if it is level 3 support it cant be unsupported
+          BOOST_CHECK_EQUAL(p->vendorSupport(), VendorSupportLevel3 );
+          BOOST_CHECK(! p->maybeUnsupported() );
+
+      }
+      else
+      {
+          BOOST_FAIL(str::form("Repo has package not contemplated in test: %s", s.ident().c_str()).c_str());
+      }
+      
+    }
+
+    // check that we actually found all testeable
+    // resolvables
+    BOOST_CHECK_EQUAL(count, 3);
+
+}
diff --git a/tests/repo/MirrorList_test.cc b/tests/repo/MirrorList_test.cc
new file mode 100644 (file)
index 0000000..40c38a6
--- /dev/null
@@ -0,0 +1,34 @@
+#include <iostream>
+#include <vector>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "WebServer.h"
+
+#include "zypp/repo/RepoMirrorList.cc"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+
+BOOST_AUTO_TEST_CASE(get_mirrorlist)
+{
+  WebServer web((Pathname(TESTS_SRC_DIR) + "/data/Mirrorlist/remote-site").c_str(), 10001);
+  web.start();
+
+  Url weburl1 (web.url());
+  Url weburl2 (web.url());
+
+  weburl1.setPathName("/metalink.xml");
+  weburl2.setPathName("/mirrors.txt");
+
+  RepoMirrorList rml1 (weburl1);
+  RepoMirrorList rml2 (weburl2);
+
+  BOOST_CHECK(rml1.getUrls().begin()->asString() == "http://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/");
+  BOOST_CHECK(rml2.getUrls().begin()->asString() == "http://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/");
+
+  BOOST_CHECK(rml1.getUrls().size() == 4);
+  BOOST_CHECK(rml2.getUrls().size() == 4);
+
+  web.stop();
+}
diff --git a/tests/repo/PluginServices_test.cc b/tests/repo/PluginServices_test.cc
new file mode 100644 (file)
index 0000000..d999c7c
--- /dev/null
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ZConfig.h"
+#include "zypp/repo/PluginServices.h"
+#include "zypp/ServiceInfo.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+using namespace zypp::repo;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) +  "/repo/yum/data")
+
+class ServiceCollector
+{
+public:
+  typedef std::set<ServiceInfo> ServiceSet;
+    
+  ServiceCollector( ServiceSet & services_r )
+    : _services( services_r )
+  {}
+
+  bool operator()( const ServiceInfo & service_r ) const
+  {
+    _services.insert( service_r );
+    return true;
+  }
+
+private:
+  ServiceSet & _services;
+};
+
+
+BOOST_AUTO_TEST_CASE(plugin_services)
+{
+  ServiceCollector::ServiceSet services;
+    
+  PluginServices local("/space/tmp/services", ServiceCollector(services));
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/repo/RepoVariables_test.cc b/tests/repo/RepoVariables_test.cc
new file mode 100644 (file)
index 0000000..18fd302
--- /dev/null
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/ZConfig.h"
+#include "zypp/repo/RepoVariables.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+using namespace zypp::repo;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) +  "/repo/yum/data")
+
+BOOST_AUTO_TEST_CASE(replace_text)
+{
+  /* check RepoVariablesStringReplacer */
+
+  RepoVariablesStringReplacer replacer1;
+
+  BOOST_CHECK_EQUAL(replacer1("http://foo/$arch/bar"),
+                    "http://foo/"+ ZConfig::instance().systemArchitecture().asString() + "/bar");
+
+  replacer1.resetVarCache();
+
+  ZConfig::instance().setSystemArchitecture(Arch("i686"));
+  BOOST_CHECK_EQUAL(replacer1("http://foo/$arch/bar/$basearch"),
+                    "http://foo/i686/bar/i386");
+
+  /* check RepoVariablesUrlReplacer */
+
+  RepoVariablesUrlReplacer replacer2;
+
+  BOOST_CHECK_EQUAL(replacer2(Url("ftp://user:secret@site.org/$arch/")).asCompleteString(),
+                   "ftp://user:secret@site.org/i686/");
+
+  BOOST_CHECK_EQUAL(replacer2(Url("http://user:my$arch@site.org/$basearch/")).asCompleteString(),
+                   "http://user:my$arch@site.org/i386/");
+
+  BOOST_CHECK_EQUAL(replacer2(Url("http://site.org/update/?arch=$arch")).asCompleteString(),
+                   "http://site.org/update/?arch=i686");
+
+  // no target activated yet, there should be no replacement of
+  // $distver
+  BOOST_CHECK_EQUAL(replacer2(Url("http://site.org/update/$distver/?arch=$arch")).asCompleteString(),
+                   "http://site.org/update/$distver/?arch=i686");
+    
+  // now we initialize the target
+  filesystem::TmpDir tmp;
+    
+  ZYpp::Ptr z = getZYpp();
+
+  // create the products.d directory
+  assert_dir(tmp.path() / "/etc/products.d" );
+  BOOST_CHECK( copy( Pathname(TESTS_SRC_DIR) / "/zypp/data/Target/product.prod",  tmp.path() / "/etc/products.d/product.prod") == 0 );
+  // make it the base product
+  BOOST_CHECK( symlink(tmp.path() / "/etc/products.d/product.prod", tmp.path() / "/etc/products.d/baseproduct" ) == 0 );
+
+  replacer2.resetVarCache();
+
+  z->initializeTarget( tmp.path() );
+  // target activated, there should be replacement of
+  // $distver
+  BOOST_CHECK_EQUAL(replacer2(Url("http://site.org/update/$releasever/?arch=$arch")).asCompleteString(),
+                   "http://site.org/update/10/?arch=i686");
+
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/repo/susetags/CMakeLists.txt b/tests/repo/susetags/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4993c4e
--- /dev/null
@@ -0,0 +1,2 @@
+
+ADD_TESTS(Downloader)
\ No newline at end of file
diff --git a/tests/repo/susetags/Downloader_test.cc b/tests/repo/susetags/Downloader_test.cc
new file mode 100644 (file)
index 0000000..958d2d8
--- /dev/null
@@ -0,0 +1,143 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/repo/susetags/Downloader.h"
+
+#include "tests/zypp/KeyRingTestReceiver.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+using namespace zypp::repo;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/repo/susetags/data")
+
+BOOST_AUTO_TEST_CASE(susetags_download)
+{
+  KeyRingTestReceiver keyring_callbacks;
+  keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+
+  Pathname p = DATADIR + "/stable-x86-subset";
+  Url url("dir:" + p.asString());
+  MediaSetAccess media(url);
+  RepoInfo repoinfo;
+  repoinfo.setAlias("testrepo");
+  repoinfo.setPath("/");
+  susetags::Downloader downloader(repoinfo);
+  filesystem::TmpDir tmp;
+  
+  Pathname localdir(tmp.path());
+  
+  downloader.download(media,localdir);
+  
+  MIL << "All files downloaded" << endl;
+  
+  const char* files[] =
+  {
+    "/suse",
+    "/suse/setup",
+    "/suse/setup/descr",
+    "/suse/setup/descr/kde-10.3-71.i586.pat",
+    "/suse/setup/descr/packages",
+    "/suse/setup/descr/packages.DU",
+    "/suse/setup/descr/packages.en",
+//    "/suse/setup/descr/packages.es",
+    "/suse/setup/descr/patterns",
+    "/content",
+    "/gpg-pubkey-7e2e3b05-44748aba.asc",
+    "/media.1",
+//    "/media.1/products.asc",
+//    "/media.1/products.key",
+    "/media.1/media",
+//    "/media.1/products",
+//    "/media.1/info.txt",
+//    "/media.1/license.zip",
+    "/gpg-pubkey-a1912208-446a0899.asc",
+    "/gpg-pubkey-307e3d54-44201d5d.asc",
+    "/gpg-pubkey-9c800aca-40d8063e.asc",
+    "/content.asc",
+    "/content.key",
+    "/gpg-pubkey-3d25d3d9-36e12d04.asc",
+    "/gpg-pubkey-0dfb3188-41ed929b.asc",
+    NULL
+  };
+  
+  int i=0;
+  while ( files[i] != NULL )
+  {
+    BOOST_CHECK_MESSAGE( PathInfo(localdir + files[i] ).isExist(), string(files[i]).c_str() );
+    i++;
+  }
+
+}
+
+BOOST_AUTO_TEST_CASE(susetags_gz_download)
+{
+  KeyRingTestReceiver keyring_callbacks;
+  keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+
+  Pathname p = DATADIR + "/stable-x86-subset-gz";
+  Url url("dir:" + p.asString());
+  MediaSetAccess media(url);
+  RepoInfo repoinfo;
+  repoinfo.setAlias("testrepo");
+  repoinfo.setPath("/");
+  susetags::Downloader downloader(repoinfo);
+  filesystem::TmpDir tmp;
+  
+  Pathname localdir(tmp.path());
+  
+  downloader.download(media,localdir);
+  
+  const char* files[] =
+  {
+    "/suse",
+    "/suse/setup",
+    "/suse/setup/descr",
+    "/suse/setup/descr/kde-10.3-71.i586.pat.gz",
+    "/suse/setup/descr/packages.gz",
+    "/suse/setup/descr/packages.DU.gz",
+    "/suse/setup/descr/packages.en.gz",
+//    "/suse/setup/descr/packages.es",
+    "/suse/setup/descr/patterns.gz",
+    "/content",
+    "/gpg-pubkey-7e2e3b05-44748aba.asc",
+    "/media.1",
+//    "/media.1/products.asc",
+//    "/media.1/products.key",
+    "/media.1/media",
+//    "/media.1/products",
+//    "/media.1/info.txt",
+//    "/license.tar.gz",
+//    "/control.xml",
+//    "/installation.xml",
+    "/gpg-pubkey-a1912208-446a0899.asc",
+    "/gpg-pubkey-307e3d54-44201d5d.asc",
+    "/gpg-pubkey-9c800aca-40d8063e.asc",
+    "/content.asc",
+    "/content.key",
+    "/gpg-pubkey-3d25d3d9-36e12d04.asc",
+    "/gpg-pubkey-0dfb3188-41ed929b.asc",
+    NULL
+  };
+  
+  int i=0;
+  while ( files[i] != NULL )
+  {
+    BOOST_CHECK_MESSAGE( PathInfo(localdir + files[i] ).isExist(), string(files[i]).c_str() );
+    i++;
+  }
+
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/repo/susetags/data/addon_in_subdir/media.1/build b/tests/repo/susetags/data/addon_in_subdir/media.1/build
new file mode 100644 (file)
index 0000000..3e09a19
--- /dev/null
@@ -0,0 +1 @@
+openSUSE-11.1-Beta5-DVD-i586-Build0128
diff --git a/tests/repo/susetags/data/addon_in_subdir/media.1/directory.yast b/tests/repo/susetags/data/addon_in_subdir/media.1/directory.yast
new file mode 100644 (file)
index 0000000..d1b27df
--- /dev/null
@@ -0,0 +1,6 @@
+build
+info.txt
+media
+products
+products.asc
+products.key
diff --git a/tests/repo/susetags/data/addon_in_subdir/media.1/media b/tests/repo/susetags/data/addon_in_subdir/media.1/media
new file mode 100644 (file)
index 0000000..42af4f5
--- /dev/null
@@ -0,0 +1,3 @@
+openSUSE
+20081112071348
+1
diff --git a/tests/repo/susetags/data/addon_in_subdir/media.1/products b/tests/repo/susetags/data/addon_in_subdir/media.1/products
new file mode 100644 (file)
index 0000000..bf73ab9
--- /dev/null
@@ -0,0 +1 @@
+/ openSUSE 11.1-0
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/SHA1SUMS b/tests/repo/susetags/data/addon_in_subdir/updates/SHA1SUMS
new file mode 100644 (file)
index 0000000..308e7d9
--- /dev/null
@@ -0,0 +1,2 @@
+33bbc20526153c35d11ce0b1c3d6b7be151b92d9  content
+bfc5ac3540529a9a898c3c444096f9bcbb5db780  directory.yast
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/content b/tests/repo/susetags/data/addon_in_subdir/updates/content
new file mode 100644 (file)
index 0000000..cc17cac
--- /dev/null
@@ -0,0 +1,19 @@
+CONTENTSTYLE 11
+BASEARCHS i586
+DATADIR suse
+DESCRDIR suse/setup/descr
+DISTRIBUTION openSUSE
+FLAVOR dvd
+LABEL openSUSE 11.1
+LINGUAS cs da de en en_GB en_US es fi fr hu it ja nb nl pl pt pt_BR ru sv zh zh_CN zh_TW
+NAME openSUSE
+REFERENCES openSUSE-release = 11.1
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/11.0.42/release-notes-openSUSE.rpm
+REPO_LOCATION http://download.opensuse.org/factory/repo/oss/
+SUMMARY openSUSE
+VENDOR openSUSE
+VERSION 11.1
+META SHA1 3d0cdf34257cc2c54929bf5414969142cad1d421  packages
+META SHA1 2d68731ab2f3c86b81f7672798f871cf96b2d0fc  packages.DU
+META SHA1 07c36c760ed0c58b58d6d8fcfdd2717df02c4256  packages.en
+HASH SHA1 875e73cf2ee139203208c860fbfd7fa5cb291c7e  license.tar.gz
\ No newline at end of file
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/content.asc b/tests/repo/susetags/data/addon_in_subdir/updates/content.asc
new file mode 100644 (file)
index 0000000..3df9e35
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkktcUUACgkQm+zCtd2wN1ZrDgCdFYz7dG8ui0ueFLOIHYxFgunL
+VEYAnjzykFqwVAmYhF/QNbfriOqN5z1B
+=4Jf7
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/content.key b/tests/repo/susetags/data/addon_in_subdir/updates/content.key
new file mode 100644 (file)
index 0000000..5c27af8
--- /dev/null
@@ -0,0 +1,439 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBDsj/RYRBACQD/DCxkMgmEjBNYh53AfsV+zcMaz4nDmeEElANfHrVzVGx33N
+Siiqs33RIjV35Gd8OH1iSnbA7ef0gWELgVSToK2ydv/3X5Cbcb1MOWYQKJE1dQz7
+fw7Ic9nP7NieM18YMsOYEmCvyL4sLZviQIlb3caP+OpI/GAoNINY8m9yowCgxgx1
+L+jnJznXyKy7v5WgwMyrE2cD/38Nvp62Rq1/IqhUDc3SDUp5+xPddwOZ/E7P9F73
+0Gb2ec2fhAm9QZyVvFvLa+SJq2/LvY+vITZSRI0HTBZf4Yrzd6eHu/cDp0m0o/BS
+McuoaHmKeHYcyIa2w8LMREpchgdlY/LnHR83Yipc3iegBRUvoTtwUYMqpswwi+6i
+50nhA/9MC5cPOZbPpqbaDbSz0NtAVM2gcvgiBx4VKCh/AhkZ+abzogeHn6uT2eaP
+3Fnk4YOa0FEbO+YHg3Lu45tZV3pBQUZoY07r5niT0Sb6dAKO/j/omEt4q44OO3ba
+fanEvFurtgpkszoD20yheQLhv7CVdS8IUfQ2R+r0eQjxtAfJWLQmRHVuY2FuIE1h
+Yy1WaWNhciBQcmV0dCA8ZHVuY2FuQHB1Yy5jbD6IVwQTEQIAFwULBwoDBAMVAwID
+FgIBAheABQJIkdYDAAoJEM0etqlmfkLR6EgAn2/KapnsJrHLcwD3XxHc3KJzosii
+AJ9K468Njg7GOzlIP1lfHhIRNB8yjohXBBMRAgAXBQI7I/0WBQsHCgMEAxUDAgMW
+AgECF4AACgkQzR62qWZ+QtGplQCcDxIiwPV5CSENHsaR6R1da2VtbVYAnR4C1zKU
+A5MZ/cB7OC0/++Va0uPmtCdEdW5jYW4gTWFjLVZpY2FyIFByZXR0IDxkdW5jYW5A
+a2RlLm9yZz6IXgQTEQIAHgUCQkG9ngIbIwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAK
+CRDNHrapZn5C0f+oAJ9B08kix2phsvE79ZFUIbFsjtm3QgCeNPFdMcNJ2FlrDC1o
+l2arF81JBDq0LUR1bmNhbiBNYWMtVmljYXIgUHJldHQgPGR1bmNhbkBtYWMtdmlj
+YXIuY29tPoheBBMRAgAeBQJCQb14AhsjBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJ
+EM0etqlmfkLRSwYAnjnoAUut+98JScgZ3RUeELfccgs2AJ49zu0stW/6dfKHUG6v
+KxbevTRtBLQqRHVuY2FuIE1hYy1WaWNhciBQcmV0dCA8ZG1hY3ZpY2FyQHN1c2Uu
+ZGU+iGAEExECACAFAkiR1d4CGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDN
+HrapZn5C0aUTAJ97XpoPCkZh6tKwWJLPSd71rTUu3QCdEIH0RIEn8Hd1EvOvrJUi
+xYNg1Tq0LER1bmNhbiBNYWMtVmljYXIgUHJldHQgPGRtYWN2aWNhckBnbWFpbC5j
+b20+iGMEExECACMCGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCSJHWAwIZAQAK
+CRDNHrapZn5C0Q32AJ0eHwMEmyJrW703lxiU8OdlzlBLawCfQoFccGWKTrQXjem9
+q7rjZ8BJltm5AQ0EOyP9LxAEAJ2V7CaBeNwDBrOXHAbH3p2y2tRmnIwQNtdvsCf7
+aEQm7Pcc7OXaM4fk52OD8eHEEmn3XLJpt48DFXgnT3UWdEbieahJtntvapvZ49jE
+y1TChI200vxWyuUpMSri1Fx7IGchSxrLBY01gyrF/48vHLtCm8vvm+oD5DtxbdAY
+8MkTAAMFBACUp+tTe06sjzrORkgsc1CAG7IeD5Rng2d1HIPgmP/lmUuwsyZVGYg1
+qiBQA5oqeRXLhfzFdJmIHHfJzAVXMte5mhH9Tp3z4RITo7KbvMQZ0jQRlkB0MfYe
+Qgcuuq4gUhxvHMxffyJSploMqPkxiV68c9UdlSGIKGraOgSmUnbhWYhGBBgRAgAG
+BQI7I/0vAAoJEM0etqlmfkLRbgUAnjGNmh+7esr1UeqNVmKKSpnMWcNFAJ94i40t
+/KUd6iH0TeEIc5Lc7taHwZkBogQ7JSbVEQQA4xR5LOB2ZWSHN1I1kKyAktnSm2wV
+sW0SCpoq1w9JjmbDgnZpDKSIMuAVjNh53DeQbT/9XsofpyMBx5xSoM6isQCGNwI1
+sxA1Syj2yWdsXUHHhGyq8d2a8sLeCnchxvQ3gDGJoYm/AVWfRoNM8kw+vx6l5gNE
+FDnzh0CiwQKVU1cAoP/heoi+DpQV9cSpYNe8hzW3QBbDBACDh/9awQp3NrCI8oxX
+YIWuTsQAsDib66HQDi4w8US/7kVp4zqyoTN+gPk8knOS7z6WqpxmHD0DDZu4VE4U
+aC1Oa2H/63VFePcvwEUGQwHWThX+JnmJNHO9+XO0003KsH+0Yt4FRn64bJkRcL1P
+ScjYZFif/R6TpNRGQmC4JfH+EgP7BCPQkVoi0vvDx39NqT81WZAWc0zGTSFJVypb
+TyfYUrG59T9DxwH5NYWMXIdEAEFp+/OheT/AwU+22PfxY9k7rvUkXg4PoStV9yv0
+vEKVGd2g7kDwU77c068iNJO8LEYqF5fnSO8ieU67ZglKZSe0EU2hL6WEZ9TLh6Kz
+PHsaq8S0MUZyYW5jaXNjYSBRdWludGFuYSBOYXJhbmpvIDxmcmFuY2lzY2FAaG90
+cG9wLmNvbT6IYAQQEQIAGAUCOyUm1QgLAwkIBwIBCgIZAQUbAwAAAAASCRBnGpo8
+nSL6HQdlR1BHAAEBtLcAoOOto28BA1l20JI6ewbDqZ9e4TSIAKDE4ysvUE+aUj7w
+kdr4ODvQxSiSXIhGBBARAgAGBQI7JUWHAAoJEM0etqlmfkLRk+gAmwVtmmKob6sh
+PFJjk6STXkjWvx65AKCa9f2BjS0dJjieCpucWfEaaxh7+7kCDQQ7JSbVEAgA9kJX
+twh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV89AHxstDqZSt90xk
+hkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50T8X8dryDxUcwYc58
+yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4
+DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdXQ6MdGGzeMyEstSr/
+POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlL
+IhkmuquiXsNV6TILOwACAggA3d73TIqNfiKwTEcXx7OW8KlE8UGx7hiK1+IBakLM
+FkB54oVj6aLQyBsH/rrgVs9V/UYhygjyDhNK0715Og7B9bP+YhNPswix7KBj0mOA
+1O1mEIK6rpkgT9AqhARcwSUlDf4gg+Dz+pX485a5nw85xGJ+9ygySlzOXNE+GCW7
+PTRQUpqvKs2PTAIsZZLcuY789xl/PxDE4EsRvYoevAk2nLqvru3820LM4ynKwKqz
+0QICTLVcE7swa6ck6f53pbzhvEk0dM99MirtJMWkj7qYvwQwoP6U40cAE+k5pHTZ
+tboXmycyvC+mysvJrKmVTLrHMZ0v4Tth2eudUbwsrMkRYYhUBBgRAgAMBQI7JSbV
+BRsMAAAAABIJEGcamjydIvodB2VHUEcAAQF5ZgCg9T9QjbJA+h+uPT22O0x2BSIK
+4LMAoJZimOZ4Tk3USFLE4uXDd+whyKG+mQGiBDslNloRBADEn8TIB8BJTMKJzQHm
+sFBk/DYaoH4ERgqiRhyt7+ssd/5rOdbFQOD3QO47esBvP524bM3nkAiMIqmbWsRq
+B2XExLuNtfRHTj8zHm2y3Jtogh+e5FxgHP75+lThgoTYcGbflYHaRIMv/vYBDkSg
+tHAQ09F/d6GuYptTuKudN9H67wCg/96X+Ac+Kpot+VPb1mA2p/zaiBcEAKHEgFrX
+59lrUulJah5CAXaXP2buPQ1Fiday+MGoCIUYBpW5cs6mThixIZzqwbB149/8sV4v
+rBd5nQ8vWdddgyvtS9bWkn/C5ERbLrW8bCarph/m01NZsG45yYHeA/P6M/K8lDnO
+jp8LJgKmho1kfzLpfh/JrcymxaVR4y/LojLPA/0f5k3urRkyEAimp4p05/aLyW0Y
+LuFdh+MAfoL6VeOt1u1LZ2OsqQmTsYI7MPrHUX6zioh/k5OW0pEVHmiYBZtzoX28
+fYOAnJym2Cw0dTPE1gJVXG9ZGaqq2+HWmB8/XuYw9qHmUL2p3Dga7E4Oo49ja5qs
+57Ni5Ffo3/wHDGfNErQpTWljaGFlbCBNYWMtVmljYXIgUHJldHQgPG1pbWFjdmlj
+QHB1Yy5jbD6IYAQQEQIAGAUCOyU2WggLAwkIBwIBCgIZAQUbAwAAAAASCRBtSv26
+VLUDggdlR1BHAAEBphMAnimtyySasb0oNoRdfPiUEuBe4uZkAKDGXjDyVlUlppVO
+NYpgBo8B6D+DcohGBBARAgAGBQI7JUcfAAoJEM0etqlmfkLRZEAAn2zEtLL/5Pdb
+VXF1eaaCtAkfpvRLAJ98sHeXEMN3npwp9c/qt4MCNxvfc7kCDQQ7JTZaEAgA9kJX
+twh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV89AHxstDqZSt90xk
+hkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50T8X8dryDxUcwYc58
+yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4
+DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdXQ6MdGGzeMyEstSr/
+POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlL
+IhkmuquiXsNV6TILOwACAgf+Os+ycvrsYeE4gApXVFdty6c49JWi/x4QJ/kr9NsQ
+GVu2FYByKttWcMokPWWxKNNOR6ryz3Ie2MpiF8aGIqJImpwoG53B7RtGnml9U/yD
+z2yW8DUcdvthgi6J4Gzb5gDvWwlj/S1XlWk/Q+C6bAorpoQLYjsObf0NnADzIDdN
+4Uy5ESNixHWZ7GqSw6IEKOb+qDoixGWrEq37dDDML95dRjFL8XX8K0dhzO1qKSUu
+h+ACtltLUgSzyhWp1vSgLC1XAFRVert5RF47l53DqPX3hVlH/AOCwSa+XEAbtpCr
+gXMtImsgs9fLOOipasQggli7voJ8XE1N2DRHUBeNTY3YGIhUBBgRAgAMBQI7JTZa
+BRsMAAAAABIJEG1K/bpUtQOCB2VHUEcAAQFpOgCg5hPPL3haLptW/G8AgAyiYzV9
+Vs4AoKietcZ1/NBAS7QDvK0GUEgSsgAUmQGiBDslQEkRBADgDXtg08dhjHQpv9Si
+svzvROcsr6QRFOLWf95GWh7c6SI7Pj5mTClUaJXBRjLuvOR0L7w8l5nVJ/4kzSjl
+e1ad04eDzP3sB4rNK18l7GAwc0ijV0mp/Kl9F4iibco3cCIMsjysWgUmlk+dYYWQ
+ubQA/EgTs7kFQo9QRxzojqnSywCg/5CZ5TJ19gNS8knyu819v6ZSkV8D/01Fem2W
+ZejqsmNBNiY1JoWxnTofi3AjMv6j/+NjyBuU9ozIhUicx4y+Ro05eKUZ+VAIecVb
+VhEqH9VUriX48TDrNxu4OGJh62q58s0u/tqIX5OrvrpmdOC1UTdcc2CM88vssTt8
+oxauZeM7YAkY9rApXUniyfnJ3fdd3FlCIbvaA/9XXrlBjG/3HQSPfknbqLlCvEcl
+elRmm6LAX8fuf/JFgj0ctRMAJKB6HOE7/pvl3yRMMITwmd9lB/s2u+fqcwV1bDqf
+nyV8h3KFpTCGM1qKT+EaaT/8JhFJifYDjBEnUe9mYG7dUlt+Ff9Fj6ZRJQs9vszz
+QcsJDzNpys+j/KJutbQtRHVuY2FuIE1hYy1WaWNhciBHYXLtbiA8ZHVuY2FuQG1h
+Yy12aWNhci5jb20+iGAEEBECABgFAjslQEkICwMJCAcCAQoCGQEFGwMAAAAAEgkQ
+wSqJHVebm9QHZUdQRwABAeHRAJsGKaYQp44NzYEjS9dsEAT4sYKzDgCgw4c7kDar
+ZL/qKqFvHbvsPS6fRteIRgQQEQIABgUCOyVG9QAKCRDNHrapZn5C0WO7AKCBUULk
+55fR/yavkYgUhL9AX0rShgCfQ0jjVarVQNc/cj5ilTdKqSkeJ8q5Ag0EOyVAShAI
+APZCV7cIfwgXcqK61qlC8wXo+VMROU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mU
+rfdMZIZJ+AyDvWXpF9Sh01D49Vlf3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VH
+MGHOfMlm/xX5u/2RXscBqtNbno2gpXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2
+azNsOA1FHQ98iLMcfFstjvbzySPAQ/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMh
+LLUq/zzhsSlAGBGNfISnCnLWhsQDGcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+c
+fL2JSyIZJrqrol7DVekyCzsAAgIH/3NYWNGw9Wa8B6Ow6NyS2WSBrx33ZxInDgOb
+Ze72mO/GSyxxZqxPiqTFTpajTD+HWKQuvSfMGZdD1kJZTrNEI/MNLwVBhrgxLX2q
+rmq1aJgWa5aRcv05wHuIUrw2g220oZ09gUhXuF3U/oDUCAX36d16CaPCMJV1TRUo
+c2L1WoLP2BjzIkAgKrqUGvIFNsvtXe3cNU5dSQzF0spExuCz1Wy67/T4fzLSJpiW
+adl/eN67cp83ega5fyYy9NpISmeiFcV25UJqbQM0ktilZzDhx+evmYd6PHP7i8Oq
+gC1oHajEohfo9kmyO/AXJ/v08svnSJVO0G/9qQeuFp3JVeBHsN6IVAQYEQIADAUC
+OyVASgUbDAAAAAASCRDBKokdV5ub1AdlR1BHAAEBRRIAnjhaS/m/eYLw2g79GuLD
+/1x12Xj9AKCW04cRwUxoIirwdJxLNRT2GoLAdpkBogRDRPbUEQQAqD1L0hdAtxso
+Pdwc34z6EM3yBuynB/l9TzHe8LGhePAed7dle6ceDBsOVgsEcukvm5M/OjrN2HMX
+sJtGu/1nvxtEJs5sjjg/n/i+bO0q9KzNX5hVRmUSop9TF9LO2KV+NK0h6LjUmF11
+9r7vqO7ib5MyPP8n9eimnOG9Zz940+cAoLlehpdXlpDkvQ38AVLiHcCzm843A/9g
+faz7LjeJoW40wJz8c/HzCiNq/uvFLxJInFSHiimrhKfSVnXn0eWH79pKGoKmQtxf
+eedEo2cNq/1psdEYQAOJrJLjF8CGraN/TSvfqF6oElW8kwgG/EhID5cOtH/zHLZS
+L3kaU5kmki9+7Y/lBB6jlEj6EZNFTV0zC9Ka1qblcQP/QGIietl/9Aldw5LHnCr6
+94VnQ91mYaJ4kSJ2IZHqJByRT9FdFpFqRw9fm6zYBxNIoI525Ey1n1WA4aaUXHCo
+zIgeAsZcDaK3jnNrkbztnAM6OXdMwnSQ30zYtSG7Kbn965Z2BWfmNnYaz/NuRTL3
+EtHS9mU1XDlz8A1VSUQwdJe0L1dpbGwncyBHVyBjcnlwdG8gdGVzdCBrZXkgPHdz
+dGVwaGVuc29uQHN1c2UuZGU+iGAEExECACAFAkNE9tQCGwMGCwkIBwMCBBUCCAME
+FgIDAQIeAQIXgAAKCRB30Gza79cROIiZAJ91uP31qZIGX6YrfXbtb8yk2kvsugCc
+C1z685uRfQg01++wHwkj7L2Dh2K5Ag0EQ0T24BAIAL3jlSmKpyjHgvZmRM6WDxY8
+MTHtKknVQKsT7wDpJ82yaQx5tDvbiH/BfyEGQvh6vwjkJPc6MDJKxyAbPd2tEkl+
+KVde+F47IcQ6GMQ4lH/100w3nON2uNtDNyt44t8ko/zMiIDq9c7tcwljfdJxSMoQ
+WwFKlsIhDuLR7SPeqTJKOkb5jGW9ILAuRETE3eYl37xDwuN+7TcrlaJHmZmQD2dk
+CyaJrN67dbEboxeL2XgzkWV8K4VFxrbHKKTYINwt/QLMNnhUu8Gnm5VHUG7R+os9
+6Hw/qs9jzXNsHJ2OaumnmU8gdFEkoRuz7EAEyw4RcJHgz6qfIOaCPiZrz4KsgGMA
+AwUIAKSw54P61phChKmlBBubIBtn6knu0PLUlw/zso1xr845LqwnvoDlIlqlHzxL
+WERgVxrxPFamBr3kGVY6ojcSeperSvMeBisTMQuITBRU6EFqAYRMNScucLzmPCqK
+ki5wxEY1xaqk0LAxJ8hYzBgZeCtl1ozmDAZkqpGgycnbb7p6oB9MlC7t6LsWO8JN
+2SqLwxprbeIuDK7YZmF6vVtrTNS7rV4ezwoSX+A1SKP9MUoIbzELySBCwD6CCKfI
+eFdO7Iz3bxoPp+J0gIcOuyixwkf0xKoE1Cx+cPNwKKcG68g5wSgVxYes8dwK2btP
+WDNfcVuSdY7uLq3kBuo25cuVZp+ISAQYEQIACQUCQ0T24AIbDAAKCRB30Gza79cR
+OKjbAJ9rKEjBV92RKyeFMMJNqYO3b2JWdACVGFhCjk3wHOZB6MfwfK1dMQ7vdJkB
+ogQ57vSBEQQAk/GN+ftr7+DBlSoixDDpfRnUk+jApGEt8hCnrnjVnPs/9Cr33+CX
+LQbILOO7Y5oiPbJdHh45t4E0fKyLVzDerCRFB1swz/mNDxT26DLysdBV5fwNHTPh
+xa67goAZVrehQPqJEckkIpYriOaYcKpF3n5fQIZMEfMaHEElQhcXML8AoJVXDkJY
+h7vI8EUB8ZURNLZMEECNA/sH0MCnb4Q6ZcRyeZ3+1PHP8hP73b6TepRdLZhaylwV
+F/iu7uIn62ZUL4//NTOCDY7V63qg4iba/fUbOsWtEnGaiE7mQuAlsSWvRspwRA9/
+g9rdVf3/JdLJrLmKBTheyG+PSJE3W7cAE4ZWafGxIRCwXhmj3TQnJn2euqylHRub
+EQP/aL53NZK0kBdvrKgff6O8Of6tqoss8Dkk55I7QVFSp+My1Dn+mngQKFejTAgt
+yo/WmR3wPjQ9HoT2lRiYI2lTRYT4uMdHuwVC3b4DqAKmoy375FERwHkrMVyKBJsl
+v8QtbAWw5A1CAUseaHo+91wmYJ4/4p6YUahqbG/tZyhbxfq0KFN1U0UgUGFja2Fn
+ZSBTaWduaW5nIEtleSA8YnVpbGRAc3VzZS5kZT6IYgQTEQIAIgUCQNgGPgIbAwUJ
+Dm3fvQQLBwMCAxUCAwMWAgECHgECF4AACgkQqE7a6JyACsqQnwCfUbmemUle0FuK
+U4EyMUPgAT6dLCMAn3xvWwjZ6xEz5YDp/nRhJAFnoCi/iEYEEBECAAYFAjpwXlIA
+CgkQnkDjEAAKq6TczgCgi+ddhWb7+FWcfeE6WwPZccqAHowAnjjtRyGwHLQHr5OT
+FAYTXi2Wv6jNiQEVAwUQOnBgb3ey5gA9JdPZAQE1pwf/QJ+b34lFBNVUJ7fk/xGJ
+JREt7V12iSafaRzGuH8xWvIz1bb+VARxnnt16FDQ1cDNjoEhCEmcW83Vxp6iJXE9
+PE8wVA/Yue/bon5JS7J69+UiQ2eq2pudfwljp52lYVM53jgPYEz0q/v3091nlZ8C
+YkAkN9JDS1lV1gEzJ7J0+POngDpU+lDQT2EC6VKaxeWK8pNt6UFDwICRDQxKnlOo
+iDvTrdWT7QdJZ4sPv8Qotdw9+tKNbWQ2DqdIRxyTdw9xDfAtcj6mXeQr7852Lwem
+1gSKVnEYHZ9g1FTJqVOutY8KhpUc9RfOCRv8XuIxrs4KSbfSF0s8qIRCQelxufg9
+AbkCDQQ57vSSEAgAhJHQTejMX+Vr6g1pHDEcusJ63fQ2CfFFE5iE9okH9O7UVCiS
+fb9CV38dmeHdPCEEjDUWquFYEnvj3WICMtH249t1Ymuf4Du3yRKQ9oXdn/qTJzlr
+x9qzjiG3mH7ocwHOgUIwCrZoEdBEVE2n0zPVm+hddwjWWTWXw6pxQz+i9dsN89xe
+xRV5M9O0bNwCLaNWX2GXeLAkqTK/9EuZy6x2yLxi6du9YYUAXkZpqBhCjtiUXpRo
+FCdglMznbcAyCk9C2wqb2j/D1Z2BeSBaGCSFkR6pRLebnE17LWcu72Iy+r0z+Jec
+bPiyDpDZj4apn7IC81aNFGi7fNITsHODbwwjiwADBgf/YPvVdzkc8OC7ztacEWCa
+nwylKvxCdKzTDA+DfES6WUYShyiVJvZzRy25LJ5WcK20kzOS6Qv1OrIXiz/pdGy1
+aKtJZrAnFEsofpmOj8VoqyyFgp/yAGQBp12+mXek7SCZRhuqalDfEMRiWEJ6J5dL
+kyShyRDWyPbFh0HXE7QTHN+IKKxxQqNQXL6Z3NSxS61p+5n6BseiDUI39xxkKTFw
+FrkgUIc5Gs2Or2lhaWvGwSfoCmwbsklszZt6xbU+R0SjFqTvjPWx6eHfqbmNC9WM
+DdTjGrXDDKXFp2aYlokfN6It9vsbVlGNlOwHt/JjGoPMxW6Xqj0FLA7/VewgCdXW
+64hMBBgRAgAMBQJA2AZ6BQkObd/oAAoJEKhO2uicgArKZ6YAn0W2MMU94qvvZfz4
+DU1DU2TpaH9qAJ9uctHhotN+9Y2qfZDlerqu9bFLjpkBogRGI2ZOEQQAoyDrTWj8
+7ypQt9m/EeUw0QdPxUNLbDLbeXS8LqtmWJRG09WCxdddL2RJeSEAKuZXbw+zHUf+
+n6nV03cQXmM2Y3iBGGx84N6zfzf/jJHBuFvCCtR+ngwGy9OR5UNbnk6dJo4KBi2C
+rdYoEHvIebgOoBBO3b9LRtDScdsORrNhqZsAoIkYrrNnr9ttT2uuSKUdYLGLlyzh
+BACidQb45vgXxyYq6qB+4j6uR5QAcJ/rPbFJn+m4wpH2q/8kgLC9FVIRzQiKB1BG
+XdopgNmQNJEwcQfgSF/xzEWc8T2RMGCPENFGLE1W/4J0LfplmmbNf9rXlkdMJ1Kf
+njuTKTgDvUIxSU3SM9BnoLH0jjwqOeQlLZLa97SVCn2d4AP/ZmXvm9RL4tf1LPpJ
+ZxEUSkdo41l8cLjzelIryW31yGY7lVDXJQIRKrLc6X51idy4T8Y9Myb4afzJ6kk2
+iOg0G69H/ib8D/uZP+IG56pvscTqHMoYlU+OczjwMlGLhuLdJvvRu1ceiwvbTLpG
+3Xx2kcpuNsgtrNOBltorCn7WtWW0LFpZcHAgVGVzdCBLZXkgUGFpciA8enlwcC1k
+ZXZlbEBvcGVuc3VzZS5vcmc+iGAEExECACAFAkYjZk4CGyMGCwkIBwMCBBUCCAME
+FgIDAQIeAQIXgAAKCRCb7MK13bA3VklQAJ45EdpWqIW9kOFp8WpnR2Fkn/FCygCe
+I432bBatL5oD7Dv2B2ikyTebAC+5AQ0ERiNmURAEALaAeUxk/SuzQr+Q+XUMO41R
+54J8adzRk7QIcIq2KYQVmhdm3t1CVUy21BVteHjJ61VvTyo/xzVS4ULtAugml7V8
+uS2oSJbPphZUisG4j++HBZTWHI+vbcwp2hSoBJUDx3DDnN9RII8kmHFCMrONtTMS
+PvrLoFvsh6+QciLC+r33AAMFBACC0sJi7Q/j8JLnrO9wzz8liDNzANHsTu0JmXhc
+J26YmWLmUNxojxq23mVah1ge9nYVvPuboEfQlUErCxwucBzCEBVPEbIi9OoGXSGi
+TYAK+2o4bVMmz9Fj1aV5M5uflQfAoXvZtGdLdrSvGNClWqQXK92A4MfPRfVQNMp8
+uqt1oohJBBgRAgAJBQJGI2ZRAhsMAAoJEJvswrXdsDdWqbgAn1Efg2qKNzdX8cQ2
+twN7eDpAH6UIAJ9VBL6UKfv+A580mxL8My3qHpN3c5kBogRGP7wCEQQA9nMS2PNz
+9htYeU5JCmEGEy6KYFkIe/+x+VCtHH+toCx+0ehewcFuBKpi7JvHhhPJCDqG+cAk
+FOqcaQTL/tVGKDtW6IbceC9iVGOcnCYquLtQjckbnIkd8oLLnjEZHpZENoaTbir5
+n4nDnKGs71N7WTOiEyY1f7UL2E0ERqsBH8MAoLNDmN6u3kfDcFnpGo9t7fE1e4Yz
+BADUukDII7KeQnS4JV9NqHoghvNqBCiwzlqGdmow/vp2s0iH5SRNA2mdFtukF3sI
+eyEc06JlffHyVeop0DWp5ZOf0zbgG9XpV3WmBbjW52dMqDmkycf5m/PL5ut1K/BU
+8inXDfnWrvMs7jZSYII+cgXDYPv/GTgJtWZAzJYPbgIYAwP9EUo17v2DZn5XjxcS
+CgQgCRSXp8+HUFGXm+XLib9tHgqpyp+QhWBVOmGIN7GGhLWXrpbQ18Hkb3hsHtvB
+P76S1Gu6fmmavl8LYyOlOluVar5AiiFLEKsFVkR4p9f+AwASea51Psj3i1zR+o6z
+EPowxvEYniPdf+J/oAl9Z28GUP60K1pZcHAgdGVzdGNhc2Uga2V5IDx6eXBwLWRl
+dmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRj+8AgIbAwYLCQgHAwIEFQIIAwQW
+AgMBAh4BAheAAAoJEL1h2JvZiCG+0yUAn03uDMK5jpVg8Vld8mehOAfcUKtGAKCF
+r7OKGFc22kM3JUrR2ouZMbhp/LkCDQRGP7wMEAgA+E9dSjxOws5KoBjGjT/sjUM3
+1A23f0mhEOVnHuat1b5lQaWDkcclo0vEDHPYHvFsXswLI8kyrF5fru3v0JjCrKT8
+z4ZFSsgerOQ/BYUdT3aO2wIuMcfjSzrvQ7rlPo6lJMh2JbfEBds1/fDjnAiLkHNo
+Y5Ndnb3xyplIg6FJ37kD8b2KxQFp3rIFCTKmaO03eS8uCI3zCUJUOwNfOgRlPGz1
+qFdUKlCJ1ZDRjVy5oJXpf5vjE1dOZ0+vtSggagkVJbh6g2gohRzlDhVVr7Yq5jyW
+f+uye/DSaRe4bs175EK2mrV8h5wMocIsE2S6to2mzI0IBoVfEMineKp4kgS/owAD
+BggAnx+BRqq7PaRgVEZko2SpRwFHZxrpdDNirsiYb/GQZAKVLT3DIWSXKOdKRaZK
+o3x+398VzEVQz+RaY2kbeu2m23s6fw4LPPuvdTrQERA1mwM2qzTUdxWwfX1Hm4dS
+douFu7XwXKCsChViQr6l9hxk81ctZN9757hglcAvyomfwuqXOA6lMAljaGTXfIZh
+WnSYMdwjJcUk5ZbGiMjl6fHTCfhRbE5szjc2aRQvQw7aWlHhAoQqbY1sMuVpEBT3
+vhTi1B9QZxknEGMBpRPJfnBZBO4VvLcjsHMeLJNSf7QwnidcBPMHtS1zHSIg+boH
+9yXo9f/fKPoUEx435MBqghliVYhJBBgRAgAJBQJGP7wMAhsMAAoJEL1h2JvZiCG+
+TvcAn3iNEaNVAivwlU9fwR/TTQdE8mMwAJsHUBROiGlUrY2w0WBJ0x0DhrFcvZkB
+ogREdFC3EQQA2b71VLFFEozMkNn1LfFLuHhibj2zIRLUf7xe6cxf/LvQa+5IelgE
+gWdNnTHjaKhcz01O87+j4j/d4ji8MtjEDTkgS5EvjLHEen80umS/NZja/g8IDhf6
+y2eLbnALAWPWd5YEcdJloZfl7p1T1x4ZEfR/UYoOMDq688AbGI43178AoLkNT0Ov
+lEIC/Ek1ajWjZ5VWM81FA/99+iH7cKIB/7SqenVfxWD/1aSx17cBXntL3AoeRcHC
+j9L2KPTSHwegkdoNKcmLBwVm4XjFl0VU5QmlFToWsnHfg16MipdxXMS+tTg9Bf33
+oNlBKh+59XLBldjAxpal7BebqynT2HAURuesp5Vl7N1uw87vSTfLJIVkp13bQRWt
+wQQAqPggF0syyZxaxu5IPAGE/tJPpE0v1MncAK+e0Ei1HP25V0erHjXfkzOCsI+C
+I9qTS/y6Wcs1aw0vQVjWR6t20JxET6316SuV1l9KrmZ/M4NSJhcMZiXbhyprcssT
+Y4z0dUlBcw9RF6T3wmIES+pFODn5W4BqC9mKZoBzui0Pzhq0Mm9wZW5TVVNFIEJ1
+aWxkIFNlcnZpY2UgPGJ1aWxkc2VydmljZUBvcGVuc3VzZS5vcmc+iGQEExECACQF
+AkR0ULcCGwMFCQPCZwAGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQOzARt2udZSP3
+sACZAeKbA5uOEXJK88jMo1xCgWF2Cl0AnAiwVvebe6KACMqn7IPeESAqW6+dmQGi
+BD+dnTsRBACwnlz4AhctOLlVBAsq+RaU82nb5P3bD1YJJpsAce1Ckd2sBUOJD11N
+UCqH8c7EctOquOZ5zTcWxHiWWbLyKQwUw2SUvnWa5SSbi8kI8q9MTPsPvhwtgMrQ
+MLenMO+nsrxrSaG6XcD+ssfJNxC7NQVCQAj3pvvg9rKi3ygsM7CXHwCghgsqX6TO
+r55HE90DbEsoq3b/jjsD/i8aIZ6urUgrpAkQslcakXdJLKgSdwjRUgVZgvYZb7kA
+x1iPq0t/AhB3NJw3zW4AAKJohGg3xj5K4V8PJEZrSIpoRYlF43Kqlfu2p5ghWT89
+SP4YAlWPeTqf0+dTYUYz3b144k2ZFOdRuXIRxunoYNAUr9oMrxBXbJ/eY+0UQX3p
+BACYzKizyY4JJgd0zFJmNkcdK9nzcm+btYFnYQo33w5GSE686UNr+9yiXt9tmPRv
+NEbj3u+xoAX8B/5k3aZ5NbUhV64/VcKlUdRIxNlFCG7I9KgxeHWAYwi7yqOGXM3T
+/v6o7GLdQEB0ChFqS7kUlqmwLV+C3QhlrFe/Cuk26i+Q6rQiRmVkb3JhIFByb2pl
+Y3QgPGZlZG9yYUByZWRoYXQuY29tPokCHAQQAQIABgUCQCztZwAKCRDdE2MqcWVu
+aC26D/0abz5VhIh2eOsTpI3vTlHJQpe3Cx5qyLJ5oFaUFbjVIU0L5Vt9dcrmXvzA
+oa4czZxtPcbo7wWvENCA3tpcdB83zPqaD9VDEZrjoFzNLL8pK2Kl73/IcMkf/UHG
+pcJCWoftag0Zq5AngZgH0Voe6YAreVPBYFSIaZhSCEaYEb8ejOwdAeYc7b01e/43
+TLfRmoeQMFoH/rZUqcLJXfHyi3KkCGZXWfwYkQiGsy5pMT7c4WzIwNZju7QaRJcN
+cB9y1FHGnkaz6we4dkh79sqRvn82dxgXbLa1rR/m6g20m/uuOrhHle7gsMtmmQAm
+aUTes54HDdlDe2JdL7dTqOhXX8mF3rf4j9BVv/Vz2y4EwBE7JVqPYHT7HDolQ80O
+xEamJDuo3h5hgPxsmPZ2JZRLzZKeuq43xz8gJlcB4vpS6Q7j15FB5trQ6iKTIWzW
+SqV5zBahG6qJ4RN1ubJzSzOgNIycJ0uFZZ5rnYIyowqTwmzGU8VZphrz3436lYlQ
+4sDnluIlYB8mxZHZz2+k3E38PHgqobXa7Al/5kSQ4owTyfo7+IvxHkSbIA5yQpdW
+mV8/QJpp7PlcvBlYyvwzXNgj6hGu1XOjSw4AxXtHgmQOya4Q+ktARfWL0wfP/ocd
+vdkHajiRv9sp59C/Ir5/zmTmgQeEudous19MaMP43AKqj3Z2/IicBBABAgAGBQJB
+3c/dAAoJEJhqQe8YjLfJfTcEAKQ6q/+LuNxz5X4nX0S+6s8pxshdUUTcFQnN013Z
+dJ7HBJIt/GZSbyXm5dsWdmvne9vA2Gfo2glP4HtVvEBbmT9vCKC/V8aI1QF/aTcV
+wdUaXzIj2rG9dl63LCLl/o9hBa3zPOL80kaSyjPGMuKBM/UzRAVFjVihd0G8gPso
+pGSuiEYEEBECAAYFAj+e2OAACgkQKdW6JI31bQVRJQCgmeuid7gnSjUcyRHP7qD0
+pW47YXIAnRfJ5s7NWYj1Tk7I5+c2ZhXTCdByiEYEEBECAAYFAkBpvjQACgkQcdLU
+aCBIxSjrhwCeMzGZSA+xfgL03obWLbZN6QtlnpoAnj4aA0MkT6/oqiJOEQp03uh7
+KMrciEYEEBECAAYFAkCo71wACgkQXanJZSIKP4/TKQCfUQmD5oobxJQKCoy/g9aH
+ryQQq/QAnjK7ql0wBaO6Z45yggecwHAP5Wm2iEYEEBECAAYFAkEU8TsACgkQ3GVW
+6BHmDogH4QCfY4tpxjMm9EYa98IWaPwaFW59OvkAoMynGza4cvZOTSrvFV9teWfv
+5r7AiEYEEBECAAYFAkEU8U4ACgkQZGYqnQA+HZ26AwCfVW71WRotzZI74EKmcltt
+Xve6WHgAnj8GrScGsOyLUbcGaiPNyu1NWdRGiEYEEBECAAYFAkEU8VoACgkQiEVN
+9vr2r+PzUQCfSKSK0ZJ/cf+YdvrHyxcABg8pWZwAn1FnhKQ1AW90BLXkhGpNy7nN
+O+ZaiEYEEBECAAYFAkEU8WUACgkQ0ptfRip0+Q2JBwCgksE9QoxWQwaRJApr+Qp/
+k4K8VnwAoKYlK6AvAdeiOKOHlM+eCQ7F3ZBniEYEEBECAAYFAkF6JkgACgkQUdrz
+TXusf2wqsQCgiLp1BCmfVnOjDeis49xCIMPNmCcAoILl+XvzwO6Fp3JHCghndJ3S
+CycbiEYEEBECAAYFAkG0c74ACgkQ+lAXgUiSypriPACdHwfmU390R8YWPg3f9uHz
+WP+iCaIAoIOXgZDN2JimS8za9+IFjzz+xQ/SiEYEEBECAAYFAkHdz30ACgkQ9tuO
+JwRFhLVkgwCgv/57BB269RcFh6fTucG1Q9kfVTgAn2z6rYMpbmodRUlbV1zifctG
+IKBUiEYEEBECAAYFAkINVnAACgkQhfn5/UKaxrY0NwCg3QZqs09BvyYFmLssuEhH
+0fN1h9MAoNjfBazVPxZRLZSZpPPZmF/AUCv+iEYEEBECAAYFAkJA1zIACgkQ/RiB
+B2NcQIpPRACfUSPNfj8tJLky/QT0c425M/4a6QsAn1wARHuu4Ns0AkQJ7qA3uHc6
+19dGiEYEEBECAAYFAkJdM08ACgkQaajtSerafFlzwQCgtKtlxNPhH4TUj+qOg/7G
+ul9fLIAAnj5Xz8m83GclDmhmpPNXkTWuetugiEYEEBECAAYFAkLWjJ8ACgkQbGUs
++HTfTWsOUgCgik2nqZNx7DbYYHB001sOVTMzIRwAn2vL3TUQmdELn+0yizufL0AN
+HL3tiEYEEBECAAYFAkRf5AoACgkQi9gubzC5S1zlxQCfehVXfZ7C4VoEoEkorx5i
++oWP4u4AnjF1CdHeHnUEtn+ar1Q4HKkuDU1biEYEEBECAAYFAkTkVbwACgkQ5aYV
+37hLgJBEPgCgg3qHa3Au96/Ycj45/t5pjSN4/f4Ani3cFmiDRiOU7wZxe9zPaqpD
+6KdmiEYEEBECAAYFAkU5xIMACgkQZ/MxGm4PtJQBdgCfZunvqtB1/ff6wgWRWqtV
+sMlenDAAn2shRndxeHiGuxgu+4N8jxwsDNG3iEYEERECAAYFAkKy80MACgkQQk6z
+7JKZxYfXfgCguWu/c5bkOADFOrH6pXP7H3U16MsAoIEki0PHq1W2W/Y45o1KKIBS
+BHvWiQIcBBIBAgAGBQJBkP08AAoJEAvXRWEqdVnVuG8P/i3IKnskOMtyBrgyoad3
+OFLxIyg9c7m5YhVpOutZU5V91ngpk35T/WcxSx1AdIDeFiZSDK69RU6cke+J38Qp
+GqfImdqSRxY417AF1AbkB+csf/V9wdHVZh1PbrA0ZGxaRxQ6EAErLWzS9FVR/d2N
+jEtTaGfQWU7HIeHER1JUZha9C77sEdjOjVpbEpXZD5L3iB5hpvKVx84ouuNXUelS
+tCsgU401Qbl9/VZYKka0GeQKrzMTDXfiXtISFqA8BsSE5DcKOjMZUx8n0kreNqVg
+RtC+PKOBOTrKoypelcbYs2VQtuztaCtOhn48HpSP9N+or6vovIBfoPsmA0pPQ1l5
+n4pfN6gAYmSi/U/EG3uLQ5xBsejH4PQtQwRKvwXRJrR3r/U5r5ZMVKiUGSNy9oT9
+ZRicjf/J7vUni8c1l6YRv48bD3yBSpZcJC6aEV9V1A9jPpPatBLI5+EtDPtr+Wuh
+5tEe8T9t6rXUWpf0KGxSV4diLQwaInPsu7qAW7RWQbaMwCfoc4EqwCvIHnmhC+S1
+dTQVjaUYJE4vc5cTZo0wVu1h/fg4QMdZygK1R6c55H+efsm9izybCYGHCNIDIeK6
++Nm3X/lab8NFCTbdsURvXXXrWaNu1Gif8kuLz1y1RR4RVdW2PU41qO01OBIGiNGG
+hPJgar3O4i3raVT0CRQACPUkiEYEEhECAAYFAj/eLnYACgkQ9jVtZM9GVc8gkQCZ
+AXjWVuzO56JGARz87PRMEaejp7oAn1CUrc36G5aWEeFa313zajqV9n7miEYEEhEC
+AAYFAkCpRCcACgkQXtn1Qb6VBHJYNACdGpDwlw+sCf2Ec4/yK13Dg+615iMAoIXO
+Nz0rmpgXItEREAdyWMHbBI3FiEYEEhECAAYFAkH2jfUACgkQgrin/Ace1CYzyACf
+Vf+0d1sd3KwuK4irlH/0weW7rjAAn2b9E1JyreDk4kgkmWZlqD4BCxPqiEYEEhEC
+AAYFAkH2nJYACgkQZL0IoGaeD6MiPgCgxGB9CzWLPpF7EefUB72pPklteR8An2N+
+TGjV7MPx3TP31t0k6ijE/2hTiEYEEhECAAYFAkZe+BQACgkQ4J/vJdlkhKw/FQCe
+OJNnc1uReGQ+lDRK/fbMWsyNSQgAn3I4/JN0LCH9ilfpr2987DYi+HC0iQEcBBMB
+AgAGBQI/zfjJAAoJEAuerLG7SymnPF0H/RU5rv/kuCVmqv2slTODZYzLunWyPBXj
+siB4UJlHDyJm43SvTNPv4Ta9YzqGthKszbLgCTvo47oWXnRZPlCtahtSIPEd0o86
+b1LJLV481utkTDNozSOqc+1/vcsSGNXh3tE+butBZ1bS7VREsGcC5MVuJh8wpC8p
+l9L/4L5bkhRM+hgjfUhCZBhngOPBQGEzKoKBeu91Y/dAfbmianYmBHOH8DEbCCNP
+2Sll680YK6j2s/YrE63inczqdp3VDMhjUiUR4CLoGn3/fb6XRwT0reUNbBlziD34
+6+gIMA1dr2e7PHolBrbNAvTAI50+S6hR4CkZ9rx+n0rQNP20FO7VvneJAhwEEwEC
+AAYFAkF3mZEACgkQX8tdKqjwLvW7kg//ZtVHiG99LPPoQWniUfH3sN0njOICebzE
+psAjig6GZh7cr6pj7AeaXlOSOVPQFGZSC+xwbiVjWGIZjNuCjcen94EMRswbYKSG
+f+qQwS7eNMeXTrQIV3CQGk7sYoTV+6CJ5MNMYUCTuBJLSvjuMkUMcDuLadKioRkF
+jb979v4F3bQGWEPZsdKLOCZyX2cNjVtCcDBlkhzYNy85PwzdiA5YdnoACxcfe8qz
+6BpOC7kZiLN3yV5xXkU+uexQGBJY564+B57y+exBRvEYwKEIQwiq50qJN3NNdhN3
+1HFdURzAV+KIzFXu43+SvtxR9F2OdfvPPHrN/Qpu4Ni1CBvypkG5XIkwd7xM3iw/
+6fh4/BFzYYIFqUADPFzkYKy6U4lls9BNL/Yk0JgEkLbrEayNW6QbM1j/j2wctUrb
+aEe6Imncp8VN8DHSjE2tOe1iHIj6G+9rywDXx4kb9hwqcOfpKL/0AoRzle6aTr7K
+nCpxBMXCIfQyLKEcJ9wiO3LCwBafJ7fmLJFUnRb4L7ZPg5gMejl0fquMtVKR+CyS
+LnhEqDwbrtIR5VOJuZlUFzr77HBL/T7rbxiU3mHONAI8T7dxs2fDv1+ktKeQtlLV
+v6DgOc2AaqbvgPO1LEtOdq9fN5aaoAi1EGBDVkIMdWDXdVYY8PnP7GXow2X3G+oM
+Ddl8+NpN3g+JAhwEEwECAAYFAkKfCxwACgkQ2MIKCVokV8/xBQ/+MBAGiuwATgRa
+lqxdV/PLCDHTaRKAqNf1kMhCMZ99ubECMPzXgI1QQa44AiTNmUUd4RNrBS3gzMjX
+nx4oPe6JjXCkP1grnBLMple2NTkaWU0Q/VQkbDxYvZaiPZuz9JTrilU3Z6L8+e6A
+zHVAs+EdIeYNxqeTfLUbFj+59AN9eJs7Ud7+uMWGeeDpiZUTcRXm2wdBRzfZi82q
+bSLREsIAAvOHr7uB5YarjQOh6lFTT2Jo0GAE3fzVlcSAzS0yV1hD3qtIQH6x7uXr
+KTAcTDYQoDJnPIOZSYiz0xbuoAuWISicGXVvlaJ03C5+qgQ9oHRbzALC9SvxPXZe
+OC63bjMC7YLXQRzZ+l4wkSjl+d2FfE6koAAC1vTzVx8febJ6eXbNB7mA3sO8gBRG
+j7oVzpqY2IAI+UqzyBuKVp7XElOKePK14mw3Dr0Ps/0MC63grWq6kLlAreGZzrS/
+dD/4L1ZgxibFbQXmaElQ+2n7AgNCV7YeESafEPCVV82yScPxC1WlpVBUjqcmn4gc
+GBAQ62TBTg/2JL5Z7axiygv7KwDRiAb7lXtBRFidF3dCYOyA696K6CKXioXZwYA3
+XIZXat2A5SfvH6NKVPzcultvqMWtvYceIx9OfiDiTCrInXnixOSn9cJXRk7SeAOo
+Q/PHczcYWeAz8kAkUzCYMLQwtusUkkKIRgQTEQIABgUCP52dtgAKCRAhkYDN20Km
+DkeUAKCKdRvqJlZLav7J8aZs+VHh45UmIwCgka/3mvy+0u2U8QdfHH9U2H2ndceI
+RgQTEQIABgUCQA2AagAKCRAqHoQY2VDGRzkxAJ4kR3qK5sz/RW34GRwgtpvqA3tm
+AQCfVz6ZbFeDgGtSzE+Hii7P2EGIz7CIRgQTEQIABgUCQC9h1gAKCRAYtrIyAv9x
+slhhAKCoaqimGJyUZZy3CmA5w9VeEQ/F8QCaAk5+ZnpWWitw9Px1srYz7Ps/knyI
+RgQTEQIABgUCQDe6OwAKCRAtu3a/rdTJM4p0AJ9ZetvNX6woPsu8P/vxOXW4h97g
+KgCffLe5NAJrQJEX/XqVyBz61UaKCRCIRgQTEQIABgUCQGhQ0QAKCRB81sDYi0Fb
+qWAeAJ9Box2B5/C/YTc8CqNbB03JYPMxDACgvth6MSFftyxQFSoFGe9A7exsyNKI
+RgQTEQIABgUCQGhS7gAKCRD2zzZt3CnlVIvzAJ0XTvHD9bKgre6hdyDFIuxuSef/
++gCfYOUsVkbwUqb3qA6hpbsl+jQHuvCIRgQTEQIABgUCQLD91gAKCRCzECrSTehe
++AiSAJwJDLftzrq7Y7cOS8uNabiJkbFFUgCfTSM2QN4PN98bvII397o9TaWonxuI
+RgQTEQIABgUCQieafAAKCRAOLabAN5HGCsDbAKCrV2bphe81jwW3UIqWK30aPI2E
+QwCdEDxoHAQbFwQlriUUKEXbngBd8UOIRgQTEQIABgUCRiZ+5QAKCRBYiMxKKtdp
+1k3XAJ95ZF9U+TW2jE9C6eg0cMXwc857FQCfZsUXM0YBMDemoV6MsUHLv7EllquI
+SQQTEQIACQUCQDqQOQIHAAAKCRDoDiJ7pAPsoBavAJwLUNAOOGHWauZJn06IhAW3
+A8uGkwCfbU2LiJwyQ/y3nBhpAehsp8yiZBmIWwQTEQIAGwUCP52dOwYLCQgHAwID
+FQIDAxYCAQIeAQIXgAAKCRC0QmnQTypv0tjwAJ0U5YaKyE6YWm/qMQB7W8WRfQVc
+gwCfVjlWmVBudyFliAFzhr94/3Jq95aIYwQTEQIAGwUCP52dOwYLCQgHAwIDFQID
+AxYCAQIeAQIXgAASCRC0QmnQTypv0gdlR1BHAAEB2PAAnRTlhorITphab+oxAHtb
+xZF9BVyDAJ9WOVaZUG53IWWIAXOGv3j/cmr3lrkBDQQ/nZ08EAQAugOfLWJbKwMA
+9vg2mJU594TZU0HRJkx/fqYhx0YxWWRpzplrEyvcDXuYcWi1Hwh0tD86T4fR5GV6
+joWiWClzD+Hwhhb6gcSdeSGlGLlZAvWYtFSHWiv+3LaI9w8Vtczl99Bh2WiMDNDD
+Gw0RQg6ZaftldLSe4j1pffpFGQ8SuisAAwUEAKVxqLT7fC5xQ6oclcZ+PhoDlePQ
+1BiTS7tuGM07bFF4nNvY91LL7S31pooz3XbGSWP8jxzSv1Fw35YhSmWGOBOEXluq
+MbVQGJJ5m8fqJOjC0imbfeWgr/T7zLrJeiljDxvX+6TyawyWQngF6v1Hq6FRV0O0
+bOp9Npt5zqCbDGs/iE4EGBECAAYFAj+dnTwAEgkQtEJp0E8qb9IHZUdQRwABAf/+
+AJwNVicN6A0I7EOfWx50PDHD7SHw5wCfUJkeh/XlCrGdPASe/AXZB44jl2eZAaIE
+PI92jxEEAKJ6P7KohUrZdFsywojJa3hs3SuHzt8/7oFymnTAA2zCHZkGslM0EtbX
+NgCoc/40+0CoXAKFzhCwktsbZJ6QcGr6nUK+tpEFO+Ku9Gmg23Pfl8AhAVtHEVuR
+qsp1aJKYx4U2wWtC0ZCC7I/slr3YlssoEnepUSZ4VkJhpFOCc6fzAKC8Kup8ioE9
+Lqh5DbyVz8dYAhUjSwQAit9wtAWBHa60q+ca0IPEqSBfB29rIhNio4PEsUB941XE
+Lm3O8EUOR7fBgwZkbAeXWqgXfYK582QF9Y/Q3finGzDJa+48cZ+Dn7sGphMQSfRY
+3Pd9870U1z+xXtCLjcrYsh49X7lGpqE9Cc4UsIYyLag0bmhS3gyjFS3llJHi1hYD
+/2NdApt8WSKs24Y6Yo0fmCb4mj+Mbe9KAL+UlrZ+bWIZth1S5AiDeNEKz5mmr863
+4DzJEOAVUpbUoHJ4j2Icl1jSWlA5vSB6/ZPG2KOMYP7GqT6Ci68lAbwSMS3N8iF/
+PcVV+04PQFqFZRNGUrk7QdFLriZlgqR5zB01n6QDbRtgtCpNYW5kcmFrZSBMaW51
+eCA8bWFuZHJha2VAbWFuZHJha2Vzb2Z0LmNvbT6JARwEEAECAAYFAkNushsACgkQ
+Dvn2xlC5DUFX3AgAiIMe+xIZU+NLNQIjjgHsP4ontwXpEWaSapL0ufxeGasTUK2B
+S6lFVcZPuqWahnNjd4ETzwNdGmZIb/hP4Sh5hLDmFHDH5q9wIirHfNqQJu4hnal/
+oP8pHQ4jDzdmdyziFxM7KaSNZypzCfEXaOeALOPRsprNOwmxuq4dq8iWOKrzKTHs
+Edl4uCdDRTyVGyAKjTpWr/jVxhSvP4BoswsY4KO6eAmVTHIlMfJISz78ix1aM3zd
+RTrqTj6TmFy3j4hKMIdYrINceF+jJDqJQSs4hx2REA2K1wHRiiZG7+lj7rM+wja2
+2sjYjwaA42WhZUkHcw5/GQlZlEg5yMoyq8jXs4hGBBARAgAGBQI8j5RCAAoJEJqo
+0NAiRYqYW9sAoLCeIkHima6e7Hk/35RlZXH81Tj0AJ4rNYxH+CA+xvKz0GYxdckY
+rzRnlYhGBBARAgAGBQI8kC0kAAoJEOJa9W6W3C+pkdcAniNAo/Qpuah4sQfIqL/d
+mFKvzhXZAJ90c1rXhV6rSFHajGkr2KgNYdG4VYhGBBARAgAGBQI8nNcvAAoJECAU
+9duuEnAVWFEAoJoKKSoJgr3oEKv1wPbcX7i3gTDpAKCUlqsEaP+amU9SFChF1g73
+TXSsIYhGBBARAgAGBQI8nNsnAAoJEJae79OXFtsKLUwAn3wAcZqdsX0I4UpAKAzk
+q93K1HBZAJ9ZTiPgafzF9I5VYOJKBgMsUN+zmIhGBBARAgAGBQI8vjYnAAoJEHW+
+ByMT/qxgqSYAoITEy7R5+JTvvAjCuHO6yyEusIDhAKCDLRcjz6goybLK8H8QIQo3
+OMe1K4hGBBARAgAGBQI/Y9fLAAoJEAVAMnUSMMYzzJYAoLXZWuB15ZVp8qRqI8aa
+t8oi8wbUAJ0YC2yLCrcbTca1C0jvy9r4KngsPIhGBBARAgAGBQJDvnH/AAoJEA8N
+e4Mg5YjtqMwAnRGeLWZ/eS/9/o/ZjroQ5lVGeRFCAKCG4Hj7OH3biZaVzjlDVI+E
+4EDzD4hGBBARAgAGBQJFzhU3AAoJEH01OEiAPLwG+RsAoKJBsiV0UShGnUwkrMAF
+HNNbfSuDAKCWwkUdkoOCiMiwEQhDVK79m+0J9YhGBBIRAgAGBQI+QhwIAAoJECnD
+EYFcQZb7shAAn0N+13uI9Yz/eulVapC19Y4t1TZ6AJ9ALexUd9yS2uE8OGyC5LM2
+ZMrS6YhGBBIRAgAGBQI+XRnvAAoJEBNgAYE58Lv0CkwAnivxzsbIAHULbwfHzSKh
+XeBGcxkYAJ9rgUz4zi0WCDj5THOEQ94cfz5864hGBBIRAgAGBQI/lQ/oAAoJELvy
+kpzxcFXQYDsAnj2ytgNhBYkT6TAa8PUzkhA774xrAJwMBoo9OaAQdSXhn4xH/Ztx
+gvjLrohGBBIRAgAGBQJBCi59AAoJEADbq9xz7cVYZ24AnjbATK28ycxYvncouVlW
+siqDx3hkAJ93q0OKgOT4L9VICOLOG43d63oFO4hGBBMRAgAGBQI/cXBPAAoJEBZP
+bR00jinYLGQAnjGvSjirxs6LU5nyUuTGTKwRaFo+AKCF52bhSRqjF0nGROaoeQzN
+tOPDaIhGBBMRAgAGBQI/cXBdAAoJEMwRmq0kUM+nJPsAn2nqCe+By4ZD4AMElTBe
+Vd1w9URbAJ9SpnOVuSQnjM2cQP1t3kdrCo6hcYhGBBMRAgAGBQI/11d9AAoJEERZ
+Nfh40Bn1wxMAnR5QfCFXtMiYT4qH5Okh7qaWgPhOAKClfahvQOUvnFhSDz2v6dtv
+gnrL+4hGBBMRAgAGBQI/11iIAAoJEN1oTXomdSYkLSEAn3Smemn0SUvsjR3guxXL
+M82ThGFxAJsFChZAmUlAAn2jfeHxTissX8g4uIhFBBMRAgAGBQI/5TrUAAoJEIvY
+Lm8wuUtcBJoAnRmyU6Ax7JMJoaj6dLL9xORhegv4AJUfFwwkkN65V1ITnfDzFa09
+SOyMiEYEExECAAYFAkMOC7cACgkQmcxCmjBUBteCHQCfQsYSND7bV2YbPt+r8G8d
+bnnjWIcAn3c07wKyoEU/CxBWcbOfMwZ6uwJFiFcEExECABcFAjyPdo8FCwcKAwQD
+FQMCAxYCAQIXgAAKCRDniYrgcHcf8x86AJ443ovF4CsXJAtEC7XzedooURgKzgCf
+VMrsPXfSqeXJXxKS04ygK0OmJaeIXwQTEQIAFwUCPI92jwULBwoDBAMVAwIDFgIB
+AheAABIJEOeJiuBwdx/zB2VHUEcAAQEfOgCeON6LxeArFyQLRAu183naKFEYCs4A
+n1TK7D130qnlyV8SktOMoCtDpiWniF0EExECAB0CF4AFAkSzgi4GCwkIBwMCBBUC
+CAMEFgIDAQIeAQAKCRDniYrgcHcf84iAAJ49Ck2WUvfoxWSlmmMpnM1vym0HHACf
+RCGDo/90haN7tYe82t+HwTjUH9i0Jk1hbmRyaXZhIExpbnV4IDxtYW5kcml2YUBt
+YW5kcml2YS5jb20+iGAEExECACACGyMCHgECF4AFAkSzgjYGCwkIBwMCBBUCCAME
+FgIDAQAKCRDniYrgcHcf8wyaAKCto5VYq0f6E2j38kmHHIHtt3kDPACfcBMF10vD
+WHonOjbuIcJYgB8FNfW5AQ0EPI92khAEAO0Z8ZajF6sP96BhmeAw/SPn9WXt0aya
+o37JA8a+Lo74exNqTEa7pF8/sb+hHcigH9y7VeZtMlhz+dzq9Xndb1sq05zjlCMX
+Cyd6Z0ssUxRHpwgAs9cFsn8kBI5elbdJDJG/rt0IC+hRTP/YdR+x8gzm81vbk7e8
+ff8XAbc7dtOLAAMFBACFt8q5+0x1X5NPvwqkq6UKOR9YJmATCt87pRZ1XCQlk9Vy
+MyjC7ArSJQpwEC2KB6O9dQXs0Pm3AAVvjbXoMQKuCKyV3C8ubcDeHHsT/uA1aLLp
+LhjGX1bsvIuV22KmR4FO6Vg1N2g95PbeEPhzvAro4xofEbt8i50ifDpo61nv/ohG
+BBgRAgAGBQI8j3aSAAoJEOeJiuBwdx/zKfYAn1D3J8iqWuAwExEf3ygyBbQP1oVM
+AKCxXARykgrcQ1Ng56HO0o8Z25kHgZkBogRHllszEQQA1PJdiHJEs4HVEtXZ9FjD
+O8FyuLHqBbBx0MP9LpxwkAsacgzJ50ylfOqeSk20dRQ3XmmZWNMglfiBrIiMS8rl
+HSZ/L7gOYAOWZFK/Pfx7VmnmVZe+yRyEMb4Q0QVgjjuEDba2WFrxm75fN8Jf8zn3
+SxLPCBhJbdC4H6IckwNan68AoPx2nLQw7HF7D5aOSXpP/t0R1Xg9A/99K4gqX6Sy
+Tpoa2i4NRG1rBNJjfSELJflgz3aGKGwe+s+nh93t/OtfFsz/y0xuFrlW8KUkqpl8
+n3VaV8VsxEn0/KtAfAhqR3/0mv0/y3pqIZkZy75H/7QH8RZgzJtYznsCSBPUt+Yw
+DRh1SXMZn0TaT1gu7h56o6KZ7Jg5C5DBHQQAumHKQznDCWfbCW7fyTWQWqE+QpbY
+DA5UjedLdr/yeXpvKjC6gkjReVCcnSxZ3jx0x8uoFY1m6nAtJ3BczqjNcuuOFKew
+aCkgK9YUU5ejMPUt1H2WWdGpdybVrfMtHhSFt0D+usNQC9Cztm2TAbjr4W+tw17+
+Yh6K3MZNMiBW28m0Qm9wZW5TVVNFOkZhY3RvcnkgT0JTIFByb2plY3QgPG9wZW5T
+VVNFOkZhY3RvcnlAYnVpbGQub3BlbnN1c2Uub3JnPohGBBMRAgAGBQJHllszAAoJ
+EDswEbdrnWUjs98AoJz1m0p8jkb+DrpXulIxeH2CL7KSAKCK898YwOH0chE7AEi5
+2jaoYiY6oohmBBMRAgAmBQJHllszAhsDBQkEHrAABgsJCAcDAgQVAggDBBYCAwEC
+HgECF4AACgkQHHIcJFa0F3rwbACgjnknJ5R9n1lQSRh0zlSdzOMgagwAmwXjZD7l
+qNOQDkNzNXSxNYZDd+hvmQGiBEeWW0IRBACKYbOW1tQdRc43WijD01BiO1ko6QiF
+SR/JgYjg97fCTUFNBpuB4PwAXvPzVXnurdpMNeuAADuvBkCggvf/PhQ/tf1nd89M
+xuki2a5mh3LACR4xolr3ADThw+a2HxEuZMmElqOHuXKEFHBzSSOtEaDh4tkETTIQ
+U0CDKpeAgRR63wCgx+M+4xZH2HuPGuEtI5oqhfLMgacD/R6KlLjnNHsLaNOSE7ZG
+hZXzzMLo6gMnJxQiIHjmz3UY6I/i1Kp9KMlg2C/YTVKDtAZY5uTqdL1Dwcr2W8//
+Qy3NoXWnfoXCeBCq1Q9HkfySqGHlLiIatuxgoLe1P07FhM+XLXZZq1iidsLpJS6j
+p76d1rCjMF9J1nFuj54r5BhQA/9JhpBautBvPg05JZtAMkv6cBkIOWohaJpDz5qw
+9HeZZPg7ex+uLcd3svZNMwppoc/Aiw28W1n8sYWcV9FiikSsTT4Amt7DGzBtHDJN
+HoOZAlt8qC4qzxEpJtBZFjPmqbmpKNICmo5Vtk+HoI92NYDcrr8+jKnrIm7GFfWV
+3qU4arRCc2VjdXJpdHk6cHJpdmFjeSBPQlMgUHJvamVjdCA8c2VjdXJpdHk6cHJp
+dmFjeUBidWlsZC5vcGVuc3VzZS5vcmc+iEYEExECAAYFAkeWW0IACgkQOzARt2ud
+ZSPU0wCgrEAM1bo9oS/OmPaFP6KgvRCbHkcAniN7gjcHPUwgkE9kTrWBDHSO1ljg
+iGYEExECACYFAkeWW0ICGwMFCQQesAAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK
+CRAADEtt49vTzVkAAJ93KK7XuMaLCqZnkd3rqnX1Y1b/xQCgk7xnntv+732TCrZH
+k/oVP1VewlU=
+=zobT
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/directory.yast b/tests/repo/susetags/data/addon_in_subdir/updates/directory.yast
new file mode 100644 (file)
index 0000000..b2c1c89
--- /dev/null
@@ -0,0 +1,2 @@
+media.1/
+suse/
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/license.tar.gz b/tests/repo/susetags/data/addon_in_subdir/updates/license.tar.gz
new file mode 100644 (file)
index 0000000..5d513d1
Binary files /dev/null and b/tests/repo/susetags/data/addon_in_subdir/updates/license.tar.gz differ
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/i586/SHA1SUMS b/tests/repo/susetags/data/addon_in_subdir/updates/suse/i586/SHA1SUMS
new file mode 100644 (file)
index 0000000..8b8b046
--- /dev/null
@@ -0,0 +1 @@
+c8db67006900a7758dda69efee189c5eb39d206c  dnsmasq-2.46-1.i586.rpm
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/SHA1SUMS b/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/SHA1SUMS
new file mode 100644 (file)
index 0000000..b5fa0b4
--- /dev/null
@@ -0,0 +1,4 @@
+965ba5faeea815d41ba308ffd193b78505b26c1c  directory.yast
+3d0cdf34257cc2c54929bf5414969142cad1d421  packages
+2d68731ab2f3c86b81f7672798f871cf96b2d0fc  packages.DU
+07c36c760ed0c58b58d6d8fcfdd2717df02c4256  packages.en
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/directory.yast b/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/directory.yast
new file mode 100644 (file)
index 0000000..09276e7
--- /dev/null
@@ -0,0 +1,4 @@
+directory.yast
+packages
+packages.DU
+packages.en
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages b/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages
new file mode 100644 (file)
index 0000000..d313420
--- /dev/null
@@ -0,0 +1,63 @@
+=Ver: 2.0
+##----------------------------------------
+=Pkg: dnsmasq 2.46 1 i586
++Req:
+/usr/sbin/useradd
+fillup
+coreutils
+grep
+diffutils
+insserv
+sed
+/bin/mkdir
+/bin/sh
+/bin/sh
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+/bin/sh
+libc.so.6
+libc.so.6(GLIBC_2.0)
+libc.so.6(GLIBC_2.1)
+libc.so.6(GLIBC_2.2)
+libc.so.6(GLIBC_2.3)
+libc.so.6(GLIBC_2.3.4)
+libc.so.6(GLIBC_2.4)
+rpmlib(PayloadIsLzma) <= 4.4.2-1
+-Req:
++Prq:
+/usr/sbin/useradd
+fillup
+coreutils
+grep
+diffutils
+insserv
+sed
+/bin/mkdir
+/bin/sh
+/bin/sh
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsLzma) <= 4.4.2-1
+-Prq:
++Prv:
+dns_daemon
+dnsmasq = 2.46-1
+/etc/dnsmasq.conf
+/etc/init.d/dnsmasq
+/etc/slp.reg.d
+/etc/slp.reg.d/dnsmasq.reg
+/etc/sysconfig/SuSEfirewall2.d/services/dnsmasq-dhcp
+/etc/sysconfig/SuSEfirewall2.d/services/dnsmasq-dns
+/usr/sbin/dnsmasq
+/usr/sbin/rcdnsmasq
+-Prv:
+=Grp: Productivity/Networking/DNS/Servers
+=Lic: GPL v2 or later
+=Src: dnsmasq 2.46 1 src
+=Tim: 1226926770
+=Loc: 1 dnsmasq-2.46-1.i586.rpm
+=Siz: 290023 826308
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.DU b/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.DU
new file mode 100644 (file)
index 0000000..054d150
--- /dev/null
@@ -0,0 +1,58 @@
+=Ver: 2.0
+##----------------------------------------
+=Pkg: dnsmasq 2.46 1 i586
++Dir:
+/ 0 840 0 61
+etc/ 19 6 1 4
+etc/init.d/ 3 0 1 0
+etc/slp.reg.d/ 1 0 1 0
+etc/sysconfig/ 0 2 0 2
+etc/sysconfig/SuSEfirewall2.d/ 0 2 0 2
+etc/sysconfig/SuSEfirewall2.d/services/ 2 0 2 0
+usr/ 0 815 0 56
+usr/sbin/ 149 0 1 0
+usr/share/ 0 666 0 55
+usr/share/doc/ 0 476 0 42
+usr/share/doc/packages/ 0 476 0 42
+usr/share/doc/packages/dnsmasq/ 188 288 7 35
+usr/share/doc/packages/dnsmasq/contrib/ 0 288 0 35
+usr/share/doc/packages/dnsmasq/contrib/Solaris10/ 4 0 2 0
+usr/share/doc/packages/dnsmasq/contrib/Suse/ 10 0 5 0
+usr/share/doc/packages/dnsmasq/contrib/dns-loc/ 17 0 2 0
+usr/share/doc/packages/dnsmasq/contrib/dnslist/ 29 0 3 0
+usr/share/doc/packages/dnsmasq/contrib/dnsmasq_MacOSX/ 4 0 3 0
+usr/share/doc/packages/dnsmasq/contrib/dynamic-dnsmasq/ 8 0 1 0
+usr/share/doc/packages/dnsmasq/contrib/openvpn/ 7 0 3 0
+usr/share/doc/packages/dnsmasq/contrib/port-forward/ 4 0 2 0
+usr/share/doc/packages/dnsmasq/contrib/slackware-dnsmasq/ 6 0 5 0
+usr/share/doc/packages/dnsmasq/contrib/try-all-ns/ 4 0 2 0
+usr/share/doc/packages/dnsmasq/contrib/webmin/ 174 0 2 0
+usr/share/doc/packages/dnsmasq/contrib/wrt/ 21 0 5 0
+usr/share/locale/ 0 128 0 10
+usr/share/locale/de/ 0 3 0 1
+usr/share/locale/de/LC_MESSAGES/ 3 0 1 0
+usr/share/locale/es/ 0 23 0 1
+usr/share/locale/es/LC_MESSAGES/ 23 0 1 0
+usr/share/locale/fi/ 0 1 0 1
+usr/share/locale/fi/LC_MESSAGES/ 1 0 1 0
+usr/share/locale/fr/ 0 24 0 1
+usr/share/locale/fr/LC_MESSAGES/ 24 0 1 0
+usr/share/locale/id/ 0 14 0 1
+usr/share/locale/id/LC_MESSAGES/ 14 0 1 0
+usr/share/locale/it/ 0 1 0 1
+usr/share/locale/it/LC_MESSAGES/ 1 0 1 0
+usr/share/locale/nb/ 0 14 0 1
+usr/share/locale/nb/LC_MESSAGES/ 14 0 1 0
+usr/share/locale/pl/ 0 32 0 1
+usr/share/locale/pl/LC_MESSAGES/ 32 0 1 0
+usr/share/locale/pt_BR/ 0 1 0 1
+usr/share/locale/pt_BR/LC_MESSAGES/ 1 0 1 0
+usr/share/locale/ro/ 0 15 0 1
+usr/share/locale/ro/LC_MESSAGES/ 15 0 1 0
+usr/share/man/ 0 62 0 3
+usr/share/man/es/ 0 21 0 1
+usr/share/man/es/man8/ 21 0 1 0
+usr/share/man/fr/ 0 22 0 1
+usr/share/man/fr/man8/ 22 0 1 0
+usr/share/man/man8/ 19 0 1 0
+-Dir:
diff --git a/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.en b/tests/repo/susetags/data/addon_in_subdir/updates/suse/setup/descr/packages.en
new file mode 100644 (file)
index 0000000..0c7748f
--- /dev/null
@@ -0,0 +1,20 @@
+=Ver: 2.0
+##----------------------------------------
+=Pkg: dnsmasq 2.46 1 i586
+=Sum: Lightweight, Easy-to-Configure DNS Forwarder and DHCP Server
++Des:
+Dnsmasq is a lightweight, easy-to-configure DNS forwarder and DHCP
+server. It is designed to provide DNS and, optionally, DHCP, to a small
+network. It can serve the names of local machines that are not in the
+global DNS. The DHCP server integrates with the DNS server and allows
+machines with DHCP-allocated addresses to appear in DNS with names
+configured either in each host or in a central configuration file.
+Dnsmasq supports static and dynamic DHCP leases and BOOTP for network
+booting of diskless machines.
+
+
+
+Authors:
+--------
+    simon@thekelleys.org.uk
+-Des:
diff --git a/tests/repo/susetags/data/shared_attributes/content b/tests/repo/susetags/data/shared_attributes/content
new file mode 100644 (file)
index 0000000..75955ab
--- /dev/null
@@ -0,0 +1,30 @@
+PRODUCT openSUSE-factory
+VERSION 10.3
+DISTPRODUCT SuSE-Linux-STABLE-X86
+DISTVERSION 10.2.42-factory
+VENDOR SUSE LINUX Products GmbH, Nuernberg, Germany
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/FACTORY/release-notes.rpm
+ARCH.x86_64 x86_64 i686 i586 i486 i386 noarch
+ARCH.i686 i686 i586 i486 i386 noarch
+ARCH.i586 i586 i486 i386 noarch
+ARCH.i486 i486 i386 noarch
+ARCH.i386 i386 noarch
+DEFAULTBASE i586
+REQUIRES openSUSE-release = 10.3 pattern:basesystem
+PROVIDES product:openSUSE = 10.2.42
+OBSOLETES product:SUSE_LINUX product:openSUSE <= 10.2
+LINGUAS cs da de en en_GB es fi fr hu it ja km nl nb pl pt_BR zh_CN zh_TW
+SHORTLABEL FACTORY
+LABEL openSUSE FACTORY 10.3
+LABEL.de openSUSE FACTORY 10.3
+EXTRAURLS http://download.opensuse.org/distribution/10.2/repo/oss/
+OPTIONALURLS http://download.opensuse.org/distribution/10.2/repo/non-oss/ http://download.opensuse.org/distribution/10.2/repo/debug/
+DESCRDIR suse/setup/descr
+DATADIR suse
+FLAGS update
+LANGUAGE en_US
+META SHA1 cec02abacb62fce2964a787d516879822691be6e  packages
+META SHA1 2664997a3bae2679cf1395a81d54a7dd4532c276  packages.DU
+META SHA1 1c539c3fe4a8ec5fbe96ce3b559fc952124f9acd  packages.en
+
+
diff --git a/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages b/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages
new file mode 100644 (file)
index 0000000..231681b
--- /dev/null
@@ -0,0 +1,32 @@
+##----------------------------------------
+=Pkg: foo 1.0 1 i586
+=Cks: SHA1 05f0647241433d01636785fd282cc824a6527269
++Req:
+bar > 1.0
+-Req:
+=Grp: Bar
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: foo 1.0 1 src
+=Tim: 1183399094
+=Loc: 1 foo-1.0-1.i586.rpm
+=Siz: 16356019 38850584
++Aut:
+Foo Bar <foo@bar.org>
+-Aut:
+##----------------------------------------
+=Pkg: foo 1.0 1 x86_64
+=Cks: SHA1 05f0647241433d01636785fd282cc824a6527269
++Req:
+bar > 1.0
+-Req:
+=Grp: Bar
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: foo 1.0 1 src
+=Tim: 1183399094
+=Loc: 1 foo-1.0-1.x86_64.rpm
+=Siz: 16356019 38850584
++Aut:
+Foo Bar <foo@bar.org>
+-Aut:
+=Shr: foo 1.0 1 i586
+
diff --git a/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.DU b/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.DU
new file mode 100644 (file)
index 0000000..f8a911b
--- /dev/null
@@ -0,0 +1,7 @@
+##----------------------------------------
+=Pkg: foo 1.0 1 i586
++Dir:
+/ 0 39444 0 3068
+usr/share/ 11 0 1 0
+-Dir:
+##----------------------------------------
diff --git a/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.en b/tests/repo/susetags/data/shared_attributes/suse/setup/descr/packages.en
new file mode 100644 (file)
index 0000000..5021a00
--- /dev/null
@@ -0,0 +1,7 @@
+##----------------------------------------
+=Pkg: foo 1.0 1 i586
+=Sum: Foo program
++Des:
+This is the description
+-Des:
+##----------------------------------------
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/content b/tests/repo/susetags/data/stable-x86-subset-gz/content
new file mode 100644 (file)
index 0000000..9c02308
--- /dev/null
@@ -0,0 +1,42 @@
+PRODUCT openSUSE-factory
+VERSION 10.3
+DISTPRODUCT SuSE-Linux-STABLE-X86
+DISTVERSION 10.2.42-factory
+VENDOR SUSE LINUX Products GmbH, Nuernberg, Germany
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/FACTORY/release-notes.rpm
+ARCH.x86_64 x86_64 i686 i586 i486 i386 noarch
+ARCH.i686 i686 i586 i486 i386 noarch
+ARCH.i586 i586 i486 i386 noarch
+ARCH.i486 i486 i386 noarch
+ARCH.i386 i386 noarch
+DEFAULTBASE i586
+REQUIRES openSUSE-release = 10.3 pattern:basesystem
+PROVIDES product:openSUSE = 10.2.42
+OBSOLETES product:SUSE_LINUX product:openSUSE <= 10.2
+LINGUAS cs da de en en_GB es fi fr hu it ja km nl nb pl pt_BR zh_CN zh_TW
+SHORTLABEL FACTORY
+LABEL openSUSE FACTORY 10.3
+LABEL.de openSUSE FACTORY 10.3
+EXTRAURLS http://download.opensuse.org/distribution/10.2/repo/oss/
+OPTIONALURLS http://download.opensuse.org/distribution/10.2/repo/non-oss/ http://download.opensuse.org/distribution/10.2/repo/debug/
+DESCRDIR suse/setup/descr
+DATADIR suse
+FLAGS update
+LANGUAGE en_US
+META SHA1 3b3a4b0f085dd3605b61cdec06783b6b2a1f1d61  kde-10.3-71.i586.pat.gz
+META SHA1 2c351e103b347ef2984cb2aa638accfeadfaef58  packages.DU.gz
+META SHA1 208a6f8e1c96e9ba2ea7c74c05113c11c7378823  packages.en.gz
+META SHA1 59d65b8575ba1edde1813fef2e9949ae85f4d4c1  packages.es.gz
+META SHA1 061c361edf6157bc4273872055822e9d1766b8c4  packages.gz
+META SHA1 14373553814a3f83c2dce6c6da40740ae03b3065  patterns.gz
+KEY SHA1 c0354069c10819674da8706822e1d4bd0c1797e9  gpg-pubkey-0dfb3188-41ed929b.asc
+KEY SHA1 2e38e503c436c5d002bdc31755c82188044d9d21  gpg-pubkey-307e3d54-44201d5d.asc
+KEY SHA1 7025932e6866932f489421990075f3ed312023ea  gpg-pubkey-3d25d3d9-36e12d04.asc
+KEY SHA1 fd6146cac8c1473c5b52548936de773d5bbd5610  gpg-pubkey-7e2e3b05-44748aba.asc
+KEY SHA1 cd7adceba1fe5d7ba27b5749718743192d82f802  gpg-pubkey-9c800aca-40d8063e.asc
+KEY SHA1 7535d79e31ef7b4232e5593bb49d9142978b2e95  gpg-pubkey-a1912208-446a0899.asc
+HASH SHA1 172b3cf77268f46e783e78a653902e563cb91e9a  license.tar.gz
+HASH SHA1 4aa8ded6302e6ec85690a51af6044dffe9b21923  control.xml
+HASH SHA1 82f1f17ce74f0cd3fca4813c178196b317fc952d  installation.xml
+HASH SHA1 68d9b548d61e31e82e8834690e884afa27751287  media.1/info.txt
+
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/content.asc b/tests/repo/susetags/data/stable-x86-subset-gz/content.asc
new file mode 100644 (file)
index 0000000..989ecc3
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.9.22 (GNU/Linux)
+
+iD8DBQBGzZ7Vm+zCtd2wN1YRAvrYAJ4or9MtpTiRk57EiGkd3YBvgoIN5QCfX969
+j0d5j3iteCP3nR/N39nrdMo=
+=w2Lh
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/content.key b/tests/repo/susetags/data/stable-x86-subset-gz/content.key
new file mode 100644 (file)
index 0000000..8e52028
--- /dev/null
@@ -0,0 +1,24 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+=5nY9
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/control.xml b/tests/repo/susetags/data/stable-x86-subset-gz/control.xml
new file mode 100644 (file)
index 0000000..096763e
--- /dev/null
@@ -0,0 +1,793 @@
+<?xml version="1.0"?>
+<productDefines  xmlns="http://www.suse.com/1.0/yast2ns"
+    xmlns:config="http://www.suse.com/1.0/configns">
+
+<!--
+Work around for the text domain
+textdomain="control"
+-->
+
+    <textdomain>control</textdomain>
+    <globals>
+       <additional_kernel_parameters></additional_kernel_parameters>
+        <enable_autologin config:type="boolean">true</enable_autologin>
+       <enable_firewall config:type="boolean">true</enable_firewall>
+       <firewall_enable_ssh config:type="boolean">false</firewall_enable_ssh>
+       <fam_local_only>never</fam_local_only>
+        <incomplete_translation_treshold>95</incomplete_translation_treshold>
+        <inform_about_suboptimal_distribution config:type="boolean">true</inform_about_suboptimal_distribution>
+        <skip_language_dialog config:type="boolean">true</skip_language_dialog>
+        <ui_mode>simple</ui_mode>
+        <enable_clone config:type="boolean">false</enable_clone>
+        <enable_register_hwdata   config:type="boolean">true</enable_register_hwdata>
+        <enable_register_optional config:type="boolean">true</enable_register_optional>
+        <display_register_forcereg config:type="boolean">true</display_register_forcereg>
+        <disable_register_w3m config:type="boolean">true</disable_register_w3m>
+        <register_monthly config:type="boolean">false</register_monthly>
+       <manual_online_update config:type="boolean">true</manual_online_update>
+       <root_password_ca_check config:type="boolean">false</root_password_ca_check>
+
+       <!-- FATE #301937, Save /root content from the installation system to the installed system -->
+       <save_instsys_content config:type="list">
+           <save_instsys_item>
+               <instsys_directory>/root/</instsys_directory>
+               <system_directory>/root/inst-sys/</system_directory>
+           </save_instsys_item>
+       </save_instsys_content>
+    </globals>
+
+    <software>
+        <delete_old_packages config:type="boolean">false</delete_old_packages>
+        <selection_type config:type="symbol">auto</selection_type>
+
+       <!-- FATE #300898, List of external sources accesible during the installation time -->
+       <external_sources_link>http://download.opensuse.org/YaST/Repos/openSUSE_103_Servers.xml</external_sources_link>
+    </software>
+
+    <partitioning>
+        <evms_config config:type="boolean">false</evms_config>
+        <try_separate_home config:type="boolean">true</try_separate_home>
+        <limit_try_home>7G</limit_try_home>
+        <root_space_percent>40</root_space_percent>
+        <root_base_size>5G</root_base_size>
+        <root_max_size>20G</root_max_size>
+       <proposal_lvm>false</proposal_lvm>
+       <proposal_evms>false</proposal_evms>
+       <vm_desired_size>15G</vm_desired_size>
+       <vm_home_max_size>25G</vm_home_max_size>
+       <boot_evms>false</boot_evms>
+    </partitioning>
+
+    <network>
+        <force_static_ip config:type="boolean">false</force_static_ip>
+        <network_manager>laptop</network_manager>
+       <startmode>ifplugd</startmode>
+    </network>
+
+    <clone_modules config:type="list">
+       <clone_module>language</clone_module>
+       <clone_module>keyboard</clone_module>
+       <clone_module>timezone</clone_module>
+       <clone_module>users</clone_module>
+       <clone_module>networking</clone_module>
+       <clone_module>firewall</clone_module>
+       <clone_module>host</clone_module>
+       <clone_module>routing</clone_module>
+       <clone_module>proxy</clone_module>
+       <clone_module>runlevel</clone_module>
+       <clone_module>x11</clone_module>
+       <clone_module>nis</clone_module>
+       <clone_module>ldap</clone_module>
+       <clone_module>printer</clone_module>
+       <clone_module>add-on</clone_module>
+    </clone_modules>
+
+    <texts>
+      <congratulate>
+       <label>
+&lt;p&gt;&lt;b&gt;Congratulations!&lt;/b&gt;&lt;/p&gt;
+&lt;p&gt;The installation of openSUSE on your machine is complete.
+After clicking &lt;b&gt;Finish&lt;/b&gt;, you can log in to the system.&lt;/p&gt;
+&lt;p&gt;Visit us at %1.&lt;/p&gt;
+&lt;p&gt;Have a lot of fun!&lt;br&gt;Your openSUSE Development Team&lt;/p&gt;
+       </label>
+      </congratulate>
+    </texts>
+
+    <proposals config:type="list">
+        <proposal>
+            <label>Installation Settings</label>
+            <mode>installation,demo,autoinstallation</mode>
+            <stage>initial</stage>
+            <name>initial</name>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>hwinfo</proposal_module>
+                <proposal_module>keyboard</proposal_module>
+                <proposal_module>mouse</proposal_module>
+                <proposal_module>partitions</proposal_module>
+                <proposal_module>software</proposal_module>
+                <proposal_module>bootloader</proposal_module>
+                <proposal_module>timezone</proposal_module>
+                <proposal_module>language</proposal_module>
+                <proposal_module>runlevel</proposal_module>
+            </proposal_modules>
+            <proposal_tabs config:type="list">
+                <proposal_tab>
+                    <label>Overview</label>
+                    <proposal_modules config:type="list">
+                        <proposal_module>partitions</proposal_module>
+                        <proposal_module>software_simple</proposal_module>
+                       <proposal_module>country_simple</proposal_module>
+                    </proposal_modules>
+                </proposal_tab>
+                <proposal_tab>
+                    <label>Expert</label>
+                    <proposal_modules config:type="list">
+                        <proposal_module>hwinfo</proposal_module>
+                        <proposal_module>keyboard</proposal_module>
+                        <proposal_module>mouse</proposal_module>
+                        <proposal_module>partitions</proposal_module>
+                        <proposal_module>software</proposal_module>
+                        <proposal_module>bootloader</proposal_module>
+                        <proposal_module>timezone</proposal_module>
+                        <proposal_module>language</proposal_module>
+                        <proposal_module>runlevel</proposal_module>
+                    </proposal_modules>
+                </proposal_tab>
+            </proposal_tabs>
+        </proposal>
+
+        <proposal>
+            <label>Update Settings</label>
+            <mode>update</mode>
+            <name>initial</name>
+            <stage>normal</stage>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>update</proposal_module>
+                <proposal_module>packages</proposal_module>
+                <proposal_module>backup</proposal_module>
+                <proposal_module>language</proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Installation Settings</label>
+            <mode>update</mode>
+            <stage>initial</stage>
+            <name>initial</name>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+               <proposal_module>hwinfo</proposal_module>
+               <proposal_module>media</proposal_module>
+<!-- disabled due to bug 165832
+                <proposal_module>rootpart</proposal_module>
+-->
+                <proposal_module>update</proposal_module>
+                <proposal_module>packages</proposal_module>
+                <proposal_module>backup</proposal_module>
+                <proposal_module>language</proposal_module>
+                <proposal_module>keyboard</proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Network Configuration</label>
+            <name>network</name>
+            <stage>continue,normal</stage>
+            <enable_skip>yes</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>
+                   <name>lan</name>
+                   <presentation_order>20</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>general</name>
+                   <presentation_order>5</presentation_order>
+               </proposal_module>
+               <proposal_module>
+                   <name>dsl</name>
+                   <presentation_order>30</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>isdn</name>
+                   <presentation_order>40</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>modem</name>
+                   <presentation_order>50</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>remote</name>
+                   <presentation_order>60</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>firewall</name>
+                   <presentation_order>10</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>proxy</name>
+                   <presentation_order>70</presentation_order>
+               </proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Hardware Configuration</label>
+            <name>hardware</name>
+            <stage>continue</stage>
+            <enable_skip>yes</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>x11</proposal_module>
+                <proposal_module>printer</proposal_module>
+                <proposal_module>sound</proposal_module>
+                <proposal_module>tv</proposal_module>
+                <proposal_module>bluetooth</proposal_module>
+            </proposal_modules>
+        </proposal>
+    </proposals>
+
+    <!-- Stage: Initial, Mode: Installation -->
+    <workflows config:type="list">
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+           <label>Preparation</label>
+            <mode>installation</mode>
+            <stage>initial</stage>
+            <modules  config:type="list">
+                <module>
+                    <name>language</name>
+                   <label>Language</label>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Language</label>
+                    <name>checkmedia</name>
+                </module>
+                <module>
+                   <label>License Agreement</label>
+                    <name>license</name>
+                </module>
+               <module>
+                   <label>Disk Activation</label>
+                    <name>disks_activate</name>
+               </module>
+                <module>
+                   <label>System Analysis</label>
+                    <name>system_analysis</name>
+                </module>
+               <!-- Here, user selects whether to perform New Installation or Upgrade -->
+               <module>
+                   <label>Online Repositories</label>
+                   <name>productsources</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <label>Add-On Products</label>
+                   <name>add-on</name>
+                   <enable_back>yes</enable_back>
+               </module>
+                <module>
+                   <label>Time Zone</label>
+                    <name>timezone</name>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                   <enable_back>yes</enable_back>
+                </module>
+                <module>
+                   <label>Desktop Selection</label>
+                    <name>desktop</name>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Installation</label>
+               </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_next>no</enable_next>
+                   <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>finish</name>
+                   <enable_back>no</enable_back>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: Update -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+           <label>Preparation</label>
+            <mode>update</mode>
+            <stage>initial</stage>
+            <modules  config:type="list">
+                <module>
+                    <name>language</name>
+                   <label>Language</label>
+                    <enable_back>no</enable_back>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Language</label>
+                    <name>checkmedia</name>
+                </module>
+                <module>
+                   <label>License Agreement</label>
+                    <name>license</name>
+                </module>
+               <module>
+                   <label>Disk Activation</label>
+                    <name>disks_activate</name>
+               </module>
+                <module>
+                   <label>System Analysis</label>
+                    <name>system_analysis</name>
+                </module>
+                <module>
+                   <label>System for Update</label>
+                    <name>update_partition</name>
+                </module>
+               <module>
+                   <name>upgrade_urls</name>
+               </module>
+               <module>
+                   <label>Online Repositories</label>
+                   <name>productsources</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <label>Add-On Products</label>
+                   <name>add-on</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Update</label>
+               </module>
+                <module>
+                   <label>Update Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>finish</name>
+                   <enable_back>no</enable_back>
+                </module>
+            </modules>
+        </workflow>
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+            </defaults>
+            <stage>initial</stage>
+           <label>Preparation</label>
+            <mode>repair</mode>
+            <modules  config:type="list">
+                <module>
+                   <label>System Information</label>
+                    <name>info</name>
+                </module>
+                <module>
+                   <label>Perform Repair</label>
+                    <name>repair</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: ScreenShot -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+            <stage>initial</stage>
+           <label>Base Installation</label>
+            <mode>screen_shot</mode>
+            <modules  config:type="list">
+                <module>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                   <label>Language</label>
+                    <name>language</name>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>yes</enable_next>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: Demo -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+            <stage>initial</stage>
+           <label>Base Installation</label>
+            <mode>demo</mode>
+            <modules  config:type="list">
+                <module>
+                    <enable_back>no</enable_back>
+                   <label>Language</label>
+                    <name>language</name>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                   <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: Update -->
+        <workflow>
+            <stage>continue</stage>
+            <mode>update</mode>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>yes</enable_back>
+                <enable_next>yes</enable_next>
+            </defaults>
+            <modules config:type="list">
+                <module>
+                   <label>Perform Update</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>Network</label>
+                    <name>ask_net_test</name>
+                   <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>do_net_test</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>addon_update_sources</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>suse_register</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>ask_online_update</name>
+                </module>
+                <module>
+                   <label>Online Update</label>
+                    <name>you</name>
+                </module>
+               <module>
+                   <label>Online Update</label>
+                   <name>restore_settings</name>
+               </module>
+                <module>
+                    <name>suseconfig</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Release Notes</label>
+                    <name>release_notes</name>
+                </module>
+                <module>
+                    <name>congratulate</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: Installation -->
+        <workflow>
+            <stage>continue</stage>
+            <mode>installation</mode>
+            <defaults>
+                <enable_back>yes</enable_back>
+                <enable_next>yes</enable_next>
+                <archs>all</archs>
+            </defaults>
+            <modules  config:type="list">
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>root Password</label>
+                    <name>root</name>
+                    <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>initialization</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>netprobe</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>rpmcopy</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Hostname</label>
+                    <name>hostname</name>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>proposal</name>
+                    <proposal>network</proposal>
+                </module>
+               <module>
+                   <label>Network</label>
+                    <name>fam</name>
+               </module>
+                <module>
+                   <label>Network</label>
+                    <name>ask_net_test</name>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>do_net_test</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>addon_update_sources</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>suse_register</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>ask_online_update</name>
+                </module>
+                <module>
+                   <label>Online Update</label>
+                    <name>you</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>extrasources</name>
+                </module>
+               <module>
+                   <label>Online Update</label>
+                   <name>restore_settings</name>
+               </module>
+                <module>
+                   <label>Users</label>
+                    <name>auth</name>
+                </module>
+                <module>
+                   <label>Users</label>
+                    <name>user</name>
+                </module>
+                <module>
+                    <name>suseconfig</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Release Notes</label>
+                    <name>release_notes</name>
+                </module>
+                <module>
+                   <label>Hardware Configuration</label>
+                    <name>proposal</name>
+                    <proposal>hardware</proposal>
+                </module>
+                <module>
+                   <label>Hardware Configuration</label>
+                   <name>save_hardware_status</name>
+                </module>
+                <module>
+                    <name>congratulate</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: AutoInstallation -->
+        <workflow>
+            <stage>initial</stage>
+            <label>Base Installation</label>
+            <mode>autoinstallation</mode>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <modules  config:type="list">
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>autoinit</name>
+                    <archs>all</archs>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>autosetup</name>
+                </module>
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>autoimage</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>finish</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: AutoInstallation -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <stage>continue</stage>
+            <mode>autoinstallation</mode>
+            <modules  config:type="list">
+                <module>
+                   <label>Perform Installation</label>
+                    <name>netprobe</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>autopost</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>System Configuration</label>
+                    <name>autoconfigure</name>
+                </module>
+                <module>
+                    <name>suseconfig</name>
+                </module>
+            </modules>
+        </workflow>
+    </workflows>
+</productDefines>
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-0dfb3188-41ed929b.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-0dfb3188-41ed929b.asc
new file mode 100644 (file)
index 0000000..89fc35c
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBEHtkpsRBACRHiXh3olS++6/Mp9N7ByGMmjaaE+Y8cJQLUPG1myrbW5aogIP
+0WenayhGbbgOHNWgd5dQ8KQpYYFoQuUHjFYzj5MvgrdOENOvD7ZNJ6+EmbkNh5cV
+zUYfNG9jdiGweZkyA1sh8DYS0JiUmQ4CzaBD/DotB/dCmDcyuNQFiw4qKwCglQah
+ATyueBRsOiXl0NIs1uB6dkkD/1A2YmQ6te1q38a1J+a8os6bDlMZhVnkZdhJdw6x
+eBwUb9XS0n7hyt/AKCcBnrDEUQJuhBMNgzctJvbuMv27yRMANAXZDQkp0ip/yHLJ
+PhUdSNTTRHOL9bV3t+JuZ9xmuclprwyrrJYUkEESXNc0tkuczHBP2c/RqA3OxYHt
+hrHLA/9Pqe2gEleeo8l26u/uFXs2dtwjh8EZmdhHoqGcOlpYR4DyAg2D+jYfh3RI
+oPzIwRlHVUR1ii5h8iPi98BVuEvukwfbbQ1K22Jwzxt6w3ihCXBKWKbeC3ElIMfA
+hVMchLFUbTAw+yodO/u3NHxKQ34+ginid9dVyxV5T0gpDEEHObQrT3BlbiBFbnRl
+cnByaXNlIFNlcnZlciA8c3VwcG9ydEBub3ZlbGwuY29tPoheBBMRAgAeBQJB7ZKb
+AhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEHPSXWMN+zGID4oAoJPTGZbZApW+
+tuU422mHYGwoqgjrAJ9fhzRhRbV3YsOxKUomNeuIfmWGXA==
+=Qv5+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-307e3d54-44201d5d.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-307e3d54-44201d5d.asc
new file mode 100644 (file)
index 0000000..2d21c1b
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mIsERCAdXQEEAL7MrBTz+3SBWpCm2ae2yaDqV3ezQcs2JlvqidJVhsZqQe9/jkxi
+KTEQW5+TXF/+BlQSiebunRI7oo3+9U8GyRCgs1sf+yRQWMLzZqRaarzRhw9w+Ihl
+edtqYl6/U2JZCb8Adp6d7RzlRliJdJ/VtsfXj2ef7Dwu7elOVSsmaBdtAAYptChT
+dVNFIFBhY2thZ2UgU2lnbmluZyBLZXkgPGJ1aWxkQHN1c2UuZGU+iLgEEwECACIF
+AkQgHV0CGwMFCQQ9AoAECwcDAgMVAgMDFgIBAh4BAheAAAoJEOOlw2Awfj1UjUIE
+AIf3SLlrfj2RsCDjyYThXen+A/WTYDPbY+NYmmVvFQilHNQY9ZrJ5cNohRQu6hA+
+Sccrf11Uy24tTHWSTzuG9VzFeeIAcIU02XHar0w3QbvTk6IqeG+OZlfOGJj1sdx4
+JKwpwk9mSdrq2ELhrkPZiVWS7RmRkPr2klwYgKGWbmOJ
+=ZmDA
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-3d25d3d9-36e12d04.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-3d25d3d9-36e12d04.asc
new file mode 100644 (file)
index 0000000..894a463
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQENAzbhLQQAAAEIAKAkXHe0lWRBXLpn38hMHy03F0I4Sszmoc8aaKJrhfhyMlOA
+BqvklPLE2f9UrI4Xc860gH79ZREwAgPt0pi6+SleNFLNcNFAuuHMLQOOsaMFatbz
+JR9i4m/lf6q929YROu5zB48rBAlcfTm+IBbijaEdnqpwGib45wE/Cfy6FAttBHQh
+1Kp+r/jPbf1mYAvljUfHKuvbg8t2EIQz/5yGp+n5trn9pElfQO2cRBq8LFpf1l+U
+P7EKjFmlOq+Gs/fF98/dP3DfniSd78LQPq5vp8RL8nr/o2i7jkAQ33m4f1wOBWd+
+cZovrKXYlXiR+Bf7m2hpZo+/sAzhd7LmAD0l09kABRG0JVN1U0UgU2VjdXJpdHkg
+VGVhbSA8c2VjdXJpdHlAc3VzZS5kZT6JARUDBRA24S1H5Fiyh7HKPEUBAVcOB/9b
+yHYji1/+4Xc2GhvXK0FSJN0MGgeXgW47yxDL7gmR4mNgjlIOUHZj0PEpVjWepOJ7
+tQS3L9oP6cpj1Fj/XxuLbkp5VCQ61hpt54coQAvYrnT9rtWEGN+xmwejT1WmYmDJ
+xG+EGBXKr+XP69oIUl1E2JO3rXeklulgjqRKos4cdXKgyjWZ7CP9V9daRXDtje63
+Om8gwSdU/nCvhdRIWp/Vwbf7Ia8iZr9OJ5YuQl0DBG4qmGDDrvImgPAFkYFzwlqo
+choXFQ9y0YVCV41DnR+GYhwl2qBd81T8aXhihEGPIgaw3g8gd8B5o6mPVgl+nJqI
+BkEYGBusiag2pS6qwznZiQEVAwUQNuEtBHey5gA9JdPZAQFtOAf+KVh939b0J94u
+v/kpg4xs1LthlhquhbHcKNoVTNspugiC3qMPyvSX4XcBr2PC0cVkS4Z9PY9iCfT+
+x9WM96g39dAF+le2CCx7XISk9XXJ4ApEy5g4AuK7NYgAJd39PPbERgWnxjxir9g0
+Ix30dS30bW39D+3NPU5Ho9TD/B7UDFvYT5AWHl3MGwo3a1RhTs6sfgL7yQ3U+mvq
+MkTExZb5mfN1FeaYKMopoI4VpzNVeGxQWIz67VjJHVyUlF20ekOz4kWVgsxkc8G2
+saqZd6yv2EwqYTi8BDAduweP33KrQc4KDDommQNDOXxaKOeCoESIdM4p7Esdjq1o
+L0oixF12CohGBBARAgAGBQI7HmHDAAoJEJ5A4xAACqukTlQAoI4QzP9yjPohY7OU
+F7J3eKBTzp25AJ42BmtSd3pvm5ldmognWF3Trhp+GYkAlQMFEDe3O8IWkDf+zvyS
+FQEBAfkD/3GG5UgJj18UhYmh1gfjIlDcPAeqMwSytEHDENmHC+vlZQ/p0mT9tPiW
+tp34io54mwr+bLPN8l6B5GJNkbGvH6M+mO7R8Lj4nHL6pyAv3PQr83WyLHcaX7It
+Klj371/4yzKV6qpz43SGRK4MacLo2rNZ/dNej7lwPCtzCcFYwqkiiEYEEBECAAYF
+AjoaQqQACgkQx1KqMrDf94ArewCfWnTUDG5gNYkmHG4bYL8fQcizyA4An2eVo/n+
+3J2KRWSOhpAMsnMxtPbB
+=Ay23
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-7e2e3b05-44748aba.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-7e2e3b05-44748aba.asc
new file mode 100644 (file)
index 0000000..a4be6b8
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBER0iroRBADfqUeJmPCXqPJFnf3CVKy40dL1F+gfvP+JHN7/uu4c9+oCYjI1
+uAE8iGTm/Twb/Zzbs4dt0iWjhNFXbRi42rMww4d/8QcPYZ21WSehh+fv8nCjt2sw
+LeC87ar2SR8OTpJBK0fQlcd4e6H5GMntfI6SYEUOPd8m/eQ+4+1AxpUUpwCgswaF
+13fePZGI//pDn5tGjbvmaP8D/R3qum/I+oDS8lbFeeDS10GkEkwTYec13gdfsq6I
+yzIj7VBsC+rGfbipv+VGR61Q4d19pOHKLDekr9OG+3G4ZcYM4NQvQZR+QIlp3xWu
+nBmYD1LRkHLVj+Z4DGQhjjOffkPSuacKPymMaZ/aRiLgTIAo97W2YPhutscXrLSG
+2Y+BA/4jsyaDb7kbW4wc8RtPIcuFEheVqgBeRakP9Uj47kBMBEpPtI/mIdY5liKk
+ztKnuQG6ROYLNV/PW0ZbE1uT64C710weh4cB3PnZLV5P10deDLBjHk8MJQGCTSDD
+JYvhutUzQfshAU6j2kErGvKdZxWGezab34vFyMP2oLGqswPAJrRQTm92ZWxsIFBy
+b3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20pIDxub3ZlbGwt
+cHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6IZgQTEQIAJgUCRHSKugIbAwUJA8JnAAYL
+CQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEBTCi8l+LjsFWEoAn13x+5ObqkW08gYF
+YNDlcGPjQuGPAJ9kAQbVUvvh1u9mBgu91cQ9W/TkHYhGBBMRAgAGBQJEexD/AAoJ
+EKhO2uicgArKFLwAn0B+g2mJ5n8LrBziTQ5SjnSPyDBXAJwJoYTta5Sfw/3vVGpU
+fJAKVDoB9w==
+=tJSz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-9c800aca-40d8063e.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-9c800aca-40d8063e.asc
new file mode 100644 (file)
index 0000000..67eedcf
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-a1912208-446a0899.asc b/tests/repo/susetags/data/stable-x86-subset-gz/gpg-pubkey-a1912208-446a0899.asc
new file mode 100644 (file)
index 0000000..279397c
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQILBERqCJkBEACdqhZWdAbUHLIumXMEgv+GFjr1ZzVHgynnFOzztU/8sxZNa9cm
+YV4HZpVfjMr7fos3ArzyiPPt/336cf7w9p79/ZS4rHSNPDMlPCtXYvFxUbvU0/GY
+q4jwcBsrJ0xaJ9CP5bWyAgVKOb7Y6k0ktaLjRR+tDfMsHA4H0ClMoRr6ATw8NL0e
+VCfAHuzqCKvX1If8ng+wTivtAhKvz/WwQiELNELmPfc5tZHOw8NgP/r0Pze18Hn2
+dlAHu0WpC7uoR00vscsMIJiJJPcsxbL1F1eADKnk+wEy8Go+EJeJ5i0WoFbqD52q
+Lv/C/oY6NVtVY0MBwtn+oQNSnQ4JBsB/Akdt53LAi0ZtNQxMyUW+76R8FCOmVCV8
+WGiF5CPRP0yvG80AMBjBjKjHb/v8ov5MnIyFimzAHS1gQcUNxTEYA/5eFwoYcGcK
+weGq9FUjPTzLQAgvp7XmOzHpSAfJ7qysxFTepNsSZZhgizJyInrdQldr+GYcUNqB
+krD9MWmFop975OxhCTEnNv/HcE79r8WD26HzDFYxTiTJbr0pU/ivBzo+rjq+YG2V
+stJk+udVYmZTnC4LmXus8JiNuqBXbxNscwCBpcJ8YcfCV6uh+7E0XfXZsgVUFLp1
+NF+ylYRGTycOlWoZODrnJevZW7N9O3bWRx/G2P4bJD07LsDLe4i5hymf5QAGKbRQ
+Tm92ZWxsIFByb3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20p
+IDxub3ZlbGwtcHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6JAjMEEwECAB0FAkRqCJkG
+CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRBHijLooZEiCKSFD/93vZHCAMLEfksU
+KnvXl08bv1rfuamuyJnE3ANRE5RDyypriHMCnkVxazvQ2WI4W4UEjluL9+SzZwtV
+ZvKVoAr31614nSyWwv2YnJTHfjMG+xRlkolZMnuIiB9PcCBo9+GPU0ABuzo4pEJW
+NIRoSS1NFbAZBhtUnY0cN+trM5QObLl7xXTavLyGk//blkk57fov7GXsQJlZUig0
+l2yt5XNyGpLUnTMDumHh8b389quF+0+ZfdwOy7A768xjipAZiTvIujBrEv51wrxh
+0HBT0VGA0MhD9t0B+Ce4BM9P/iVMO00naaOp6PqMfPPKxQQqer8qy1i6UWBx95SY
+mKZBIvOm2d9PezDxkckCu61r6krx1iKnT1wdprCAkIYwALK118SpbxuyGW0bhRHc
+wsc/akzWH72fS0Xu49mvL4k4A2U9asdeQid3dMgbtm5mSWof0yiU/G4YNn0yeXoY
+oG1VbCAqQbFX1Rvd6GITJVqI+ekW/uMA9BP78dF8wBeG0+QmpQnSf+eOsxB/RT8o
+Kb4hHY+29MUlg+i9ceVt7hoKr03J/uIG5TXFXRYLaI0iAFVlKfWxpqDfS2XA4+dD
+VYt+5RDgBcnxDaTB4FE9GqcYScNfe7+NFtL0p0wOPftbmgZzGjucTmrD8mDUNdqA
+xGK7vlk4GATSfOQlq7G6LXW6RYnInohGBBMRAgAGBQJEazMlAAoJEKhO2uicgArK
+2vMAn0TbVDESEVKVuFZStrfIzOvJQrR9AJsH733Ju1kE99GFrdfCeGqpckmNhg==
+=E+qN
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/installation.xml b/tests/repo/susetags/data/stable-x86-subset-gz/installation.xml
new file mode 100644 (file)
index 0000000..f71e6b7
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<!DOCTYPE productDefines SYSTEM "/usr/share/YaST2/control/control.dtd">
+<productDefines xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
+    <textdomain>control-ISSLE</textdomain>
+    <globals>
+        <enable_firewall config:type="boolean">false</enable_firewall>
+    </globals>
+    <software>
+        <selection_type config:type="symbol">auto</selection_type>
+    </software>
+    <network>
+        <force_static_ip config:type="boolean">true</force_static_ip>
+    </network>
+    <texts>
+        <congratulate>
+            <label>
+&lt;p&gt;
+You have successfully installed the Integrated Stack for SUSE Linux Enterprise.
+&lt;/p&gt;
+&lt;p&gt;
+The services you just installed might require additional
+setup before use. See the Integrated Stack for SUSE Linux Enterprise
+Installation guide for details.
+&lt;/p&gt;
+            </label>
+        </congratulate>
+    </texts>
+    <proposals config:type="list">
+        <proposal>
+            <label>ISSLE Configuration</label>
+            <name>product</name>
+            <stage>continue,normal</stage>
+            <mode>installation</mode>
+            <proposal_modules config:type="list">
+                <proposal_module>yast2-issleconfig</proposal_module>
+            </proposal_modules>
+        </proposal>
+    </proposals>
+    <workflows config:type="list">
+        <workflow>
+            <defaults>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <stage>normal</stage>
+            <mode>installation,normal</mode>
+            <modules config:type="list">
+                <module>
+                    <label>ISSLE Software Selection</label>
+                    <name>sw_single</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <label>ISSLE Configuration</label>
+                    <name>issleconfig</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <label>Release Notes</label>
+                    <name>inst_release_notes</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <name>inst_congratulate</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+            </modules>
+        </workflow>
+    </workflows>
+    <update>
+        <workflows config:type="list">
+            <workflow>
+                <defaults>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </defaults>
+                <stage>continue,normal</stage>
+                <mode>installation,normal,update</mode>
+                <remove_modules config:type="list">
+                    <remove_module>hostname</remove_module>
+                    <remove_module>root</remove_module>
+                    <remove_module>ca_mgm</remove_module>
+                </remove_modules>
+                <append_modules config:type="list">
+                    <module>
+                        <name>yast2-issleconfig</name>
+                    </module>
+                </append_modules>
+                <insert_modules config:type="list">
+                    <insert_module>
+                        <before>suseconfig</before>
+                        <modules config:type="list">
+                            <module>
+                                <label>ISSLE Configuration</label>
+                                <name>issleconfig</name>
+                                <enable_back>yes</enable_back>
+                                <enable_next>yes</enable_next>
+                            </module>
+                        </modules>
+                    </insert_module>
+                </insert_modules>
+            </workflow>
+        </workflows>
+    </update>
+</productDefines>
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/license.tar.gz b/tests/repo/susetags/data/stable-x86-subset-gz/license.tar.gz
new file mode 100644 (file)
index 0000000..ecd631d
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/license.tar.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/directory.yast b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/directory.yast
new file mode 100644 (file)
index 0000000..f4fb49c
--- /dev/null
@@ -0,0 +1,6 @@
+info.txt
+license.zip
+media
+products
+products.asc
+products.key
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/info.txt b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/info.txt
new file mode 100644 (file)
index 0000000..d5d1a49
--- /dev/null
@@ -0,0 +1,38 @@
+
+   openSUSE FACTORY 10.3-factory
+   Attention! You are accessing our BETA Distribution.  If you install
+   any package, note that we can NOT GIVE ANY SUPPORT for your system - 
+   no matter if you update from a previous system or do a complete 
+   new installation.
+
+   Use this BETA distribution at your own risk! We recommend it for
+   testing, porting and evaluation purposes but not for any critical
+   production systems.
+
+   If you are curious and would like to help us to find the bugs, you're
+   very welcome.  Please enter bug reports following the instructions
+   given at http://bugs.opensuse.org .
+   If you want to talk about this distribution with others, you can
+   discuss on the mailing list opensuse-factory@opensuse.org.
+
+   Sources for development releases are not distributed via mirrors to
+   reduce the bandwidth and storage on these mirrors.
+
+   You can always find the latest source at
+   http://download.opensuse.org/distribution/SL-OSS-factory/
+
+   In case you need the exact source of this development release you can
+   find it on:
+   http://www.novell.com/products/opensuse/source_code.html
+
+   Alternatively, see
+   http://www.novell.com/products/opensuse/source_code.html or send e-mail
+   to sourcedvd@suse.de to request the source
+   for a specific release of openSUSE on DVD. Please note that we will
+   charge $15 or 15 Euros to cover our costs of distribution.
+
+   Use this distribution at your own risk - and remember to have a
+   lot of fun! :)
+
+                Your openSUSE Team.
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/license.zip b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/license.zip
new file mode 100644 (file)
index 0000000..dd500d6
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/license.zip differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/media b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/media
new file mode 100644 (file)
index 0000000..d5aa1ef
--- /dev/null
@@ -0,0 +1,3 @@
+SUSE Linux Products GmbH
+20070705102239
+1
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products
new file mode 100644 (file)
index 0000000..5563ffd
--- /dev/null
@@ -0,0 +1 @@
+/ SuSE-Linux-STABLE-X86 10.3
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.asc b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.asc
new file mode 100644 (file)
index 0000000..c6f169d
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBGjNGIqE7a6JyACsoRAoWnAJkB1wTxOQngy5xvjTiUvwWp65wW5wCg
+hCbA+jw64zZYCa7IM71hBBynFL4=
+=nSHl
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.key b/tests/repo/susetags/data/stable-x86-subset-gz/media.1/products.key
new file mode 100644 (file)
index 0000000..67eedcf
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/kde-10.3-71.i586.pat.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/kde-10.3-71.i586.pat.gz
new file mode 100644 (file)
index 0000000..9664f1d
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/kde-10.3-71.i586.pat.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.DU.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.DU.gz
new file mode 100644 (file)
index 0000000..c49020e
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.DU.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.en.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.en.gz
new file mode 100644 (file)
index 0000000..a321085
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.en.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.es.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.es.gz
new file mode 100644 (file)
index 0000000..41ff621
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.es.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.gz
new file mode 100644 (file)
index 0000000..fb012ea
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/packages.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/patterns.gz b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/patterns.gz
new file mode 100644 (file)
index 0000000..e14fee1
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset-gz/suse/setup/descr/patterns.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset/content b/tests/repo/susetags/data/stable-x86-subset/content
new file mode 100644 (file)
index 0000000..fb211e2
--- /dev/null
@@ -0,0 +1,42 @@
+PRODUCT openSUSE-factory
+VERSION 10.3
+DISTPRODUCT SuSE-Linux-STABLE-X86
+DISTVERSION 10.2.42-factory
+VENDOR SUSE LINUX Products GmbH, Nuernberg, Germany
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/FACTORY/release-notes.rpm
+ARCH.x86_64 x86_64 i686 i586 i486 i386 noarch
+ARCH.i686 i686 i586 i486 i386 noarch
+ARCH.i586 i586 i486 i386 noarch
+ARCH.i486 i486 i386 noarch
+ARCH.i386 i386 noarch
+DEFAULTBASE i586
+REQUIRES openSUSE-release = 10.3 pattern:basesystem
+PROVIDES product:openSUSE = 10.2.42
+OBSOLETES product:SUSE_LINUX product:openSUSE <= 10.2
+LINGUAS cs da de en en_GB es fi fr hu it ja km nl nb pl pt_BR zh_CN zh_TW
+SHORTLABEL FACTORY
+LABEL openSUSE FACTORY 10.3
+LABEL.de openSUSE FACTORY 10.3
+EXTRAURLS http://download.opensuse.org/distribution/10.2/repo/oss/
+OPTIONALURLS http://download.opensuse.org/distribution/10.2/repo/non-oss/ http://download.opensuse.org/distribution/10.2/repo/debug/
+DESCRDIR suse/setup/descr
+DATADIR suse
+FLAGS update
+LANGUAGE en_US
+META SHA1 c37f4ba4225650844363711710c3824a58c901dd  kde-10.3-71.i586.pat
+META SHA1 80f9bb1f9e95ebcebfd9b22f338f779e204cd50b  packages
+META SHA1 9c341d93124860f03b001681513ca07b4d0ca873  packages.DU
+META SHA1 a48fa507afccbce76b549a07394e41d9813e6b01  packages.en
+META SHA1 765900f5513ce6bf496d5006bdff45f805e61997  packages.es
+META SHA1 fbb6a2d6976c37c9a6beb52ca35f7594ae3eff1e  patterns
+KEY SHA1 c0354069c10819674da8706822e1d4bd0c1797e9  gpg-pubkey-0dfb3188-41ed929b.asc
+KEY SHA1 2e38e503c436c5d002bdc31755c82188044d9d21  gpg-pubkey-307e3d54-44201d5d.asc
+KEY SHA1 7025932e6866932f489421990075f3ed312023ea  gpg-pubkey-3d25d3d9-36e12d04.asc
+KEY SHA1 fd6146cac8c1473c5b52548936de773d5bbd5610  gpg-pubkey-7e2e3b05-44748aba.asc
+KEY SHA1 cd7adceba1fe5d7ba27b5749718743192d82f802  gpg-pubkey-9c800aca-40d8063e.asc
+KEY SHA1 7535d79e31ef7b4232e5593bb49d9142978b2e95  gpg-pubkey-a1912208-446a0899.asc
+HASH SHA1 172b3cf77268f46e783e78a653902e563cb91e9a  license.tar.gz
+HASH SHA1 4aa8ded6302e6ec85690a51af6044dffe9b21923  control.xml
+HASH SHA1 82f1f17ce74f0cd3fca4813c178196b317fc952d  installation.xml
+HASH SHA1 68d9b548d61e31e82e8834690e884afa27751287  media.1/info.txt
+
diff --git a/tests/repo/susetags/data/stable-x86-subset/content.asc b/tests/repo/susetags/data/stable-x86-subset/content.asc
new file mode 100644 (file)
index 0000000..8a3bd48
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.9.22 (GNU/Linux)
+
+iD8DBQBGzZ7Im+zCtd2wN1YRAvQ2AJ9S5chSnjnFR0P2IZO7485uodmb1QCeJzuj
+Ta+M4yWexFiqyaFmWRjdAuQ=
+=lrpc
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/content.key b/tests/repo/susetags/data/stable-x86-subset/content.key
new file mode 100644 (file)
index 0000000..8e52028
--- /dev/null
@@ -0,0 +1,24 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+=5nY9
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/control.xml b/tests/repo/susetags/data/stable-x86-subset/control.xml
new file mode 100644 (file)
index 0000000..096763e
--- /dev/null
@@ -0,0 +1,793 @@
+<?xml version="1.0"?>
+<productDefines  xmlns="http://www.suse.com/1.0/yast2ns"
+    xmlns:config="http://www.suse.com/1.0/configns">
+
+<!--
+Work around for the text domain
+textdomain="control"
+-->
+
+    <textdomain>control</textdomain>
+    <globals>
+       <additional_kernel_parameters></additional_kernel_parameters>
+        <enable_autologin config:type="boolean">true</enable_autologin>
+       <enable_firewall config:type="boolean">true</enable_firewall>
+       <firewall_enable_ssh config:type="boolean">false</firewall_enable_ssh>
+       <fam_local_only>never</fam_local_only>
+        <incomplete_translation_treshold>95</incomplete_translation_treshold>
+        <inform_about_suboptimal_distribution config:type="boolean">true</inform_about_suboptimal_distribution>
+        <skip_language_dialog config:type="boolean">true</skip_language_dialog>
+        <ui_mode>simple</ui_mode>
+        <enable_clone config:type="boolean">false</enable_clone>
+        <enable_register_hwdata   config:type="boolean">true</enable_register_hwdata>
+        <enable_register_optional config:type="boolean">true</enable_register_optional>
+        <display_register_forcereg config:type="boolean">true</display_register_forcereg>
+        <disable_register_w3m config:type="boolean">true</disable_register_w3m>
+        <register_monthly config:type="boolean">false</register_monthly>
+       <manual_online_update config:type="boolean">true</manual_online_update>
+       <root_password_ca_check config:type="boolean">false</root_password_ca_check>
+
+       <!-- FATE #301937, Save /root content from the installation system to the installed system -->
+       <save_instsys_content config:type="list">
+           <save_instsys_item>
+               <instsys_directory>/root/</instsys_directory>
+               <system_directory>/root/inst-sys/</system_directory>
+           </save_instsys_item>
+       </save_instsys_content>
+    </globals>
+
+    <software>
+        <delete_old_packages config:type="boolean">false</delete_old_packages>
+        <selection_type config:type="symbol">auto</selection_type>
+
+       <!-- FATE #300898, List of external sources accesible during the installation time -->
+       <external_sources_link>http://download.opensuse.org/YaST/Repos/openSUSE_103_Servers.xml</external_sources_link>
+    </software>
+
+    <partitioning>
+        <evms_config config:type="boolean">false</evms_config>
+        <try_separate_home config:type="boolean">true</try_separate_home>
+        <limit_try_home>7G</limit_try_home>
+        <root_space_percent>40</root_space_percent>
+        <root_base_size>5G</root_base_size>
+        <root_max_size>20G</root_max_size>
+       <proposal_lvm>false</proposal_lvm>
+       <proposal_evms>false</proposal_evms>
+       <vm_desired_size>15G</vm_desired_size>
+       <vm_home_max_size>25G</vm_home_max_size>
+       <boot_evms>false</boot_evms>
+    </partitioning>
+
+    <network>
+        <force_static_ip config:type="boolean">false</force_static_ip>
+        <network_manager>laptop</network_manager>
+       <startmode>ifplugd</startmode>
+    </network>
+
+    <clone_modules config:type="list">
+       <clone_module>language</clone_module>
+       <clone_module>keyboard</clone_module>
+       <clone_module>timezone</clone_module>
+       <clone_module>users</clone_module>
+       <clone_module>networking</clone_module>
+       <clone_module>firewall</clone_module>
+       <clone_module>host</clone_module>
+       <clone_module>routing</clone_module>
+       <clone_module>proxy</clone_module>
+       <clone_module>runlevel</clone_module>
+       <clone_module>x11</clone_module>
+       <clone_module>nis</clone_module>
+       <clone_module>ldap</clone_module>
+       <clone_module>printer</clone_module>
+       <clone_module>add-on</clone_module>
+    </clone_modules>
+
+    <texts>
+      <congratulate>
+       <label>
+&lt;p&gt;&lt;b&gt;Congratulations!&lt;/b&gt;&lt;/p&gt;
+&lt;p&gt;The installation of openSUSE on your machine is complete.
+After clicking &lt;b&gt;Finish&lt;/b&gt;, you can log in to the system.&lt;/p&gt;
+&lt;p&gt;Visit us at %1.&lt;/p&gt;
+&lt;p&gt;Have a lot of fun!&lt;br&gt;Your openSUSE Development Team&lt;/p&gt;
+       </label>
+      </congratulate>
+    </texts>
+
+    <proposals config:type="list">
+        <proposal>
+            <label>Installation Settings</label>
+            <mode>installation,demo,autoinstallation</mode>
+            <stage>initial</stage>
+            <name>initial</name>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>hwinfo</proposal_module>
+                <proposal_module>keyboard</proposal_module>
+                <proposal_module>mouse</proposal_module>
+                <proposal_module>partitions</proposal_module>
+                <proposal_module>software</proposal_module>
+                <proposal_module>bootloader</proposal_module>
+                <proposal_module>timezone</proposal_module>
+                <proposal_module>language</proposal_module>
+                <proposal_module>runlevel</proposal_module>
+            </proposal_modules>
+            <proposal_tabs config:type="list">
+                <proposal_tab>
+                    <label>Overview</label>
+                    <proposal_modules config:type="list">
+                        <proposal_module>partitions</proposal_module>
+                        <proposal_module>software_simple</proposal_module>
+                       <proposal_module>country_simple</proposal_module>
+                    </proposal_modules>
+                </proposal_tab>
+                <proposal_tab>
+                    <label>Expert</label>
+                    <proposal_modules config:type="list">
+                        <proposal_module>hwinfo</proposal_module>
+                        <proposal_module>keyboard</proposal_module>
+                        <proposal_module>mouse</proposal_module>
+                        <proposal_module>partitions</proposal_module>
+                        <proposal_module>software</proposal_module>
+                        <proposal_module>bootloader</proposal_module>
+                        <proposal_module>timezone</proposal_module>
+                        <proposal_module>language</proposal_module>
+                        <proposal_module>runlevel</proposal_module>
+                    </proposal_modules>
+                </proposal_tab>
+            </proposal_tabs>
+        </proposal>
+
+        <proposal>
+            <label>Update Settings</label>
+            <mode>update</mode>
+            <name>initial</name>
+            <stage>normal</stage>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>update</proposal_module>
+                <proposal_module>packages</proposal_module>
+                <proposal_module>backup</proposal_module>
+                <proposal_module>language</proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Installation Settings</label>
+            <mode>update</mode>
+            <stage>initial</stage>
+            <name>initial</name>
+            <enable_skip>no</enable_skip>
+            <proposal_modules config:type="list">
+               <proposal_module>hwinfo</proposal_module>
+               <proposal_module>media</proposal_module>
+<!-- disabled due to bug 165832
+                <proposal_module>rootpart</proposal_module>
+-->
+                <proposal_module>update</proposal_module>
+                <proposal_module>packages</proposal_module>
+                <proposal_module>backup</proposal_module>
+                <proposal_module>language</proposal_module>
+                <proposal_module>keyboard</proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Network Configuration</label>
+            <name>network</name>
+            <stage>continue,normal</stage>
+            <enable_skip>yes</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>
+                   <name>lan</name>
+                   <presentation_order>20</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>general</name>
+                   <presentation_order>5</presentation_order>
+               </proposal_module>
+               <proposal_module>
+                   <name>dsl</name>
+                   <presentation_order>30</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>isdn</name>
+                   <presentation_order>40</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>modem</name>
+                   <presentation_order>50</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>remote</name>
+                   <presentation_order>60</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>firewall</name>
+                   <presentation_order>10</presentation_order>
+               </proposal_module>
+                <proposal_module>
+                   <name>proxy</name>
+                   <presentation_order>70</presentation_order>
+               </proposal_module>
+            </proposal_modules>
+        </proposal>
+
+        <proposal>
+            <label>Hardware Configuration</label>
+            <name>hardware</name>
+            <stage>continue</stage>
+            <enable_skip>yes</enable_skip>
+            <proposal_modules config:type="list">
+                <proposal_module>x11</proposal_module>
+                <proposal_module>printer</proposal_module>
+                <proposal_module>sound</proposal_module>
+                <proposal_module>tv</proposal_module>
+                <proposal_module>bluetooth</proposal_module>
+            </proposal_modules>
+        </proposal>
+    </proposals>
+
+    <!-- Stage: Initial, Mode: Installation -->
+    <workflows config:type="list">
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+           <label>Preparation</label>
+            <mode>installation</mode>
+            <stage>initial</stage>
+            <modules  config:type="list">
+                <module>
+                    <name>language</name>
+                   <label>Language</label>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Language</label>
+                    <name>checkmedia</name>
+                </module>
+                <module>
+                   <label>License Agreement</label>
+                    <name>license</name>
+                </module>
+               <module>
+                   <label>Disk Activation</label>
+                    <name>disks_activate</name>
+               </module>
+                <module>
+                   <label>System Analysis</label>
+                    <name>system_analysis</name>
+                </module>
+               <!-- Here, user selects whether to perform New Installation or Upgrade -->
+               <module>
+                   <label>Online Repositories</label>
+                   <name>productsources</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <label>Add-On Products</label>
+                   <name>add-on</name>
+                   <enable_back>yes</enable_back>
+               </module>
+                <module>
+                   <label>Time Zone</label>
+                    <name>timezone</name>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                   <enable_back>yes</enable_back>
+                </module>
+                <module>
+                   <label>Desktop Selection</label>
+                    <name>desktop</name>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Installation</label>
+               </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_next>no</enable_next>
+                   <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>finish</name>
+                   <enable_back>no</enable_back>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: Update -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+           <label>Preparation</label>
+            <mode>update</mode>
+            <stage>initial</stage>
+            <modules  config:type="list">
+                <module>
+                    <name>language</name>
+                   <label>Language</label>
+                    <enable_back>no</enable_back>
+                    <arguments>
+                        <first_run>yes</first_run>
+                    </arguments>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Language</label>
+                    <name>checkmedia</name>
+                </module>
+                <module>
+                   <label>License Agreement</label>
+                    <name>license</name>
+                </module>
+               <module>
+                   <label>Disk Activation</label>
+                    <name>disks_activate</name>
+               </module>
+                <module>
+                   <label>System Analysis</label>
+                    <name>system_analysis</name>
+                </module>
+                <module>
+                   <label>System for Update</label>
+                    <name>update_partition</name>
+                </module>
+               <module>
+                   <name>upgrade_urls</name>
+               </module>
+               <module>
+                   <label>Online Repositories</label>
+                   <name>productsources</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <label>Add-On Products</label>
+                   <name>add-on</name>
+                   <enable_back>yes</enable_back>
+               </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Update</label>
+               </module>
+                <module>
+                   <label>Update Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Perform Update</label>
+                    <name>finish</name>
+                   <enable_back>no</enable_back>
+                </module>
+            </modules>
+        </workflow>
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+            </defaults>
+            <stage>initial</stage>
+           <label>Preparation</label>
+            <mode>repair</mode>
+            <modules  config:type="list">
+                <module>
+                   <label>System Information</label>
+                    <name>info</name>
+                </module>
+                <module>
+                   <label>Perform Repair</label>
+                    <name>repair</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: ScreenShot -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+            <stage>initial</stage>
+           <label>Base Installation</label>
+            <mode>screen_shot</mode>
+            <modules  config:type="list">
+                <module>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                   <label>Language</label>
+                    <name>language</name>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>yes</enable_next>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: Demo -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+               <enable_back>yes</enable_back>
+               <enable_next>yes</enable_next>
+            </defaults>
+            <stage>initial</stage>
+           <label>Base Installation</label>
+            <mode>demo</mode>
+            <modules  config:type="list">
+                <module>
+                    <enable_back>no</enable_back>
+                   <label>Language</label>
+                    <name>language</name>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>Installation Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>do_resize</name>
+                    <update config:type="boolean">false</update>
+                    <archs>i386,x86_64,ia64</archs>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                   <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: Update -->
+        <workflow>
+            <stage>continue</stage>
+            <mode>update</mode>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>yes</enable_back>
+                <enable_next>yes</enable_next>
+            </defaults>
+            <modules config:type="list">
+                <module>
+                   <label>Perform Update</label>
+                    <name>rpmcopy</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>Network</label>
+                    <name>ask_net_test</name>
+                   <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>do_net_test</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>addon_update_sources</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>suse_register</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>ask_online_update</name>
+                </module>
+                <module>
+                   <label>Online Update</label>
+                    <name>you</name>
+                </module>
+               <module>
+                   <label>Online Update</label>
+                   <name>restore_settings</name>
+               </module>
+                <module>
+                    <name>suseconfig</name>
+                   <enable_back>no</enable_back>
+                   <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Release Notes</label>
+                    <name>release_notes</name>
+                </module>
+                <module>
+                    <name>congratulate</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: Installation -->
+        <workflow>
+            <stage>continue</stage>
+            <mode>installation</mode>
+            <defaults>
+                <enable_back>yes</enable_back>
+                <enable_next>yes</enable_next>
+                <archs>all</archs>
+            </defaults>
+            <modules  config:type="list">
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>root Password</label>
+                    <name>root</name>
+                    <enable_back>no</enable_back>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>initialization</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>netprobe</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Check Installation</label>
+                    <name>rpmcopy</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Hostname</label>
+                    <name>hostname</name>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>proposal</name>
+                    <proposal>network</proposal>
+                </module>
+               <module>
+                   <label>Network</label>
+                    <name>fam</name>
+               </module>
+                <module>
+                   <label>Network</label>
+                    <name>ask_net_test</name>
+                </module>
+                <module>
+                   <label>Network</label>
+                    <name>do_net_test</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>addon_update_sources</name>
+                </module>
+                <module>
+                    <label>Registration</label>
+                    <name>suse_register</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>ask_online_update</name>
+                </module>
+                <module>
+                   <label>Online Update</label>
+                    <name>you</name>
+                </module>
+                <module>
+                    <label>Online Update</label>
+                    <name>extrasources</name>
+                </module>
+               <module>
+                   <label>Online Update</label>
+                   <name>restore_settings</name>
+               </module>
+                <module>
+                   <label>Users</label>
+                    <name>auth</name>
+                </module>
+                <module>
+                   <label>Users</label>
+                    <name>user</name>
+                </module>
+                <module>
+                    <name>suseconfig</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </module>
+                <module>
+                   <label>Release Notes</label>
+                    <name>release_notes</name>
+                </module>
+                <module>
+                   <label>Hardware Configuration</label>
+                    <name>proposal</name>
+                    <proposal>hardware</proposal>
+                </module>
+                <module>
+                   <label>Hardware Configuration</label>
+                   <name>save_hardware_status</name>
+                </module>
+                <module>
+                    <name>congratulate</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Initial, Mode: AutoInstallation -->
+        <workflow>
+            <stage>initial</stage>
+            <label>Base Installation</label>
+            <mode>autoinstallation</mode>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <modules  config:type="list">
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>autoinit</name>
+                    <archs>all</archs>
+                    <retranslate config:type="boolean">true</retranslate>
+                </module>
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>autosetup</name>
+                </module>
+                <module>
+                   <label>AutoYaST Settings</label>
+                    <name>proposal</name>
+                    <proposal>initial</proposal>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>prepdisk</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>kickoff</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>autoimage</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>finish</name>
+                </module>
+            </modules>
+        </workflow>
+
+       <!-- Stage: Continue, Mode: AutoInstallation -->
+        <workflow>
+            <defaults>
+                <archs>all</archs>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <stage>continue</stage>
+            <mode>autoinstallation</mode>
+            <modules  config:type="list">
+                <module>
+                   <label>Perform Installation</label>
+                    <name>netprobe</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>autopost</name>
+                </module>
+                <module>
+                   <label>Perform Installation</label>
+                    <name>rpmcopy</name>
+                </module>
+               <module>
+                   <heading>yes</heading>
+                   <label>Configuration</label>
+               </module>
+                <module>
+                   <label>System Configuration</label>
+                    <name>autoconfigure</name>
+                </module>
+                <module>
+                    <name>suseconfig</name>
+                </module>
+            </modules>
+        </workflow>
+    </workflows>
+</productDefines>
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-0dfb3188-41ed929b.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-0dfb3188-41ed929b.asc
new file mode 100644 (file)
index 0000000..89fc35c
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBEHtkpsRBACRHiXh3olS++6/Mp9N7ByGMmjaaE+Y8cJQLUPG1myrbW5aogIP
+0WenayhGbbgOHNWgd5dQ8KQpYYFoQuUHjFYzj5MvgrdOENOvD7ZNJ6+EmbkNh5cV
+zUYfNG9jdiGweZkyA1sh8DYS0JiUmQ4CzaBD/DotB/dCmDcyuNQFiw4qKwCglQah
+ATyueBRsOiXl0NIs1uB6dkkD/1A2YmQ6te1q38a1J+a8os6bDlMZhVnkZdhJdw6x
+eBwUb9XS0n7hyt/AKCcBnrDEUQJuhBMNgzctJvbuMv27yRMANAXZDQkp0ip/yHLJ
+PhUdSNTTRHOL9bV3t+JuZ9xmuclprwyrrJYUkEESXNc0tkuczHBP2c/RqA3OxYHt
+hrHLA/9Pqe2gEleeo8l26u/uFXs2dtwjh8EZmdhHoqGcOlpYR4DyAg2D+jYfh3RI
+oPzIwRlHVUR1ii5h8iPi98BVuEvukwfbbQ1K22Jwzxt6w3ihCXBKWKbeC3ElIMfA
+hVMchLFUbTAw+yodO/u3NHxKQ34+ginid9dVyxV5T0gpDEEHObQrT3BlbiBFbnRl
+cnByaXNlIFNlcnZlciA8c3VwcG9ydEBub3ZlbGwuY29tPoheBBMRAgAeBQJB7ZKb
+AhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEHPSXWMN+zGID4oAoJPTGZbZApW+
+tuU422mHYGwoqgjrAJ9fhzRhRbV3YsOxKUomNeuIfmWGXA==
+=Qv5+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-307e3d54-44201d5d.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-307e3d54-44201d5d.asc
new file mode 100644 (file)
index 0000000..2d21c1b
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mIsERCAdXQEEAL7MrBTz+3SBWpCm2ae2yaDqV3ezQcs2JlvqidJVhsZqQe9/jkxi
+KTEQW5+TXF/+BlQSiebunRI7oo3+9U8GyRCgs1sf+yRQWMLzZqRaarzRhw9w+Ihl
+edtqYl6/U2JZCb8Adp6d7RzlRliJdJ/VtsfXj2ef7Dwu7elOVSsmaBdtAAYptChT
+dVNFIFBhY2thZ2UgU2lnbmluZyBLZXkgPGJ1aWxkQHN1c2UuZGU+iLgEEwECACIF
+AkQgHV0CGwMFCQQ9AoAECwcDAgMVAgMDFgIBAh4BAheAAAoJEOOlw2Awfj1UjUIE
+AIf3SLlrfj2RsCDjyYThXen+A/WTYDPbY+NYmmVvFQilHNQY9ZrJ5cNohRQu6hA+
+Sccrf11Uy24tTHWSTzuG9VzFeeIAcIU02XHar0w3QbvTk6IqeG+OZlfOGJj1sdx4
+JKwpwk9mSdrq2ELhrkPZiVWS7RmRkPr2klwYgKGWbmOJ
+=ZmDA
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-3d25d3d9-36e12d04.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-3d25d3d9-36e12d04.asc
new file mode 100644 (file)
index 0000000..894a463
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQENAzbhLQQAAAEIAKAkXHe0lWRBXLpn38hMHy03F0I4Sszmoc8aaKJrhfhyMlOA
+BqvklPLE2f9UrI4Xc860gH79ZREwAgPt0pi6+SleNFLNcNFAuuHMLQOOsaMFatbz
+JR9i4m/lf6q929YROu5zB48rBAlcfTm+IBbijaEdnqpwGib45wE/Cfy6FAttBHQh
+1Kp+r/jPbf1mYAvljUfHKuvbg8t2EIQz/5yGp+n5trn9pElfQO2cRBq8LFpf1l+U
+P7EKjFmlOq+Gs/fF98/dP3DfniSd78LQPq5vp8RL8nr/o2i7jkAQ33m4f1wOBWd+
+cZovrKXYlXiR+Bf7m2hpZo+/sAzhd7LmAD0l09kABRG0JVN1U0UgU2VjdXJpdHkg
+VGVhbSA8c2VjdXJpdHlAc3VzZS5kZT6JARUDBRA24S1H5Fiyh7HKPEUBAVcOB/9b
+yHYji1/+4Xc2GhvXK0FSJN0MGgeXgW47yxDL7gmR4mNgjlIOUHZj0PEpVjWepOJ7
+tQS3L9oP6cpj1Fj/XxuLbkp5VCQ61hpt54coQAvYrnT9rtWEGN+xmwejT1WmYmDJ
+xG+EGBXKr+XP69oIUl1E2JO3rXeklulgjqRKos4cdXKgyjWZ7CP9V9daRXDtje63
+Om8gwSdU/nCvhdRIWp/Vwbf7Ia8iZr9OJ5YuQl0DBG4qmGDDrvImgPAFkYFzwlqo
+choXFQ9y0YVCV41DnR+GYhwl2qBd81T8aXhihEGPIgaw3g8gd8B5o6mPVgl+nJqI
+BkEYGBusiag2pS6qwznZiQEVAwUQNuEtBHey5gA9JdPZAQFtOAf+KVh939b0J94u
+v/kpg4xs1LthlhquhbHcKNoVTNspugiC3qMPyvSX4XcBr2PC0cVkS4Z9PY9iCfT+
+x9WM96g39dAF+le2CCx7XISk9XXJ4ApEy5g4AuK7NYgAJd39PPbERgWnxjxir9g0
+Ix30dS30bW39D+3NPU5Ho9TD/B7UDFvYT5AWHl3MGwo3a1RhTs6sfgL7yQ3U+mvq
+MkTExZb5mfN1FeaYKMopoI4VpzNVeGxQWIz67VjJHVyUlF20ekOz4kWVgsxkc8G2
+saqZd6yv2EwqYTi8BDAduweP33KrQc4KDDommQNDOXxaKOeCoESIdM4p7Esdjq1o
+L0oixF12CohGBBARAgAGBQI7HmHDAAoJEJ5A4xAACqukTlQAoI4QzP9yjPohY7OU
+F7J3eKBTzp25AJ42BmtSd3pvm5ldmognWF3Trhp+GYkAlQMFEDe3O8IWkDf+zvyS
+FQEBAfkD/3GG5UgJj18UhYmh1gfjIlDcPAeqMwSytEHDENmHC+vlZQ/p0mT9tPiW
+tp34io54mwr+bLPN8l6B5GJNkbGvH6M+mO7R8Lj4nHL6pyAv3PQr83WyLHcaX7It
+Klj371/4yzKV6qpz43SGRK4MacLo2rNZ/dNej7lwPCtzCcFYwqkiiEYEEBECAAYF
+AjoaQqQACgkQx1KqMrDf94ArewCfWnTUDG5gNYkmHG4bYL8fQcizyA4An2eVo/n+
+3J2KRWSOhpAMsnMxtPbB
+=Ay23
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-7e2e3b05-44748aba.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-7e2e3b05-44748aba.asc
new file mode 100644 (file)
index 0000000..a4be6b8
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBER0iroRBADfqUeJmPCXqPJFnf3CVKy40dL1F+gfvP+JHN7/uu4c9+oCYjI1
+uAE8iGTm/Twb/Zzbs4dt0iWjhNFXbRi42rMww4d/8QcPYZ21WSehh+fv8nCjt2sw
+LeC87ar2SR8OTpJBK0fQlcd4e6H5GMntfI6SYEUOPd8m/eQ+4+1AxpUUpwCgswaF
+13fePZGI//pDn5tGjbvmaP8D/R3qum/I+oDS8lbFeeDS10GkEkwTYec13gdfsq6I
+yzIj7VBsC+rGfbipv+VGR61Q4d19pOHKLDekr9OG+3G4ZcYM4NQvQZR+QIlp3xWu
+nBmYD1LRkHLVj+Z4DGQhjjOffkPSuacKPymMaZ/aRiLgTIAo97W2YPhutscXrLSG
+2Y+BA/4jsyaDb7kbW4wc8RtPIcuFEheVqgBeRakP9Uj47kBMBEpPtI/mIdY5liKk
+ztKnuQG6ROYLNV/PW0ZbE1uT64C710weh4cB3PnZLV5P10deDLBjHk8MJQGCTSDD
+JYvhutUzQfshAU6j2kErGvKdZxWGezab34vFyMP2oLGqswPAJrRQTm92ZWxsIFBy
+b3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20pIDxub3ZlbGwt
+cHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6IZgQTEQIAJgUCRHSKugIbAwUJA8JnAAYL
+CQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEBTCi8l+LjsFWEoAn13x+5ObqkW08gYF
+YNDlcGPjQuGPAJ9kAQbVUvvh1u9mBgu91cQ9W/TkHYhGBBMRAgAGBQJEexD/AAoJ
+EKhO2uicgArKFLwAn0B+g2mJ5n8LrBziTQ5SjnSPyDBXAJwJoYTta5Sfw/3vVGpU
+fJAKVDoB9w==
+=tJSz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-9c800aca-40d8063e.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-9c800aca-40d8063e.asc
new file mode 100644 (file)
index 0000000..67eedcf
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-a1912208-446a0899.asc b/tests/repo/susetags/data/stable-x86-subset/gpg-pubkey-a1912208-446a0899.asc
new file mode 100644 (file)
index 0000000..279397c
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQILBERqCJkBEACdqhZWdAbUHLIumXMEgv+GFjr1ZzVHgynnFOzztU/8sxZNa9cm
+YV4HZpVfjMr7fos3ArzyiPPt/336cf7w9p79/ZS4rHSNPDMlPCtXYvFxUbvU0/GY
+q4jwcBsrJ0xaJ9CP5bWyAgVKOb7Y6k0ktaLjRR+tDfMsHA4H0ClMoRr6ATw8NL0e
+VCfAHuzqCKvX1If8ng+wTivtAhKvz/WwQiELNELmPfc5tZHOw8NgP/r0Pze18Hn2
+dlAHu0WpC7uoR00vscsMIJiJJPcsxbL1F1eADKnk+wEy8Go+EJeJ5i0WoFbqD52q
+Lv/C/oY6NVtVY0MBwtn+oQNSnQ4JBsB/Akdt53LAi0ZtNQxMyUW+76R8FCOmVCV8
+WGiF5CPRP0yvG80AMBjBjKjHb/v8ov5MnIyFimzAHS1gQcUNxTEYA/5eFwoYcGcK
+weGq9FUjPTzLQAgvp7XmOzHpSAfJ7qysxFTepNsSZZhgizJyInrdQldr+GYcUNqB
+krD9MWmFop975OxhCTEnNv/HcE79r8WD26HzDFYxTiTJbr0pU/ivBzo+rjq+YG2V
+stJk+udVYmZTnC4LmXus8JiNuqBXbxNscwCBpcJ8YcfCV6uh+7E0XfXZsgVUFLp1
+NF+ylYRGTycOlWoZODrnJevZW7N9O3bWRx/G2P4bJD07LsDLe4i5hymf5QAGKbRQ
+Tm92ZWxsIFByb3ZvIEJ1aWxkIChDb250YWN0IHNlY3VyaXR5QG5vdmVsbC5jb20p
+IDxub3ZlbGwtcHJvdm8tYnVpbGRAbm92ZWxsLmNvbT6JAjMEEwECAB0FAkRqCJkG
+CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRBHijLooZEiCKSFD/93vZHCAMLEfksU
+KnvXl08bv1rfuamuyJnE3ANRE5RDyypriHMCnkVxazvQ2WI4W4UEjluL9+SzZwtV
+ZvKVoAr31614nSyWwv2YnJTHfjMG+xRlkolZMnuIiB9PcCBo9+GPU0ABuzo4pEJW
+NIRoSS1NFbAZBhtUnY0cN+trM5QObLl7xXTavLyGk//blkk57fov7GXsQJlZUig0
+l2yt5XNyGpLUnTMDumHh8b389quF+0+ZfdwOy7A768xjipAZiTvIujBrEv51wrxh
+0HBT0VGA0MhD9t0B+Ce4BM9P/iVMO00naaOp6PqMfPPKxQQqer8qy1i6UWBx95SY
+mKZBIvOm2d9PezDxkckCu61r6krx1iKnT1wdprCAkIYwALK118SpbxuyGW0bhRHc
+wsc/akzWH72fS0Xu49mvL4k4A2U9asdeQid3dMgbtm5mSWof0yiU/G4YNn0yeXoY
+oG1VbCAqQbFX1Rvd6GITJVqI+ekW/uMA9BP78dF8wBeG0+QmpQnSf+eOsxB/RT8o
+Kb4hHY+29MUlg+i9ceVt7hoKr03J/uIG5TXFXRYLaI0iAFVlKfWxpqDfS2XA4+dD
+VYt+5RDgBcnxDaTB4FE9GqcYScNfe7+NFtL0p0wOPftbmgZzGjucTmrD8mDUNdqA
+xGK7vlk4GATSfOQlq7G6LXW6RYnInohGBBMRAgAGBQJEazMlAAoJEKhO2uicgArK
+2vMAn0TbVDESEVKVuFZStrfIzOvJQrR9AJsH733Ju1kE99GFrdfCeGqpckmNhg==
+=E+qN
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/installation.xml b/tests/repo/susetags/data/stable-x86-subset/installation.xml
new file mode 100644 (file)
index 0000000..f71e6b7
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<!DOCTYPE productDefines SYSTEM "/usr/share/YaST2/control/control.dtd">
+<productDefines xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
+    <textdomain>control-ISSLE</textdomain>
+    <globals>
+        <enable_firewall config:type="boolean">false</enable_firewall>
+    </globals>
+    <software>
+        <selection_type config:type="symbol">auto</selection_type>
+    </software>
+    <network>
+        <force_static_ip config:type="boolean">true</force_static_ip>
+    </network>
+    <texts>
+        <congratulate>
+            <label>
+&lt;p&gt;
+You have successfully installed the Integrated Stack for SUSE Linux Enterprise.
+&lt;/p&gt;
+&lt;p&gt;
+The services you just installed might require additional
+setup before use. See the Integrated Stack for SUSE Linux Enterprise
+Installation guide for details.
+&lt;/p&gt;
+            </label>
+        </congratulate>
+    </texts>
+    <proposals config:type="list">
+        <proposal>
+            <label>ISSLE Configuration</label>
+            <name>product</name>
+            <stage>continue,normal</stage>
+            <mode>installation</mode>
+            <proposal_modules config:type="list">
+                <proposal_module>yast2-issleconfig</proposal_module>
+            </proposal_modules>
+        </proposal>
+    </proposals>
+    <workflows config:type="list">
+        <workflow>
+            <defaults>
+                <enable_back>no</enable_back>
+                <enable_next>no</enable_next>
+            </defaults>
+            <stage>normal</stage>
+            <mode>installation,normal</mode>
+            <modules config:type="list">
+                <module>
+                    <label>ISSLE Software Selection</label>
+                    <name>sw_single</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <label>ISSLE Configuration</label>
+                    <name>issleconfig</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <label>Release Notes</label>
+                    <name>inst_release_notes</name>
+                    <enable_back>yes</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+                <module>
+                    <name>inst_congratulate</name>
+                    <enable_back>no</enable_back>
+                    <enable_next>yes</enable_next>
+                </module>
+            </modules>
+        </workflow>
+    </workflows>
+    <update>
+        <workflows config:type="list">
+            <workflow>
+                <defaults>
+                    <enable_back>no</enable_back>
+                    <enable_next>no</enable_next>
+                </defaults>
+                <stage>continue,normal</stage>
+                <mode>installation,normal,update</mode>
+                <remove_modules config:type="list">
+                    <remove_module>hostname</remove_module>
+                    <remove_module>root</remove_module>
+                    <remove_module>ca_mgm</remove_module>
+                </remove_modules>
+                <append_modules config:type="list">
+                    <module>
+                        <name>yast2-issleconfig</name>
+                    </module>
+                </append_modules>
+                <insert_modules config:type="list">
+                    <insert_module>
+                        <before>suseconfig</before>
+                        <modules config:type="list">
+                            <module>
+                                <label>ISSLE Configuration</label>
+                                <name>issleconfig</name>
+                                <enable_back>yes</enable_back>
+                                <enable_next>yes</enable_next>
+                            </module>
+                        </modules>
+                    </insert_module>
+                </insert_modules>
+            </workflow>
+        </workflows>
+    </update>
+</productDefines>
diff --git a/tests/repo/susetags/data/stable-x86-subset/license.tar.gz b/tests/repo/susetags/data/stable-x86-subset/license.tar.gz
new file mode 100644 (file)
index 0000000..ecd631d
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset/license.tar.gz differ
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/directory.yast b/tests/repo/susetags/data/stable-x86-subset/media.1/directory.yast
new file mode 100644 (file)
index 0000000..f4fb49c
--- /dev/null
@@ -0,0 +1,6 @@
+info.txt
+license.zip
+media
+products
+products.asc
+products.key
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/info.txt b/tests/repo/susetags/data/stable-x86-subset/media.1/info.txt
new file mode 100644 (file)
index 0000000..d5d1a49
--- /dev/null
@@ -0,0 +1,38 @@
+
+   openSUSE FACTORY 10.3-factory
+   Attention! You are accessing our BETA Distribution.  If you install
+   any package, note that we can NOT GIVE ANY SUPPORT for your system - 
+   no matter if you update from a previous system or do a complete 
+   new installation.
+
+   Use this BETA distribution at your own risk! We recommend it for
+   testing, porting and evaluation purposes but not for any critical
+   production systems.
+
+   If you are curious and would like to help us to find the bugs, you're
+   very welcome.  Please enter bug reports following the instructions
+   given at http://bugs.opensuse.org .
+   If you want to talk about this distribution with others, you can
+   discuss on the mailing list opensuse-factory@opensuse.org.
+
+   Sources for development releases are not distributed via mirrors to
+   reduce the bandwidth and storage on these mirrors.
+
+   You can always find the latest source at
+   http://download.opensuse.org/distribution/SL-OSS-factory/
+
+   In case you need the exact source of this development release you can
+   find it on:
+   http://www.novell.com/products/opensuse/source_code.html
+
+   Alternatively, see
+   http://www.novell.com/products/opensuse/source_code.html or send e-mail
+   to sourcedvd@suse.de to request the source
+   for a specific release of openSUSE on DVD. Please note that we will
+   charge $15 or 15 Euros to cover our costs of distribution.
+
+   Use this distribution at your own risk - and remember to have a
+   lot of fun! :)
+
+                Your openSUSE Team.
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/license.zip b/tests/repo/susetags/data/stable-x86-subset/media.1/license.zip
new file mode 100644 (file)
index 0000000..dd500d6
Binary files /dev/null and b/tests/repo/susetags/data/stable-x86-subset/media.1/license.zip differ
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/media b/tests/repo/susetags/data/stable-x86-subset/media.1/media
new file mode 100644 (file)
index 0000000..d5aa1ef
--- /dev/null
@@ -0,0 +1,3 @@
+SUSE Linux Products GmbH
+20070705102239
+1
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/products b/tests/repo/susetags/data/stable-x86-subset/media.1/products
new file mode 100644 (file)
index 0000000..5563ffd
--- /dev/null
@@ -0,0 +1 @@
+/ SuSE-Linux-STABLE-X86 10.3
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/products.asc b/tests/repo/susetags/data/stable-x86-subset/media.1/products.asc
new file mode 100644 (file)
index 0000000..c6f169d
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.7 (GNU/Linux)
+
+iD8DBQBGjNGIqE7a6JyACsoRAoWnAJkB1wTxOQngy5xvjTiUvwWp65wW5wCg
+hCbA+jw64zZYCa7IM71hBBynFL4=
+=nSHl
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/media.1/products.key b/tests/repo/susetags/data/stable-x86-subset/media.1/products.key
new file mode 100644 (file)
index 0000000..67eedcf
--- /dev/null
@@ -0,0 +1,37 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.4-svn0 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/kde-10.3-71.i586.pat b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/kde-10.3-71.i586.pat
new file mode 100644 (file)
index 0000000..f3ccf93
--- /dev/null
@@ -0,0 +1,178 @@
+# openSUSE Patterns 10.3-71.i586 -- (c) 2007 SUSE LINUX Products GmbH
+# generated on Wed Jul  4 17:31:46 UTC 2007
+
+=Ver: 5.0
+
+=Pat:  kde 10.3 71 i586
+
+=Cat.cs: Grafická rozhraní
+=Cat.da: Grafiske miljøer
+=Cat.de: Grafische Umgebungen
+=Cat: Graphical Environments
+=Cat.en_GB: Graphical Environments
+=Cat.es: Entornos gráficos
+=Cat.et: Graafilised keskkonnad
+=Cat.fi: Graafinen ympäristö
+=Cat.fr: Environnements graphiques
+=Cat.hr: Grafičko okruženje
+=Cat.hu: Grafikus környezet
+=Cat.id: Lingkungan Grafis
+=Cat.it: Ambienti grafici
+=Cat.ja: グラフィック環境
+=Cat.km: បរិស្ថាន​ក្រាហ្វិក
+=Cat.lt: Grafinės aplinkos
+=Cat.nb: Grafisk miljø
+=Cat.nl: Grafische omgevingen
+=Cat.pl: Środowiska graficzne
+=Cat.pt_BR: Ambientes Gráficos
+=Cat.ru: Графическое окружение
+=Cat.sk: Grafické prostredia
+=Cat.sv: Grafiska miljöer
+=Cat.uk: Графічні середовища
+=Cat.zh_CN: 图形环境
+=Cat.zh_TW: 圖形環境
+
+=Ico: kde
+
+=Sum: KDE Desktop Environment
+=Sum.bg: KDE Работна среда
+=Sum.cs: Prostředí KDE
+=Sum.da: KDE skrivebordsmiljø
+=Sum.de: KDE Desktop-Umgebung
+=Sum.el: Ξ Ξ΅Ο\81ιβάλλον Ξ\95Ο\80ΞΉΟ\86άνΡιαΟ\82 Ξ\95Ο\81Ξ³Ξ±Ο\83Ξ—Ξ±Ο\82 KDE 
+=Sum.en_GB: KDE Desktop Environment
+=Sum.es: Escritorio KDE
+=Sum.et: KDE töölaua keskkond
+=Sum.fi: KDE-työpöytäympäristö
+=Sum.fr: Environnement de bureau KDE
+=Sum.hu: KDE asztali környezet
+=Sum.it: Ambiente Desktop KDE
+=Sum.ja: KDE デスクトップ環境
+=Sum.km: បរិស្ថាន​ផ្ទៃតុ KDE
+=Sum.ko: KDE 데스크탑 환경
+=Sum.lt: KDE darbalaukio aplinka
+=Sum.nb: Skrivebordsmiljøet KDE
+=Sum.nl: KDE Desktop Environment
+=Sum.pl: Środowisko graficzne KDE
+=Sum.pt_BR: KDE Ambiente de Desktop
+=Sum.ru: Рабочая среда KDE
+=Sum.sk: Prostredie KDE
+=Sum.sl: Namizno okolje KDE
+=Sum.sv: Skrivbordsmiljön KDE
+=Sum.uk: Стільничне середовище KDE
+=Sum.zh_CN: KDE 桌面环境
+=Sum.zh_TW: KDE 桌面環境
+
++Des.bg:
+KDE е мощна свободна графична среда за работни станции с Линукс, комбинираща в себе си лекота на употреба, съвременна функционалност и изключителен графичен дизайн с техническото превъзходство операционната система Линукс.
+-Des.bg:
++Des.ca:
+KDE és un potent entorn gràfic d'escriptori de programari lliure per a les estacions de treball de Linux. Combina la facilitat d'ús, la funcionalitat contemporània i un disseny gràfic excepcional amb la tecnologia del sistema operatiu Linux.
+-Des.ca:
++Des.cs:
+KDE je svobodné grafické prostředí pro pracovní stanice. Snoubí v sobě uživatelskou přívětivost, moderní funkcionalitu a atraktivní vzhled s linuxovou technologií.
+-Des.cs:
++Des.da:
+KDE er et stærkt grafisk skrivebordsmiljø til Linux-arbejdsstationer og er fri software. Det er både let at bruge, har mange funktioner og et flot grafisk design med teknologien fra Linux-operativsystemet.
+-Des.da:
++Des.de:
+KDE ist eine leistungsstarke kostenlose grafische Desktop-Umgebung für Linux-Arbeitsstationen. Das Programm kombiniert Benutzerfreundlichkeit, moderne Funktionsweise und hervorragendes grafisches Design mit der Technologie des Linux-Betriebssystems.
+-Des.de:
++Des:
+KDE is a powerful free software graphical desktop environment for Linux workstations. It combines ease of use, contemporary functionality, and outstanding graphical design with the technology of the Linux operating system.
+-Des:
++Des.en_GB:
+KDE is a powerful free software graphical desktop environment for Linux workstations. It combines ease of use, contemporary functionality, and outstanding graphical design with the technology of the Linux operating system.
+-Des.en_GB:
++Des.es:
+KDE es un potente programa escritorio gráfico gratuito para estaciones de trabajo Linux. Combina la facilidad de uso, funciones contemporáneas y un diseño gráfico insuperable con la tecnología del sistema operativo Linux.
+-Des.es:
++Des.fi:
+KDE on vaikuttava vapaa työpöytäympäristö Linux-työasemille. Siinä yhdistyy helppokäyttöisyys, käytettävyys ja upea graafinen suunnittelu Linux-käyttöjärjestelmäteknologian kanssa.
+-Des.fi:
++Des.fr:
+KDE est un environnement de bureau graphique gratuit et puissant pour les stations de travail Linux. Il allie simplicité d'utilisation, fonctionnalités modernes et conception graphique exceptionnelle grâce à la technologie du système d'exploitation Linux.
+-Des.fr:
++Des.hr:
+KDE je moćno besplatano grafičko okruženje radne površine za Linux radne stanice. Kombinira lakoću korištenja, svakodnevnu funkcionalnost i izvrstan grafički dizajn zajedno s tehnologijom Linux operativnog sustava.
+-Des.hr:
++Des.hu:
+A KDE egy nagyteljesítményű, ingyenes, grafikus, asztali környezet Linux munkaállomásokhoz. Az egyszerű használatot, a modern funkcionalitást, valamint a kiemelkedő grafikai kivitelt egyesíti a Linux operációs rendszer technológiai előnyeivel.
+-Des.hu:
++Des.id:
+KDE merupakan lingkungan desktop grafis free software yang powerful untuk workstation Linux. Ia menggabungkan kemudahan, fungsionalitas kontemporer, dan desain grafis yang hebat dengan teknologi sistem operasi Linux.
+-Des.id:
++Des.it:
+KDE rappresenta un efficiente ambiente desktop grafico software gratuito per le workstation Linux. Associa facilità d'uso, funzioni aggiornate e ambiente grafico accattivante alla tecnologia del sistema operativo Linux.
+-Des.it:
++Des.ja:
+KDEは、Linuxワークステーション用の無料で強力なソフトウェアグラフィカルデスクトップ環境です。 使いやすさ、現代の機能性、そして優れたグラフィカルデザインをLinuxオペレーティングシステムの技術と統合します。
+-Des.ja:
++Des.km:
+KDE គឺ​ជា​បរិស្ថាន​ផ្ទៃតុ​ក្រាហ្វិក​ឥត​គិត​ថ្លៃ​សម្រាប់​ស្ថានីយ​ការងារ​របស់​លីនុច ។ វា​រួមបញ្ចូល​នូវ​ភាព​ងាយស្រួល​ក្នុងការ​ប្រើប្រាស់ និង​មុខងារ​ដែល​វិវត្ត​តាម​សម័យកាល​ជានិច្ច ជាមួយ​នឹង​បច្ចេកវិជ្ជា​រចនា​ក្រាហ្វិក​គំរូ​តាម​​ប្រព័ន្ធ​ប្រតិបត្តិការ​លីនុច ។
+-Des.km:
++Des.lt:
+KDE yra galinga nemokama grafinė darbalaukio aplinka Linux darbo stotims. Ji sujungia lengvą naudojimą, šiuolaikinį funkcionalumą ir puikų grafinį dizainą su Linux operacinės sistemos technologijomis.
+-Des.lt:
++Des.nb:
+KDE er et effektivt og elegant grafisk skrivebordsmiljø for Linux-arbeidsstasjoner. KDE kombinerer brukervennlighet, avanserte funksjoner og lekker grafisk design med teknologien i Linux-operativsystemet.
+-Des.nb:
++Des.nl:
+KDE is een krachtige, vrije grafische desktop environment voor Linux-computers. Het combineert eenvoudig gebruik, uitgebreide functionaliteit en een uitstekend grafisch design met de technologie van het Linux-besturingssysteem.
+-Des.nl:
++Des.pl:
+KDE jest potężnym środowiskiem graficznym dla linuksowych stacji roboczych. Łączy łatwość obsługi, szerokie możliwości i atrakcyjny wygląd z technologiamisystemu operacyjnego Linux.
+-Des.pl:
++Des.pt:
+O KDE é um ambiente de trabalho gráfico livre e potente, para estações de trabalho Linux. Este combina a facilidade de utilização, funcionalidade contemporânea, e design gráfico de grande destaque, com a tecnologia do sistema operativo Linux.
+-Des.pt:
++Des.pt_BR:
+O KDE é um poderoso ambiente gráfico de área de trabalho de software gratuito para estações de trabalho Linux. Ele combina a facilidade de uso, a funcionalidade contemporânea e o excelente design gráfico à tecnologia do sistema operacional Linux.
+-Des.pt_BR:
++Des.ru:
+KDE это мощная бесплатная графическая система для Linux рабочих станций. Она комбинирует легкость в использовании, современную функциональность, превосходный дизайн с технологиями оперативной системы Linux.
+-Des.ru:
++Des.sk:
+KDE je  výkonné grafické prostredie a slobodný softvér, pre linuxové pracovné stanice. Kombinuje jednoduchosž použitia, modernú funkcionalitu a výnimočný grafický dizajn spolu s technológiou operačného systému Linux.
+-Des.sk:
++Des.sv:
+KDE är en kraftfull fri programvara för grafisk skrivbordsmiljö för Linux-arbetsstationer. Den kombinerar användarvänlighet, moderna funktioner och fantastisk grafisk formgivning med tekniken hos operativsystemet Linux.
+-Des.sv:
++Des.uk:
+KDE - це потужне графічне середовище для робочих станцій Linux. Воно поєднує простоту використання, модерну функціональність та видатний графічний дизайн з операційною системою Linux.
+-Des.uk:
++Des.zh_CN:
+KDE 是一个软件图形桌面环境,它用于 Linux 工作站,不但功能强大,而且还可以免费使用。 它将先进的功能、独特的图形设计与 Linux 操作系统的技术相结合,使用方便。
+-Des.zh_CN:
++Des.zh_TW:
+KDE 是針對 Linux 工作站所設計、功能強大的免費軟體圖形桌面環境。它將容易使用、現代化功能及傑出的圖形設計等結合到 Linux 作業系統之中。
+-Des.zh_TW:
+
++Rec:
+kde_internet
+multimedia
+office
+kde_utilities
+imaging
+games
+non_oss
+non_oss_java
+xgl
+-Rec:
+
++Req:
+kde_basis
+-Req:
+
+=Vis: true
+
+=Ord: 1520
+
++Prc:
+opensuse-quickstart_en
+-Prc:
++Psg:
+kdeedu3
+kiosktool
+-Psg:
+
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages
new file mode 100644 (file)
index 0000000..8664af9
--- /dev/null
@@ -0,0 +1,512 @@
+##----------------------------------------
+=Pkg: kdelibs3 3.5.7 24 i586
+=Cks: SHA1 05f0647241433d01636785fd282cc824a6527269
++Req:
+rpmlib(VersionedDependencies) <= 3.0.3-1
+qt3 >= 3.3.8
+openssl
+hicolor-icon-theme
+sudo
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+/bin/sh
+/usr/bin/perl
+libDCOP.so.4
+libHalf.so.4
+libICE.so.6
+libIlmImf.so.4
+libSM.so.6
+libX11.so.6
+libXau.so.6
+libXcursor.so.1
+libXext.so.6
+libXfixes.so.3
+libXft.so.2
+libXi.so.6
+libXinerama.so.1
+libXrandr.so.2
+libXrender.so.1
+libacl.so.1
+libacl.so.1(ACL_1.0)
+libart_lgpl_2.so.2
+libasound.so.2
+libasound.so.2(ALSA_0.9)
+libaspell.so.15
+libattr.so.1
+libattr.so.1(ATTR_1.0)
+libbz2.so.1
+libc.so.6
+libc.so.6(GLIBC_2.0)
+libc.so.6(GLIBC_2.1)
+libc.so.6(GLIBC_2.1.2)
+libc.so.6(GLIBC_2.1.3)
+libc.so.6(GLIBC_2.2)
+libc.so.6(GLIBC_2.3)
+libc.so.6(GLIBC_2.3.4)
+libc.so.6(GLIBC_2.4)
+libcups.so.2
+libdl.so.2
+libdl.so.2(GLIBC_2.0)
+libdl.so.2(GLIBC_2.1)
+libdns_sd.so.1
+libexpat.so.1
+libfam.so.0
+libfontconfig.so.1
+libfreetype.so.6
+libgcc_s.so.1
+libgcc_s.so.1(GCC_3.0)
+libgcc_s.so.1(GLIBC_2.0)
+libgssapi_krb5.so.2
+libgssapi_krb5.so.2(gssapi_krb5_2_MIT)
+libidn.so.11
+libjasper.so.1
+libjpeg.so.62
+libkabc.so.1
+libkabc_dir.so.1
+libkabc_file.so.1
+libkabc_ldapkio.so.1
+libkatepartinterfaces.so.0
+libkdecore.so.4
+libkdefakes.so.4
+libkdefx.so.4
+libkdeinit_cupsdconf.so
+libkdeinit_dcopserver.so
+libkdeinit_kaddprinterwizard.so
+libkdeinit_kbuildsycoca.so
+libkdeinit_kcmshell.so
+libkdeinit_kconf_update.so
+libkdeinit_kcookiejar.so
+libkdeinit_kded.so
+libkdeinit_kio_http_cache_cleaner.so
+libkdeinit_kio_uiserver.so
+libkdeinit_klauncher.so
+libkdeinit_knotify.so
+libkdemm.so.0
+libkdeprint.so.4
+libkdeprint_management.so.4
+libkdesu.so.4
+libkdeui.so.4
+libkhtml.so.4
+libkio.so.4
+libkjs.so.1
+libkmediaplayer.so.0
+libknewstuff.so.1
+libkntlm.so.0
+libkparts.so.2
+libkresources.so.1
+libkscript.so.0
+libkspell2.so.1
+libktexteditor.so.0
+libkutils.so.1
+libkwalletbackend.so.1
+libkwalletclient.so.1
+libm.so.6
+libm.so.6(GLIBC_2.0)
+libnetworkstatus.so.0
+libpcre.so.0
+libpng12.so.0
+libpthread.so.0
+libpthread.so.0(GLIBC_2.0)
+libpthread.so.0(GLIBC_2.2)
+libqt-mt.so.3
+libresolv.so.2
+libresolv.so.2(GLIBC_2.0)
+libresolv.so.2(GLIBC_2.2)
+libstdc++.so.6
+libstdc++.so.6(CXXABI_1.3)
+libstdc++.so.6(CXXABI_1.3.1)
+libstdc++.so.6(GLIBCXX_3.4)
+libstdc++.so.6(GLIBCXX_3.4.9)
+libtiff.so.3
+libutil.so.1
+libvcard.so.0
+libxcb-xlib.so.0
+libxcb.so.1
+libxml2.so.2
+libxslt.so.1
+libz.so.1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Req:
++Prq:
+rpmlib(VersionedDependencies) <= 3.0.3-1
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Prq:
++Prv:
+kups
+keramik
+kdelibs3-cups
+kdelibs3-33addons
+kdepim3-networkstatus
+kdelibs3_base = 3.3
+cupsdconf.so
+dcopserver.so
+highcolor.so
+highcontrast.so
+kabc_dir.so
+kabc_file.so
+kabc_ldapkio.so
+kabcformat_binary.so
+kaddprinterwizard.so
+kbuildsycoca.so
+kbzip2filter.so
+kcm_kresources.so
+kcmshell.so
+kconf_update.so
+kcookiejar.so
+kded.so
+kded_kcookiejar.so
+kded_kdeprintd.so
+kded_kdetrayproxy.so
+kded_kpasswdserver.so
+kded_kssld.so
+kded_kwalletd.so
+kded_networkstatus.so
+kded_proxyscout.so
+kdeprint_cups.so
+kdeprint_ext.so
+kdeprint_lpdunix.so
+kdeprint_lpr.so
+kdeprint_rlpr.so
+kdeprint_tool_escputil.so
+kdewidgets.so
+keramik.so
+kfileaudiopreview.so
+kgzipfilter.so
+khtmlimagepart.so
+kimg_dds.so
+kimg_eps.so
+kimg_exr.so
+kimg_hdr.so
+kimg_ico.so
+kimg_jp2.so
+kimg_pcx.so
+kimg_psd.so
+kimg_rgb.so
+kimg_tga.so
+kimg_tiff.so
+kimg_xcf.so
+kimg_xview.so
+kio_file.so
+kio_ftp.so
+kio_ghelp.so
+kio_help.so
+kio_http.so
+kio_http_cache_cleaner.so
+kio_metainfo.so
+kio_uiserver.so
+kjavaappletviewer.so
+klauncher.so
+knotify.so
+kspell_aspell.so
+kspell_ispell.so
+kstyle_highcontrast_config.so
+kstyle_plastik_config.so
+ktexteditor_docwordcompletion.so
+ktexteditor_insertfile.so
+ktexteditor_isearch.so
+ktexteditor_kdatatool.so
+kthemestyle.so
+libDCOP.so.4
+libconnectionmanager.so.0
+libkabc.so.1
+libkabc_dir.so.1
+libkabc_file.so.1
+libkabc_ldapkio.so.1
+libkatepart.so
+libkatepartinterfaces.so.0
+libkcertpart.so
+libkdecore.so.4
+libkdefakes.so.4
+libkdefx.so.4
+libkdeinit_cupsdconf.so
+libkdeinit_dcopserver.so
+libkdeinit_kaddprinterwizard.so
+libkdeinit_kbuildsycoca.so
+libkdeinit_kcmshell.so
+libkdeinit_kconf_update.so
+libkdeinit_kcookiejar.so
+libkdeinit_kded.so
+libkdeinit_kio_http_cache_cleaner.so
+libkdeinit_kio_uiserver.so
+libkdeinit_klauncher.so
+libkdeinit_knotify.so
+libkdemm.so.0
+libkdeprint.so.4
+libkdeprint_management.so.4
+libkdeprint_management_module.so
+libkdesasl.so.1
+libkdesu.so.4
+libkdeui.so.4
+libkdnssd.so.1
+libkhtml.so.4
+libkhtmlpart.so
+libkimproxy.so.0
+libkio.so.4
+libkjava.so.1
+libkjs.so.1
+libkmdi.so.1
+libkmdi2.so.1
+libkmediaplayer.so.0
+libkmid.so.0
+libkmultipart.so
+libknewstuff.so.1
+libkntlm.so.0
+libkparts.so.2
+libkresources.so.1
+libkscreensaver.so.4
+libkscript.so.0
+libkspell.so.4
+libkspell2.so.1
+libktexteditor.so.0
+libkunittest.so.1
+libkutils.so.1
+libkwalletbackend.so.1
+libkwalletclient.so.1
+libnetworkstatus.so.0
+libshellscript.so
+libvcard.so.0
+light.so
+plastik.so
+kdelibs3 = 3.5.7-24
+-Prv:
++Obs:
+kde3-i18n
+kups
+keramik
+kdelibs3-cups
+kdelibs3-33addons
+kdepim3-networkstatus
+-Obs:
++Rec:
+ispell
+ispell_dictionary
+enscript
+-Rec:
+=Grp: System/GUI/KDE
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: kdelibs3 3.5.7 24 src
+=Tim: 1183399094
+=Loc: 1 kdelibs3-3.5.7-24.i586.rpm
+=Siz: 16356019 38850584
++Aut:
+The KDE Team <kde@kde.org>
+-Aut:
+##----------------------------------------
+=Pkg: kdelibs3-arts 3.5.7 24 i586
+=Cks: SHA1 84d8c8e875395b8caaed90ae40bdfad3fe903eb7
++Req:
+arts >= 1.5.7
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+libX11.so.6
+libartsflow.so.1
+libartsflow_idl.so.1
+libc.so.6
+libc.so.6(GLIBC_2.0)
+libc.so.6(GLIBC_2.1.3)
+libc.so.6(GLIBC_2.4)
+libkdecore.so.4
+libkdeui.so.4
+libkio.so.4
+libkmedia2_idl.so.1
+libmcop.so.1
+libpthread.so.0
+libpthread.so.0(GLIBC_2.0)
+libqt-mt.so.3
+libqtmcop.so.1
+libsoundserver_idl.so.1
+libstdc++.so.6
+libstdc++.so.6(CXXABI_1.3)
+libstdc++.so.6(GLIBCXX_3.4)
+libstdc++.so.6(GLIBCXX_3.4.9)
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Req:
++Prq:
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Prq:
++Prv:
+kdelibs3:/opt/kde3/bin/artsmessage
+libartskde.so.1
+kdelibs3-arts = 3.5.7-24
+-Prv:
++Rec:
+kdemultimedia3-arts
+-Rec:
+=Grp: System/GUI/KDE
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: kdelibs3 3.5.7 24 src
+=Tim: 1183399094
+=Loc: 1 kdelibs3-arts-3.5.7-24.i586.rpm
+=Siz: 183999 393828
++Aut:
+The KDE Team <kde@kde.org>
+-Aut:
+##----------------------------------------
+=Pkg: kdelibs3-debuginfo 3.5.7 24 i586
+=Cks: SHA1 0f4c7798729fae70830fd03962dff8cdcadb7bdc
++Req:
+kdelibs3 = 3.5.7-24
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Req:
++Prq:
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Prq:
++Prv:
+kdelibs3-debuginfo = 3.5.7-24
+-Prv:
+=Grp: Development/Debug
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: kdelibs3 3.5.7 24 src
+=Tim: 1183399094
+=Loc: 1 kdelibs3-debuginfo-3.5.7-24.i586.rpm
+=Siz: 44452218 139124954
++Aut:
+The KDE Team <kde@kde.org>
+-Aut:
+##----------------------------------------
+=Pkg: kdelibs3-devel 3.5.7 24 i586
+=Cks: SHA1 46cfb73e29561af9e5445f6c525821856d067e61
++Req:
+qt3-devel
+libvorbis-devel
+kdelibs3 = 3.5.7
+autoconf
+automake
+libxslt-devel
+libxml2-devel
+libart_lgpl-devel
+libjpeg-devel
+kdelibs3-doc
+libtiff-devel
+openssl-devel
+unsermake
+update-desktop-files
+libdrm-devel
+dbus-1-qt3-devel
+libattr-devel
+libacl-devel
+avahi-compat-mDNSResponder-devel
+libbz2-devel
+kdelibs3-arts
+fam-devel
+pcre-devel
+libidn-devel
+arts-devel
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+/bin/bash
+/bin/sh
+/usr/bin/env
+/usr/bin/perl
+libc.so.6
+libc.so.6(GLIBC_2.0)
+libc.so.6(GLIBC_2.1.3)
+libc.so.6(GLIBC_2.4)
+libkdecore.so.4
+libkio.so.4
+libkunittest.so.1
+libqt-mt.so.3
+libstdc++.so.6
+libstdc++.so.6(CXXABI_1.3)
+libstdc++.so.6(GLIBCXX_3.4)
+libstdc++.so.6(GLIBCXX_3.4.9)
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Req:
++Prq:
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Prq:
++Prv:
+perl(Ast)
+perl(Iter)
+perl(kalyptusCxxToDcopIDL)
+perl(kdocAstUtil)
+perl(kdocParseDoc)
+perl(kdocUtil)
+kdelibs3-devel = 3.5.7-24
+-Prv:
+=Grp: System/GUI/KDE
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: kdelibs3 3.5.7 24 src
+=Tim: 1183399094
+=Loc: 1 kdelibs3-devel-3.5.7-24.i586.rpm
+=Siz: 1403143 6898685
++Aut:
+The KDE Team <kde@kde.org>
+-Aut:
+##----------------------------------------
+=Pkg: kdelibs3-doc 3.5.7 24 i586
+=Cks: SHA1 28714c6b0ab4dbed12039d511995d3085721ed4e
++Req:
+sgml-skel
+libxml2
+/usr/bin/sgml-register-catalog
+/usr/bin/xmlcatalog
+/usr/bin/edit-xml-catalog
+sed
+grep
+awk
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+libbz2.so.1
+libc.so.6
+libc.so.6(GLIBC_2.0)
+libc.so.6(GLIBC_2.1)
+libc.so.6(GLIBC_2.4)
+libkdecore.so.4
+libkio.so.4
+libqt-mt.so.3
+libstdc++.so.6
+libstdc++.so.6(CXXABI_1.3)
+libstdc++.so.6(GLIBCXX_3.4)
+libxml2.so.2
+libxslt.so.1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Req:
++Prq:
+/usr/bin/sgml-register-catalog
+/usr/bin/xmlcatalog
+/usr/bin/edit-xml-catalog
+sed
+grep
+awk
+/bin/sh
+/bin/sh
+rpmlib(PayloadFilesHavePrefix) <= 4.0-1
+rpmlib(CompressedFileNames) <= 3.0.4-1
+rpmlib(PayloadIsBzip2) <= 3.0.5-1
+-Prq:
++Prv:
+kdelibs3:/opt/kde3/share/apps/ksgmltools2
+kdelibs3_doc
+kdelibs3-doc = 3.5.7-24
+-Prv:
+=Grp: System/GUI/KDE
+=Lic: BSD License and BSD-like, GNU General Public License (GPL)
+=Src: kdelibs3 3.5.7 24 src
+=Tim: 1183399094
+=Loc: 1 kdelibs3-doc-3.5.7-24.i586.rpm
+=Siz: 918394 5908265
++Aut:
+The KDE Team <kde@kde.org>
+-Aut:
+##----------------------------------------
\ No newline at end of file
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.DU b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.DU
new file mode 100644 (file)
index 0000000..462fb94
--- /dev/null
@@ -0,0 +1,498 @@
+##----------------------------------------
+=Pkg: kdelibs3 3.5.7 24 i586
++Dir:
+/ 0 39444 0 3068
+etc/ 1 12 1 1
+etc/xdg/ 0 12 0 1
+etc/xdg/menus/ 12 0 1 0
+opt/ 0 39366 0 3054
+opt/kde3/ 0 39366 0 3054
+opt/kde3/bin/ 1104 0 51 0
+opt/kde3/lib/ 19666 5798 53 152
+opt/kde3/lib/kde3/ 4968 830 138 14
+opt/kde3/lib/kde3/plugins/ 0 830 0 14
+opt/kde3/lib/kde3/plugins/designer/ 108 0 2 0
+opt/kde3/lib/kde3/plugins/styles/ 722 0 12 0
+opt/kde3/share/ 0 12798 0 2798
+opt/kde3/share/applications/ 0 13 0 1
+opt/kde3/share/applications/kde/ 13 0 1 0
+opt/kde3/share/apps/ 0 3902 0 410
+opt/kde3/share/apps/LICENSES/ 57 0 5 0
+opt/kde3/share/apps/kabc/ 229 4 1 1
+opt/kde3/share/apps/kabc/formats/ 4 0 1 0
+opt/kde3/share/apps/katepart/ 11 2194 2 134
+opt/kde3/share/apps/katepart/scripts/ 16 10 5 2
+opt/kde3/share/apps/katepart/scripts/indent/ 10 0 2 0
+opt/kde3/share/apps/katepart/syntax/ 2168 0 127 0
+opt/kde3/share/apps/kcertpart/ 1 0 1 0
+opt/kde3/share/apps/kcm_componentchooser/ 38 0 2 0
+opt/kde3/share/apps/kconf_update/ 8 0 8 0
+opt/kde3/share/apps/kdeprint/ 194 299 13 99
+opt/kde3/share/apps/kdeprint/filters/ 156 0 20 0
+opt/kde3/share/apps/kdeprint/icons/ 0 91 0 55
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/ 0 91 0 55
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/16x16/ 0 20 0 20
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/16x16/actions/ 3 0 3 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/16x16/apps/ 17 0 17 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/22x22/ 0 32 0 19
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/22x22/actions/ 31 0 18 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/22x22/apps/ 1 0 1 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/32x32/ 0 26 0 12
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/32x32/actions/ 3 0 1 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/32x32/apps/ 23 0 11 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/48x48/ 0 6 0 2
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/48x48/apps/ 6 0 2 0
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/64x64/ 0 7 0 2
+opt/kde3/share/apps/kdeprint/icons/crystalsvg/64x64/apps/ 7 0 2 0
+opt/kde3/share/apps/kdeprint/pics/ 25 0 18 0
+opt/kde3/share/apps/kdeprint/plugins/ 25 0 5 0
+opt/kde3/share/apps/kdeprint/tools/ 2 0 1 0
+opt/kde3/share/apps/kdeui/ 0 93 0 26
+opt/kde3/share/apps/kdeui/about/ 45 0 23 0
+opt/kde3/share/apps/kdeui/pics/ 48 0 3 0
+opt/kde3/share/apps/kdewidgets/ 0 60 0 42
+opt/kde3/share/apps/kdewidgets/pics/ 60 0 42 0
+opt/kde3/share/apps/khtml/ 6 50 4 12
+opt/kde3/share/apps/khtml/css/ 9 0 2 0
+opt/kde3/share/apps/khtml/icons/ 0 41 0 10
+opt/kde3/share/apps/khtml/icons/crystalsvg/ 0 41 0 10
+opt/kde3/share/apps/khtml/icons/crystalsvg/128x128/ 0 14 0 1
+opt/kde3/share/apps/khtml/icons/crystalsvg/128x128/actions/ 14 0 1 0
+opt/kde3/share/apps/khtml/icons/crystalsvg/16x16/ 0 4 0 3
+opt/kde3/share/apps/khtml/icons/crystalsvg/16x16/actions/ 4 0 3 0
+opt/kde3/share/apps/khtml/icons/crystalsvg/22x22/ 0 4 0 2
+opt/kde3/share/apps/khtml/icons/crystalsvg/22x22/actions/ 4 0 2 0
+opt/kde3/share/apps/khtml/icons/crystalsvg/32x32/ 0 6 0 2
+opt/kde3/share/apps/khtml/icons/crystalsvg/32x32/actions/ 6 0 2 0
+opt/kde3/share/apps/khtml/icons/crystalsvg/48x48/ 0 5 0 1
+opt/kde3/share/apps/khtml/icons/crystalsvg/48x48/actions/ 5 0 1 0
+opt/kde3/share/apps/khtml/icons/crystalsvg/64x64/ 0 8 0 1
+opt/kde3/share/apps/khtml/icons/crystalsvg/64x64/actions/ 8 0 1 0
+opt/kde3/share/apps/kio_uiserver/ 0 1 0 1
+opt/kde3/share/apps/kio_uiserver/icons/ 0 1 0 1
+opt/kde3/share/apps/kio_uiserver/icons/crystalsvg/ 0 1 0 1
+opt/kde3/share/apps/kio_uiserver/icons/crystalsvg/16x16/ 0 1 0 1
+opt/kde3/share/apps/kio_uiserver/icons/crystalsvg/16x16/apps/ 1 0 1 0
+opt/kde3/share/apps/kjava/ 156 2 3 1
+opt/kde3/share/apps/kjava/icons/ 0 2 0 1
+opt/kde3/share/apps/kjava/icons/crystalsvg/ 0 2 0 1
+opt/kde3/share/apps/kjava/icons/crystalsvg/16x16/ 0 2 0 1
+opt/kde3/share/apps/kjava/icons/crystalsvg/16x16/actions/ 2 0 1 0
+opt/kde3/share/apps/knewstuff/ 1 0 1 0
+opt/kde3/share/apps/knotify/ 119 0 1 0
+opt/kde3/share/apps/kssl/ 159 0 1 0
+opt/kde3/share/apps/kstyle/ 0 183 0 47
+opt/kde3/share/apps/kstyle/pixmaps/ 0 90 0 32
+opt/kde3/share/apps/kstyle/pixmaps/riscos/ 90 0 32 0
+opt/kde3/share/apps/kstyle/themes/ 93 0 15 0
+opt/kde3/share/apps/ktexteditor_docwordcompletion/ 1 0 1 0
+opt/kde3/share/apps/ktexteditor_insertfile/ 1 0 1 0
+opt/kde3/share/apps/ktexteditor_isearch/ 1 0 1 0
+opt/kde3/share/apps/ktexteditor_kdatatool/ 1 0 1 0
+opt/kde3/share/apps/proxyscout/ 33 0 1 0
+opt/kde3/share/autostart/ 7 0 1 0
+opt/kde3/share/config/ 202 18 10 6
+opt/kde3/share/config/colors/ 10 0 4 0
+opt/kde3/share/config/ui/ 8 0 2 0
+opt/kde3/share/emoticons/ 0 79 0 42
+opt/kde3/share/emoticons/Default/ 79 0 42 0
+opt/kde3/share/icons/ 0 6549 0 1906
+opt/kde3/share/icons/crystalsvg/ 8 6480 1 1898
+opt/kde3/share/icons/crystalsvg/128x128/ 0 1622 0 151
+opt/kde3/share/icons/crystalsvg/128x128/actions/ 27 0 3 0
+opt/kde3/share/icons/crystalsvg/128x128/apps/ 33 0 2 0
+opt/kde3/share/icons/crystalsvg/128x128/devices/ 269 0 22 0
+opt/kde3/share/icons/crystalsvg/128x128/filesystems/ 529 0 40 0
+opt/kde3/share/icons/crystalsvg/128x128/mimetypes/ 764 0 84 0
+opt/kde3/share/icons/crystalsvg/16x16/ 0 429 0 408
+opt/kde3/share/icons/crystalsvg/16x16/actions/ 248 0 228 0
+opt/kde3/share/icons/crystalsvg/16x16/apps/ 3 0 2 0
+opt/kde3/share/icons/crystalsvg/16x16/devices/ 47 0 47 0
+opt/kde3/share/icons/crystalsvg/16x16/filesystems/ 45 0 45 0
+opt/kde3/share/icons/crystalsvg/16x16/mimetypes/ 86 0 86 0
+opt/kde3/share/icons/crystalsvg/22x22/ 0 552 0 346
+opt/kde3/share/icons/crystalsvg/22x22/actions/ 316 0 211 0
+opt/kde3/share/icons/crystalsvg/22x22/apps/ 4 0 2 0
+opt/kde3/share/icons/crystalsvg/22x22/devices/ 41 0 23 0
+opt/kde3/share/icons/crystalsvg/22x22/filesystems/ 43 0 23 0
+opt/kde3/share/icons/crystalsvg/22x22/mimetypes/ 148 0 87 0
+opt/kde3/share/icons/crystalsvg/32x32/ 0 782 0 359
+opt/kde3/share/icons/crystalsvg/32x32/actions/ 347 0 181 0
+opt/kde3/share/icons/crystalsvg/32x32/apps/ 5 0 2 0
+opt/kde3/share/icons/crystalsvg/32x32/devices/ 119 0 47 0
+opt/kde3/share/icons/crystalsvg/32x32/filesystems/ 107 0 41 0
+opt/kde3/share/icons/crystalsvg/32x32/mimetypes/ 204 0 88 0
+opt/kde3/share/icons/crystalsvg/48x48/ 0 842 0 223
+opt/kde3/share/icons/crystalsvg/48x48/actions/ 148 0 39 0
+opt/kde3/share/icons/crystalsvg/48x48/apps/ 10 0 2 0
+opt/kde3/share/icons/crystalsvg/48x48/devices/ 185 0 47 0
+opt/kde3/share/icons/crystalsvg/48x48/filesystems/ 188 0 44 0
+opt/kde3/share/icons/crystalsvg/48x48/mimetypes/ 311 0 91 0
+opt/kde3/share/icons/crystalsvg/64x64/ 0 917 0 184
+opt/kde3/share/icons/crystalsvg/64x64/actions/ 21 0 5 0
+opt/kde3/share/icons/crystalsvg/64x64/apps/ 13 0 2 0
+opt/kde3/share/icons/crystalsvg/64x64/devices/ 258 0 47 0
+opt/kde3/share/icons/crystalsvg/64x64/filesystems/ 240 0 40 0
+opt/kde3/share/icons/crystalsvg/64x64/mimetypes/ 385 0 90 0
+opt/kde3/share/icons/crystalsvg/scalable/ 0 1336 0 227
+opt/kde3/share/icons/crystalsvg/scalable/actions/ 304 0 56 0
+opt/kde3/share/icons/crystalsvg/scalable/apps/ 7 0 1 0
+opt/kde3/share/icons/crystalsvg/scalable/devices/ 290 0 47 0
+opt/kde3/share/icons/crystalsvg/scalable/filesystems/ 212 0 44 0
+opt/kde3/share/icons/crystalsvg/scalable/mimetypes/ 523 0 79 0
+opt/kde3/share/icons/hicolor/ 0 61 0 7
+opt/kde3/share/icons/hicolor/128x128/ 0 24 0 1
+opt/kde3/share/icons/hicolor/128x128/apps/ 24 0 1 0
+opt/kde3/share/icons/hicolor/16x16/ 0 1 0 1
+opt/kde3/share/icons/hicolor/16x16/apps/ 1 0 1 0
+opt/kde3/share/icons/hicolor/22x22/ 0 2 0 1
+opt/kde3/share/icons/hicolor/22x22/apps/ 2 0 1 0
+opt/kde3/share/icons/hicolor/32x32/ 0 3 0 1
+opt/kde3/share/icons/hicolor/32x32/apps/ 3 0 1 0
+opt/kde3/share/icons/hicolor/48x48/ 0 5 0 1
+opt/kde3/share/icons/hicolor/48x48/apps/ 5 0 1 0
+opt/kde3/share/icons/hicolor/64x64/ 0 8 0 1
+opt/kde3/share/icons/hicolor/64x64/apps/ 8 0 1 0
+opt/kde3/share/icons/hicolor/scalable/ 0 18 0 1
+opt/kde3/share/icons/hicolor/scalable/apps/ 18 0 1 0
+opt/kde3/share/locale/ 259 0 1 0
+opt/kde3/share/mimelnk/ 42 1290 1 317
+opt/kde3/share/mimelnk/all/ 9 0 2 0
+opt/kde3/share/mimelnk/application/ 761 0 179 0
+opt/kde3/share/mimelnk/audio/ 101 0 27 0
+opt/kde3/share/mimelnk/image/ 144 0 38 0
+opt/kde3/share/mimelnk/inode/ 21 0 6 0
+opt/kde3/share/mimelnk/message/ 9 0 2 0
+opt/kde3/share/mimelnk/model/ 3 0 1 0
+opt/kde3/share/mimelnk/multipart/ 9 0 2 0
+opt/kde3/share/mimelnk/text/ 175 0 41 0
+opt/kde3/share/mimelnk/uri/ 12 0 6 0
+opt/kde3/share/mimelnk/video/ 46 0 13 0
+opt/kde3/share/services/ 226 80 62 12
+opt/kde3/share/services/kded/ 70 0 8 0
+opt/kde3/share/services/kresources/ 3 7 1 3
+opt/kde3/share/services/kresources/kabc/ 7 0 3 0
+opt/kde3/share/servicetypes/ 131 0 29 0
+usr/ 0 65 0 12
+usr/share/ 0 65 0 12
+usr/share/doc/ 0 57 0 6
+usr/share/doc/packages/ 0 57 0 6
+usr/share/doc/packages/kdelibs3/ 57 0 6 0
+usr/share/man/ 0 8 0 6
+usr/share/man/man1/ 4 0 3 0
+usr/share/man/man7/ 3 0 2 0
+usr/share/man/man8/ 1 0 1 0
+-Dir:
+##----------------------------------------
+=Pkg: kdelibs3-arts 3.5.7 24 i586
++Dir:
+/ 0 386 0 2
+opt/ 0 386 0 2
+opt/kde3/ 0 386 0 2
+opt/kde3/bin/ 11 0 1 0
+opt/kde3/lib/ 375 0 1 0
+-Dir:
+##----------------------------------------
+=Pkg: kdelibs3-debuginfo 3.5.7 24 i586
++Dir:
+/ 0 137630 0 3429
+usr/ 0 137630 0 3429
+usr/lib/ 0 106253 0 186
+usr/lib/debug/ 0 106253 0 186
+usr/lib/debug/opt/ 0 106253 0 186
+usr/lib/debug/opt/kde3/ 0 106253 0 186
+usr/lib/debug/opt/kde3/bin/ 8618 0 56 0
+usr/lib/debug/opt/kde3/lib/ 70633 27002 54 76
+usr/lib/debug/opt/kde3/lib/kde3/ 23702 3300 69 7
+usr/lib/debug/opt/kde3/lib/kde3/plugins/ 0 3300 0 7
+usr/lib/debug/opt/kde3/lib/kde3/plugins/designer/ 317 0 1 0
+usr/lib/debug/opt/kde3/lib/kde3/plugins/styles/ 2983 0 6 0
+usr/src/ 0 31377 0 3243
+usr/src/debug/ 0 31377 0 3243
+usr/src/debug/kdelibs-3.5.7/ 0 31377 0 3243
+usr/src/debug/kdelibs-3.5.7/arts/ 0 273 0 52
+usr/src/debug/kdelibs-3.5.7/arts/kde/ 235 0 45 0
+usr/src/debug/kdelibs-3.5.7/arts/knotify/ 35 0 6 0
+usr/src/debug/kdelibs-3.5.7/arts/message/ 3 0 1 0
+usr/src/debug/kdelibs-3.5.7/dcop/ 285 451 20 44
+usr/src/debug/kdelibs-3.5.7/dcop/KDE-ICE/ 359 0 31 0
+usr/src/debug/kdelibs-3.5.7/dcop/client/ 52 0 7 0
+usr/src/debug/kdelibs-3.5.7/dcop/dcopidl/ 3 0 1 0
+usr/src/debug/kdelibs-3.5.7/dcop/dcopidl2cpp/ 37 0 5 0
+usr/src/debug/kdelibs-3.5.7/dnssd/ 101 0 23 0
+usr/src/debug/kdelibs-3.5.7/interfaces/ 0 437 0 134
+usr/src/debug/kdelibs-3.5.7/interfaces/kimproxy/ 0 63 0 7
+usr/src/debug/kdelibs-3.5.7/interfaces/kimproxy/library/ 63 0 7 0
+usr/src/debug/kdelibs-3.5.7/interfaces/kio/ 2 0 1 0
+usr/src/debug/kdelibs-3.5.7/interfaces/kmediaplayer/ 33 10 8 3
+usr/src/debug/kdelibs-3.5.7/interfaces/kmediaplayer/kfileaudiopreview/ 10 0 3 0
+usr/src/debug/kdelibs-3.5.7/interfaces/kregexpeditor/ 5 0 1 0
+usr/src/debug/kdelibs-3.5.7/interfaces/kscript/ 28 9 6 3
+usr/src/debug/kdelibs-3.5.7/interfaces/kscript/sample/ 9 0 3 0
+usr/src/debug/kdelibs-3.5.7/interfaces/ktexteditor/ 287 0 105 0
+usr/src/debug/kdelibs-3.5.7/kab/ 217 0 9 0
+usr/src/debug/kdelibs-3.5.7/kabc/ 658 383 93 134
+usr/src/debug/kdelibs-3.5.7/kabc/formats/ 8 0 2 0
+usr/src/debug/kdelibs-3.5.7/kabc/plugins/ 0 124 0 21
+usr/src/debug/kdelibs-3.5.7/kabc/plugins/dir/ 25 0 7 0
+usr/src/debug/kdelibs-3.5.7/kabc/plugins/file/ 30 0 7 0
+usr/src/debug/kdelibs-3.5.7/kabc/plugins/ldapkio/ 69 0 7 0
+usr/src/debug/kdelibs-3.5.7/kabc/vcard/ 119 109 36 69
+usr/src/debug/kdelibs-3.5.7/kabc/vcard/include/ 76 33 36 33
+usr/src/debug/kdelibs-3.5.7/kabc/vcard/include/generated/ 33 0 33 0
+usr/src/debug/kdelibs-3.5.7/kabc/vcardparser/ 23 0 6 0
+usr/src/debug/kdelibs-3.5.7/kate/ 0 1740 0 108
+usr/src/debug/kdelibs-3.5.7/kate/interfaces/ 59 0 7 0
+usr/src/debug/kdelibs-3.5.7/kate/part/ 1592 0 89 0
+usr/src/debug/kdelibs-3.5.7/kate/plugins/ 0 89 0 12
+usr/src/debug/kdelibs-3.5.7/kate/plugins/insertfile/ 15 0 3 0
+usr/src/debug/kdelibs-3.5.7/kate/plugins/isearch/ 26 0 3 0
+usr/src/debug/kdelibs-3.5.7/kate/plugins/kdatatool/ 17 0 3 0
+usr/src/debug/kdelibs-3.5.7/kate/plugins/wordcompletion/ 31 0 3 0
+usr/src/debug/kdelibs-3.5.7/kcert/ 34 0 3 0
+usr/src/debug/kdelibs-3.5.7/kcmshell/ 22 0 6 0
+usr/src/debug/kdelibs-3.5.7/kconf_update/ 27 0 3 0
+usr/src/debug/kdelibs-3.5.7/kdecore/ 3210 768 259 50
+usr/src/debug/kdelibs-3.5.7/kdecore/kconfig_compiler/ 51 0 1 0
+usr/src/debug/kdelibs-3.5.7/kdecore/malloc/ 181 0 1 0
+usr/src/debug/kdelibs-3.5.7/kdecore/network/ 446 0 44 0
+usr/src/debug/kdelibs-3.5.7/kdecore/svgicons/ 90 0 4 0
+usr/src/debug/kdelibs-3.5.7/kded/ 218 0 32 0
+usr/src/debug/kdelibs-3.5.7/kdefx/ 337 0 14 0
+usr/src/debug/kdelibs-3.5.7/kdemm/ 78 0 21 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/ 729 1418 126 353
+usr/src/debug/kdelibs-3.5.7/kdeprint/cups/ 337 224 73 58
+usr/src/debug/kdelibs-3.5.7/kdeprint/cups/cupsdconf2/ 224 0 58 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/ext/ 14 0 7 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/lpdunix/ 24 0 7 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/lpr/ 141 0 35 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/management/ 621 0 154 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/rlpr/ 41 0 16 0
+usr/src/debug/kdelibs-3.5.7/kdeprint/tools/ 0 16 0 3
+usr/src/debug/kdelibs-3.5.7/kdeprint/tools/escputil/ 16 0 3 0
+usr/src/debug/kdelibs-3.5.7/kdesu/ 90 0 15 0
+usr/src/debug/kdelibs-3.5.7/kdeui/ 3964 22 362 7
+usr/src/debug/kdelibs-3.5.7/kdeui/kdetrayproxy/ 22 0 7 0
+usr/src/debug/kdelibs-3.5.7/kdewidgets/ 55 0 4 0
+usr/src/debug/kdelibs-3.5.7/kdoctools/ 56 0 8 0
+usr/src/debug/kdelibs-3.5.7/khtml/ 866 4750 44 283
+usr/src/debug/kdelibs-3.5.7/khtml/css/ 530 0 18 0
+usr/src/debug/kdelibs-3.5.7/khtml/dom/ 700 0 53 0
+usr/src/debug/kdelibs-3.5.7/khtml/ecma/ 827 0 48 0
+usr/src/debug/kdelibs-3.5.7/khtml/html/ 492 0 32 0
+usr/src/debug/kdelibs-3.5.7/khtml/java/ 174 0 21 0
+usr/src/debug/kdelibs-3.5.7/khtml/kmultipart/ 31 0 3 0
+usr/src/debug/kdelibs-3.5.7/khtml/misc/ 211 0 23 0
+usr/src/debug/kdelibs-3.5.7/khtml/rendering/ 1307 0 59 0
+usr/src/debug/kdelibs-3.5.7/khtml/xml/ 478 0 26 0
+usr/src/debug/kdelibs-3.5.7/kimgio/ 198 0 17 0
+usr/src/debug/kdelibs-3.5.7/kinit/ 182 0 18 0
+usr/src/debug/kdelibs-3.5.7/kio/ 0 4866 0 488
+usr/src/debug/kdelibs-3.5.7/kio/bookmarks/ 300 0 40 0
+usr/src/debug/kdelibs-3.5.7/kio/httpfilter/ 25 0 3 0
+usr/src/debug/kdelibs-3.5.7/kio/kfile/ 1289 0 124 0
+usr/src/debug/kdelibs-3.5.7/kio/kio/ 2424 0 201 0
+usr/src/debug/kdelibs-3.5.7/kio/kioexec/ 14 0 3 0
+usr/src/debug/kdelibs-3.5.7/kio/kpasswdserver/ 35 0 4 0
+usr/src/debug/kdelibs-3.5.7/kio/kssl/ 354 0 54 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/ 95 326 10 48
+usr/src/debug/kdelibs-3.5.7/kio/misc/kdesasl/ 14 0 2 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/kfile/ 18 0 2 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/kntlm/ 46 0 5 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/kpac/ 63 0 14 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/ksendbugmail/ 27 0 6 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/kssld/ 50 0 4 0
+usr/src/debug/kdelibs-3.5.7/kio/misc/kwalletd/ 108 0 15 0
+usr/src/debug/kdelibs-3.5.7/kio/tests/ 4 0 1 0
+usr/src/debug/kdelibs-3.5.7/kioslave/ 0 500 0 29
+usr/src/debug/kdelibs-3.5.7/kioslave/bzip2/ 8 0 2 0
+usr/src/debug/kdelibs-3.5.7/kioslave/file/ 61 0 3 0
+usr/src/debug/kdelibs-3.5.7/kioslave/ftp/ 94 0 2 0
+usr/src/debug/kdelibs-3.5.7/kioslave/gzip/ 13 0 2 0
+usr/src/debug/kdelibs-3.5.7/kioslave/http/ 204 115 6 12
+usr/src/debug/kdelibs-3.5.7/kioslave/http/kcookiejar/ 115 0 12 0
+usr/src/debug/kdelibs-3.5.7/kioslave/metainfo/ 5 0 2 0
+usr/src/debug/kdelibs-3.5.7/kjs/ 748 0 69 0
+usr/src/debug/kdelibs-3.5.7/kmdi/ 460 138 38 39
+usr/src/debug/kdelibs-3.5.7/kmdi/kmdi/ 116 0 17 0
+usr/src/debug/kdelibs-3.5.7/kmdi/res/ 22 0 22 0
+usr/src/debug/kdelibs-3.5.7/knewstuff/ 182 0 31 0
+usr/src/debug/kdelibs-3.5.7/kparts/ 317 0 37 0
+usr/src/debug/kdelibs-3.5.7/kresources/ 123 0 26 0
+usr/src/debug/kdelibs-3.5.7/kspell2/ 92 275 23 36
+usr/src/debug/kdelibs-3.5.7/kspell2/plugins/ 0 204 0 19
+usr/src/debug/kdelibs-3.5.7/kspell2/plugins/aspell/ 14 0 5 0
+usr/src/debug/kdelibs-3.5.7/kspell2/plugins/ispell/ 190 0 14 0
+usr/src/debug/kdelibs-3.5.7/kspell2/ui/ 71 0 17 0
+usr/src/debug/kdelibs-3.5.7/kstyles/ 0 1078 0 46
+usr/src/debug/kdelibs-3.5.7/kstyles/highcolor/ 69 0 4 0
+usr/src/debug/kdelibs-3.5.7/kstyles/highcontrast/ 60 9 3 3
+usr/src/debug/kdelibs-3.5.7/kstyles/highcontrast/config/ 9 0 3 0
+usr/src/debug/kdelibs-3.5.7/kstyles/keramik/ 436 0 13 0
+usr/src/debug/kdelibs-3.5.7/kstyles/kthemestyle/ 203 0 8 0
+usr/src/debug/kdelibs-3.5.7/kstyles/light/ 112 0 7 0
+usr/src/debug/kdelibs-3.5.7/kstyles/plastik/ 167 18 4 3
+usr/src/debug/kdelibs-3.5.7/kstyles/plastik/config/ 18 0 3 0
+usr/src/debug/kdelibs-3.5.7/kstyles/utils/ 0 4 0 1
+usr/src/debug/kdelibs-3.5.7/kstyles/utils/installtheme/ 4 0 1 0
+usr/src/debug/kdelibs-3.5.7/kunittest/ 61 0 7 0
+usr/src/debug/kdelibs-3.5.7/kutils/ 374 66 42 12
+usr/src/debug/kdelibs-3.5.7/kutils/ksettings/ 66 0 12 0
+usr/src/debug/kdelibs-3.5.7/kwallet/ 0 110 0 18
+usr/src/debug/kdelibs-3.5.7/kwallet/backend/ 68 0 13 0
+usr/src/debug/kdelibs-3.5.7/kwallet/client/ 42 0 5 0
+usr/src/debug/kdelibs-3.5.7/libkmid/ 250 0 32 0
+usr/src/debug/kdelibs-3.5.7/libkscreensaver/ 25 0 5 0
+usr/src/debug/kdelibs-3.5.7/libltdl/ 79 0 2 0
+usr/src/debug/kdelibs-3.5.7/networkstatus/ 63 0 20 0
+usr/src/debug/kdelibs-3.5.7/pics/ 1 0 1 0
+-Dir:
+##----------------------------------------
+=Pkg: kdelibs3-devel 3.5.7 24 i586
++Dir:
+/ 0 7141 0 791
+etc/ 0 5 0 1
+etc/opt/ 0 5 0 1
+etc/opt/kde3/ 5 0 1 0
+opt/ 0 7136 0 790
+opt/kde3/ 0 7136 0 790
+opt/kde3/bin/ 130 0 7 0
+opt/kde3/include/ 4111 1867 415 289
+opt/kde3/include/arts/ 59 0 10 0
+opt/kde3/include/dnssd/ 30 0 7 0
+opt/kde3/include/dom/ 420 0 29 0
+opt/kde3/include/kabc/ 202 0 43 0
+opt/kde3/include/kate/ 22 0 2 0
+opt/kde3/include/kdemm/ 26 0 7 0
+opt/kde3/include/kdeprint/ 63 8 14 3
+opt/kde3/include/kdeprint/lpr/ 8 0 3 0
+opt/kde3/include/kdesu/ 25 0 8 0
+opt/kde3/include/khexedit/ 26 0 5 0
+opt/kde3/include/kio/ 332 0 37 0
+opt/kde3/include/kjs/ 120 0 17 0
+opt/kde3/include/kmdi/ 16 0 4 0
+opt/kde3/include/kmediaplayer/ 11 0 3 0
+opt/kde3/include/knewstuff/ 47 0 10 0
+opt/kde3/include/kparts/ 125 0 14 0
+opt/kde3/include/kresources/ 43 0 9 0
+opt/kde3/include/ksettings/ 20 0 4 0
+opt/kde3/include/kspell2/ 32 0 10 0
+opt/kde3/include/ktexteditor/ 97 0 33 0
+opt/kde3/include/kunittest/ 43 0 3 0
+opt/kde3/include/libkmid/ 100 0 17 0
+opt/kde3/lib/ 84 0 43 0
+opt/kde3/share/ 0 944 0 36
+opt/kde3/share/apps/ 0 944 0 36
+opt/kde3/share/apps/dcopidlng/ 82 0 7 0
+opt/kde3/share/apps/kdelibs/ 0 862 0 29
+opt/kde3/share/apps/kdelibs/admin/ 862 0 29 0
+-Dir:
+##----------------------------------------
+=Pkg: kdelibs3-doc 3.5.7 24 i586
++Dir:
+/ 0 6378 0 1199
+etc/ 0 1 0 1
+etc/xml/ 1 0 1 0
+opt/ 0 6377 0 1198
+opt/kde3/ 0 6377 0 1198
+opt/kde3/bin/ 43 0 1 0
+opt/kde3/share/ 0 6334 0 1197
+opt/kde3/share/apps/ 0 6003 0 1145
+opt/kde3/share/apps/ksgmltools2/ 0 6003 0 1145
+opt/kde3/share/apps/ksgmltools2/customization/ 66 940 16 487
+opt/kde3/share/apps/ksgmltools2/customization/af/ 7 9 4 9
+opt/kde3/share/apps/ksgmltools2/customization/af/entities/ 9 0 9 0
+opt/kde3/share/apps/ksgmltools2/customization/bg/ 5 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/ca/ 11 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/ca/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/cs/ 7 1 3 1
+opt/kde3/share/apps/ksgmltools2/customization/cs/entities/ 1 0 1 0
+opt/kde3/share/apps/ksgmltools2/customization/da/ 11 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/da/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/de/ 11 19 4 14
+opt/kde3/share/apps/ksgmltools2/customization/de/entities/ 19 0 14 0
+opt/kde3/share/apps/ksgmltools2/customization/dtd/ 19 0 4 0
+opt/kde3/share/apps/ksgmltools2/customization/el/ 5 1 2 1
+opt/kde3/share/apps/ksgmltools2/customization/el/entities/ 1 0 1 0
+opt/kde3/share/apps/ksgmltools2/customization/en-GB/ 7 2 3 1
+opt/kde3/share/apps/ksgmltools2/customization/en-GB/entities/ 2 0 1 0
+opt/kde3/share/apps/ksgmltools2/customization/en/ 9 2 4 2
+opt/kde3/share/apps/ksgmltools2/customization/en/entities/ 2 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/entities/ 76 0 3 0
+opt/kde3/share/apps/ksgmltools2/customization/es/ 10 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/es/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/et/ 9 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/et/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/fi/ 7 4 4 4
+opt/kde3/share/apps/ksgmltools2/customization/fi/entities/ 4 0 4 0
+opt/kde3/share/apps/ksgmltools2/customization/fo/ 7 14 4 9
+opt/kde3/share/apps/ksgmltools2/customization/fo/entities/ 14 0 9 0
+opt/kde3/share/apps/ksgmltools2/customization/fr/ 51 15 4 13
+opt/kde3/share/apps/ksgmltools2/customization/fr/entities/ 15 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/he/ 8 17 4 12
+opt/kde3/share/apps/ksgmltools2/customization/he/entities/ 17 0 12 0
+opt/kde3/share/apps/ksgmltools2/customization/hu/ 7 6 3 5
+opt/kde3/share/apps/ksgmltools2/customization/hu/entities/ 6 0 5 0
+opt/kde3/share/apps/ksgmltools2/customization/id/ 6 4 3 3
+opt/kde3/share/apps/ksgmltools2/customization/id/entities/ 4 0 3 0
+opt/kde3/share/apps/ksgmltools2/customization/it/ 10 18 4 13
+opt/kde3/share/apps/ksgmltools2/customization/it/entities/ 18 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/ja/ 6 0 3 0
+opt/kde3/share/apps/ksgmltools2/customization/ko/ 6 7 3 7
+opt/kde3/share/apps/ksgmltools2/customization/ko/entities/ 7 0 7 0
+opt/kde3/share/apps/ksgmltools2/customization/lt/ 5 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/nl/ 23 16 4 13
+opt/kde3/share/apps/ksgmltools2/customization/nl/entities/ 16 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/nn/ 11 14 4 11
+opt/kde3/share/apps/ksgmltools2/customization/nn/entities/ 14 0 11 0
+opt/kde3/share/apps/ksgmltools2/customization/no/ 7 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/no/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/obsolete/ 36 0 5 0
+opt/kde3/share/apps/ksgmltools2/customization/pl/ 20 13 4 12
+opt/kde3/share/apps/ksgmltools2/customization/pl/entities/ 13 0 12 0
+opt/kde3/share/apps/ksgmltools2/customization/pt-BR/ 9 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/pt-BR/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/pt/ 12 13 5 12
+opt/kde3/share/apps/ksgmltools2/customization/pt/entities/ 13 0 12 0
+opt/kde3/share/apps/ksgmltools2/customization/ro/ 11 14 4 13
+opt/kde3/share/apps/ksgmltools2/customization/ro/entities/ 14 0 13 0
+opt/kde3/share/apps/ksgmltools2/customization/ru/ 9 15 4 11
+opt/kde3/share/apps/ksgmltools2/customization/ru/entities/ 15 0 11 0
+opt/kde3/share/apps/ksgmltools2/customization/sk/ 7 12 3 11
+opt/kde3/share/apps/ksgmltools2/customization/sk/entities/ 12 0 11 0
+opt/kde3/share/apps/ksgmltools2/customization/sl/ 8 12 4 11
+opt/kde3/share/apps/ksgmltools2/customization/sl/entities/ 12 0 11 0
+opt/kde3/share/apps/ksgmltools2/customization/sr/ 40 18 4 12
+opt/kde3/share/apps/ksgmltools2/customization/sr/entities/ 18 0 12 0
+opt/kde3/share/apps/ksgmltools2/customization/sv/ 7 13 4 12
+opt/kde3/share/apps/ksgmltools2/customization/sv/entities/ 13 0 12 0
+opt/kde3/share/apps/ksgmltools2/customization/tr/ 5 13 2 9
+opt/kde3/share/apps/ksgmltools2/customization/tr/entities/ 13 0 9 0
+opt/kde3/share/apps/ksgmltools2/customization/uk/ 9 0 4 0
+opt/kde3/share/apps/ksgmltools2/customization/wa/ 5 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/xh/ 5 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/xsl/ 28 0 28 0
+opt/kde3/share/apps/ksgmltools2/customization/xx/ 5 0 2 0
+opt/kde3/share/apps/ksgmltools2/customization/zh-CN/ 7 8 3 5
+opt/kde3/share/apps/ksgmltools2/customization/zh-CN/entities/ 8 0 5 0
+opt/kde3/share/apps/ksgmltools2/customization/zh-TW/ 7 5 3 5
+opt/kde3/share/apps/ksgmltools2/customization/zh-TW/entities/ 5 0 5 0
+opt/kde3/share/apps/ksgmltools2/docbook/ 1 4996 1 641
+opt/kde3/share/apps/ksgmltools2/docbook/xml-dtd-4.1.2/ 311 67 13 19
+opt/kde3/share/apps/ksgmltools2/docbook/xml-dtd-4.1.2/ent/ 67 0 19 0
+opt/kde3/share/apps/ksgmltools2/docbook/xml-dtd-4.2/ 321 0 11 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/ 31 4266 6 592
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/common/ 2232 0 60 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/html/ 1291 0 58 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/images/ 58 31 29 31
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/images/callouts/ 31 0 31 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/lib/ 70 0 4 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/manpages/ 43 0 7 0
+opt/kde3/share/apps/ksgmltools2/docbook/xsl/params/ 541 0 403 0
+opt/kde3/share/doc/ 0 331 0 52
+opt/kde3/share/doc/HTML/ 0 331 0 52
+opt/kde3/share/doc/HTML/en/ 0 331 0 52
+opt/kde3/share/doc/HTML/en/common/ 320 0 50 0
+opt/kde3/share/doc/HTML/en/kspell/ 11 0 2 0
+-Dir:
+##----------------------------------------
\ No newline at end of file
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.en b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.en
new file mode 100644 (file)
index 0000000..d1ece01
--- /dev/null
@@ -0,0 +1,47 @@
+##----------------------------------------
+=Pkg: kdelibs3 3.5.7 24 i586
+=Sum: KDE Base Libraries
++Des:
+<!-- DT:Rich -->
+<p>This package contains kdelibs, one of the basic packages of the K
+Desktop Environment. It contains the necessary libraries for the KDE desktop.
+</p><p>
+This package is absolutely necessary for using KDE.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-arts 3.5.7 24 i586
+=Sum: KDE aRts support
++Des:
+<!-- DT:Rich -->
+<p>This package contains bindings and gui elements for using aRts sound daemon.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-debuginfo 3.5.7 24 i586
+=Sum: KDE Base Libraries
++Des:
+<!-- DT:Rich -->
+<p>This package contains kdelibs, one of the basic packages of the K
+Desktop Environment. It contains the necessary libraries for the KDE desktop.
+</p><p>
+This package is absolutely necessary for using KDE.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-devel 3.5.7 24 i586
+=Sum: KDE Base Package: Build Environment
++Des:
+<!-- DT:Rich -->
+<p>This package contains all necessary include files and libraries needed to develop applications that require these.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-doc 3.5.7 24 i586
+=Sum: Documentation for KDE Base Libraries
++Des:
+<!-- DT:Rich -->
+<p>This package contains the core environment and templates for the KDE help system.
+</p>
+-Des:
+##----------------------------------------
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.es b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/packages.es
new file mode 100644 (file)
index 0000000..570a4df
--- /dev/null
@@ -0,0 +1,47 @@
+##----------------------------------------
+=Pkg: kdelibs3 3.5.7 24 i586
+=Sum: KDE Base Libraries
++Des:
+<!-- DT:Rich -->
+<p>This package contains kdelibs, one of the basic packages of the K
+Desktop Environment. It contains the necessary libraries for the KDE desktop.
+</p><p>
+This package is absolutely necessary for using KDE.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-arts 3.5.7 24 i586
+=Sum: KDE aRts support
++Des:
+<!-- DT:Rich -->
+<p>This package contains bindings and gui elements for using aRts sound daemon.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-debuginfo 3.5.7 24 i586
+=Sum: KDE Base Libraries
++Des:
+<!-- DT:Rich -->
+<p>This package contains kdelibs, one of the basic packages of the K
+Desktop Environment. It contains the necessary libraries for the KDE desktop.
+</p><p>
+This package is absolutely necessary for using KDE.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-devel 3.5.7 24 i586
+=Sum: Paquete básico de KDE: entorno de desarrollo
++Des:
+<!-- DT:Rich -->
+<p>Este paquete contiene todas las librerías y archivos include necesarios para desarrollar aplicaciones que los requieran.
+</p>
+-Des:
+##----------------------------------------
+=Pkg: kdelibs3-doc 3.5.7 24 i586
+=Sum: Documentación para las bibliotecas básicas de KDE
++Des:
+<!-- DT:Rich -->
+<p>Este paquete contiene el entorno central y las plantillas para el sistema de ayuda de KDE.
+</p>
+-Des:
+##----------------------------------------
diff --git a/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/patterns b/tests/repo/susetags/data/stable-x86-subset/suse/setup/descr/patterns
new file mode 100644 (file)
index 0000000..82e92e9
--- /dev/null
@@ -0,0 +1,2 @@
+kde-10.3-71.i586.pat
+
diff --git a/tests/repo/yum/CMakeLists.txt b/tests/repo/yum/CMakeLists.txt
new file mode 100644 (file)
index 0000000..de237c0
--- /dev/null
@@ -0,0 +1,2 @@
+
+ADD_TESTS(YUMDownloader)
\ No newline at end of file
diff --git a/tests/repo/yum/YUMDownloader_test.cc b/tests/repo/yum/YUMDownloader_test.cc
new file mode 100644 (file)
index 0000000..a7ac950
--- /dev/null
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <list>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/repo/yum/Downloader.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+using namespace zypp::repo;
+
+#include "tests/zypp/KeyRingTestReceiver.h"
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/repo/yum/data")
+
+BOOST_AUTO_TEST_CASE(yum_download)
+{
+  KeyRingTestReceiver keyring_callbacks;
+  keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+
+  Pathname p = DATADIR + "/10.2-updates-subset";
+  Url url("dir:" + p.asString());
+  MediaSetAccess media(url);
+  RepoInfo repoinfo;
+  repoinfo.setAlias("testrepo");
+  repoinfo.setPath("/");
+  yum::Downloader yum(repoinfo);
+  filesystem::TmpDir tmp;
+
+  Pathname localdir(tmp.path());
+  
+  yum.download(media, localdir);
+  
+  const char* files[] =
+  {
+//    "filelists.xml.gz",
+//    "other.xml.gz",
+    "patches.xml",
+    "patch-fetchmsttfonts.sh-2333.xml",
+    "patch-flash-player-2359.xml",
+    "patch-glabels-2348.xml",
+    "patch-gv-2350.xml",
+    "patch-openssl-2349.xml",
+    "patch-tar-2351.xml",
+    "primary.xml.gz",
+    "repomd.xml",
+    "repomd.xml.asc",
+    "repomd.xml.key",
+    NULL
+  };
+  
+  int i=0;
+  while ( files[i] != NULL )
+  {
+    BOOST_CHECK_MESSAGE( PathInfo(localdir + "/repodata/" + files[i] ).isExist(), (string("/repodata/") + files[i]).c_str() );
+    i++;
+  }
+
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/filelists.xml.gz b/tests/repo/yum/data/10.2-updates-subset/repodata/filelists.xml.gz
new file mode 100644 (file)
index 0000000..dad0a0e
Binary files /dev/null and b/tests/repo/yum/data/10.2-updates-subset/repodata/filelists.xml.gz differ
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/other.xml.gz b/tests/repo/yum/data/10.2-updates-subset/repodata/other.xml.gz
new file mode 100644 (file)
index 0000000..5694694
Binary files /dev/null and b/tests/repo/yum/data/10.2-updates-subset/repodata/other.xml.gz differ
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-fetchmsttfonts.sh-2333.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-fetchmsttfonts.sh-2333.xml
new file mode 100644 (file)
index 0000000..3f91cf4
--- /dev/null
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="88913dfb2ec2942d59a19ef8152d0aef"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="fetchmsttfonts.sh-2333"
+    timestamp="20061204"
+    engine="1.0">
+  <yum:name>fetchmsttfonts.sh</yum:name>
+  <summary lang="en">Download Microsoft(r) TrueType Core Fonts</summary>
+  <summary lang="de">Download Microsoft(r) TrueType Core Fonts</summary>
+  <description lang="en">For legal reasons we can't include the Microsoft(r)
+TrueType Core Fonts in our product. This patch downloads
+these fonts and installs them on your system. Please note
+that about 4 MByte data are downloaded therefore. License
+for the fonts will be installed as
+/usr/share/doc/corefonts/EULA.html.
+</description>
+  <description lang="de">Aus rechtlichen Gründen können wir leider die TrueType Core
+Fonts von Microsoft(r) auf unserem Produkt nicht
+mitliefern. Dieser Patch lädt diese Fonts herunter und
+installiert diese auf Ihrem System. Beachten Sie bitte,
+dass dazu in etwa 4 MByte an Daten heruntergeladen werden.
+Die Lizenz für die Fonts wird unter
+/usr/share/doc/corefonts/EULA.html abgelegt.
+</description>
+  <yum:version ver="2333" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="script" name="fetchmsttfonts.sh-2333-patch-fetchmsttfonts.sh-2" epoch="0" ver="2333" rel="1" flags="EQ"/>
+  </rpm:requires>
+  <category>optional</category>
+    <license-to-confirm>
+END-USER LICENSE AGREEMENT FOR
+MICROSOFT SOFTWARE
+
+IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement (&quot;EULA&quot;) is
+a legal agreement between you (either an individual or a single entity) and
+Microsoft Corporation for the Microsoft software accompanying this EULA, which
+includes computer software and may include associated media, printed materials,
+and &quot;on-line&quot; or electronic documentation (&quot;SOFTWARE PRODUCT&quot; or &quot;SOFTWARE&quot;).
+By exercising your rights to make and use copies of the SOFTWARE PRODUCT, you
+agree to be bound by the terms of this EULA. If you do not agree to the terms
+of this EULA, you may not use the SOFTWARE PRODUCT.
+
+
+SOFTWARE PRODUCT LICENSE
+The SOFTWARE PRODUCT is protected by copyright laws and international copyright
+treaties, as well as other intellectual property laws and treaties. The
+SOFTWARE PRODUCT is licensed, not sold.
+
+
+1. GRANT OF LICENSE. This EULA grants you the following rights:
+
+  * Installation and Use. You may install and use an unlimited number of copies
+    of the SOFTWARE PRODUCT.
+  * Reproduction and Distribution. You may reproduce and distribute an
+    unlimited number of copies of the SOFTWARE PRODUCT; provided that each copy
+    shall be a true and complete copy, including all copyright and trademark
+    notices, and shall be accompanied by a copy of this EULA. Copies of the
+    SOFTWARE PRODUCT may not be distributed for profit either on a standalone
+    basis or included as part of your own product.
+
+
+2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS.
+
+  * Limitations on Reverse Engineering, Decompilation, and Disassembly. You may
+    not reverse engineer, decompile, or disassemble the SOFTWARE PRODUCT,
+    except and only to the extent that such activity is expressly permitted by
+    applicable law notwithstanding this limitation.
+  * Restrictions on Alteration. You may not rename, edit or create any
+    derivative works from the SOFTWARE PRODUCT, other than subsetting when
+    embedding them in documents.
+  * Software Transfer. You may permanently transfer all of your rights under
+    this EULA, provided the recipient agrees to the terms of this EULA.
+  * Termination. Without prejudice to any other rights, Microsoft may terminate
+    this EULA if you fail to comply with the terms and conditions of this EULA.
+    In such event, you must destroy all copies of the SOFTWARE PRODUCT and all
+    of its component parts.
+
+
+3. COPYRIGHT. All title and copyrights in and to the SOFTWARE PRODUCT
+(including but not limited to any images, text, and &quot;applets&quot; incorporated into
+the SOFTWARE PRODUCT), the accompanying printed materials, and any copies of
+the SOFTWARE PRODUCT are owned by Microsoft or its suppliers. The SOFTWARE
+PRODUCT is protected by copyright laws and international treaty provisions.
+Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted
+material.
+
+
+4. U.S. GOVERNMENT RESTRICTED RIGHTS. The SOFTWARE PRODUCT and documentation
+are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the
+Government is subject to restrictions as set forth in subparagraph (c)(1)(ii)
+of the Rights in Technical Data and Computer Software clause at DFARS
+252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer
+Software - Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer
+is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399.
+
+
+LIMITED WARRANTY
+
+NO WARRANTIES. Microsoft expressly disclaims any warranty for the SOFTWARE
+PRODUCT. The SOFTWARE PRODUCT and any related documentation is provided &quot;as is&quot;
+without warranty of any kind, either express or implied, including, without
+limitation, the implied warranties or merchantability, fitness for a particular
+purpose, or noninfringement. The entire risk arising out of use or performance
+of the SOFTWARE PRODUCT remains with you.
+
+NO LIABILITY FOR CONSEQUENTIAL DAMAGES. In no event shall Microsoft or its
+suppliers be liable for any damages whatsoever (including, without limitation,
+damages for loss of business profits, business interruption, loss of business
+information, or any other pecuniary loss) arising out of the use of or
+inability to use this Microsoft product, even if Microsoft has been advised of
+the possibility of such damages. Because some states/jurisdictions do not allow
+the exclusion or limitation of liability for consequential or incidental
+damages, the above limitation may not apply to you.
+
+
+MISCELLANEOUS
+
+If you acquired this product in the United States, this EULA is governed by the
+laws of the State of Washington.
+
+If this product was acquired outside the United States, then local laws may
+apply.
+
+Should you have any questions concerning this EULA, or if you desire to contact
+Microsoft for any reason, please contact the Microsoft subsidiary serving your
+country, or write: Microsoft Sales Information Center/One Microsoft Way/
+Redmond, WA 98052-6399.
+    </license-to-confirm>
+  <atoms>
+    <script>
+      <yum:name>fetchmsttfonts.sh-2333-patch-fetchmsttfonts.sh-2</yum:name>
+      <yum:version ver="2333" rel="1"/>
+      <do>
+#!/bin/sh
+
+EULA=&quot;http://corefonts.sourceforge.net/eula.htm&quot;
+
+FONTS=&quot; \
+dl.sourceforge.net/sourceforge/corefonts/andale32.exe \
+dl.sourceforge.net/sourceforge/corefonts/arial32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/arialb32.exe \
+dl.sourceforge.net/sourceforge/corefonts/comic32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/courie32.exe \
+dl.sourceforge.net/sourceforge/corefonts/georgi32.exe \
+dl.sourceforge.net/sourceforge/corefonts/impact32.exe \
+dl.sourceforge.net/sourceforge/corefonts/times32.exe  \
+dl.sourceforge.net/sourceforge/corefonts/trebuc32.exe \
+dl.sourceforge.net/sourceforge/corefonts/verdan32.exe \
+dl.sourceforge.net/sourceforge/corefonts/webdin32.exe \
+&quot;
+
+SERVER=&quot; \
+switch   \
+mesh     \
+jaist    \
+kent     \
+nchc     \
+heanet   \
+easynews \
+optusnet \
+&quot;
+
+CURL_OPTIONS=&quot;-s --speed-limit 3500 --speed-time 15&quot;
+
+if [ &quot;`id -u`&quot; != &quot;0&quot; ]; then
+ echo &quot;error: You must be root to use this program!&quot;
+ exit 1
+fi
+
+if [ ! -x /usr/bin/cabextract ]; then
+  echo &quot;error: cabextract missing! Please install package cabextract first.&quot;
+  exit 2
+fi
+
+. /etc/sysconfig/proxy
+
+if test &quot;$PROXY_ENABLED&quot; != &quot;no&quot;; then
+  if test -n &quot;$HTTP_PROXY&quot; ; then
+    export http_proxy=&quot;$HTTP_PROXY&quot;
+  fi
+fi
+
+if [ -z $http_proxy ]; then
+  echo 
+  echo &quot;note: No proxy is used. Please set the environment variable \&quot;http_proxy\&quot;&quot;
+  echo &quot;note: to your favorite proxy, if you want to use a proxy for the download.&quot;
+  echo &quot;note:&quot;
+  echo &quot;note:   bash: export http_proxy=\&quot;http://proxy.example.com:3128/\&quot;&quot;
+  echo &quot;note:   tcsh: setenv http_proxy \&quot;http://proxy.example.com:3128/\&quot;&quot;
+fi
+
+echo &quot;EULA:&quot;
+mkdir -p /usr/share/doc/corefonts
+echo -n &quot;  Fetching   ... &quot;
+curl $CURL_OPTIONS -o /usr/share/doc/corefonts/EULA.html $EULA || \
+  rm -f /usr/share/doc/corefonts/EULA.html
+echo &quot;done&quot;
+
+tmpname=`basename $0`
+tmpdir=`mktemp -d /tmp/$tmpname.XXXXXX`
+trap &quot;rm -rf $tmpdir&quot; EXIT
+if [ $? -ne 0 ]; then
+  echo &quot;$0: Can't create temp dir, exiting...&quot;
+  exit 4
+fi
+
+pushd $tmpdir &amp;&gt; /dev/null
+
+echo
+echo &quot;Trying to find the fastest server:&quot;
+besttime=1000
+
+for server in $SERVER; do
+  echo -n &quot; $server ... &quot;
+  start=$SECONDS
+  curl $CURL_OPTIONS --connect-timeout 10 -o cabextract.rpm \
+    http://$server.dl.sourceforge.net/sourceforge/corefonts/cabextract-0.5-1.i386.rpm
+  if [ $? -ne 0 ]; then
+    echo &quot;too slow (aborted)&quot;
+    continue
+  fi
+  stop=$SECONDS
+  time=$((stop - start))
+  echo &quot;$time sec&quot;
+  if [ $time -lt $besttime ]; then 
+    besttime=$time
+    useserver=$server
+  fi
+done
+
+rm -f cabextract.rpm
+if [ -n &quot;$useserver&quot; ]; then
+  echo &quot;The winner is: &gt;&gt; $useserver &lt;&lt;&quot;
+  echo
+else
+  echo &quot;Connection too slow or no server available. Aborting ... &quot;
+  exit 5
+fi
+
+for font in $FONTS; do
+ for i in $useserver $SERVER; do
+  archive=http://$i.$font
+  file=`echo $archive|awk -F &quot;/&quot; '{print $NF}'`
+  rm -f $file
+  echo &quot;$file ($archive):&quot;
+  echo -n &quot;  Fetching   ... &quot;
+  curl $CURL_OPTIONS -o $file $archive
+  if [ $? -ne 0 ]; then
+    rm -f $file
+    echo &quot;failed ... deleted!&quot;
+    continue
+  fi
+  echo done
+  echo -n &quot;  Extracting ... &quot;
+  cabextract -l $file &amp;&gt; /dev/null
+  if [ $? -ne 0 ]; then
+    rm -f $file
+    echo &quot;failed ... deleted!&quot;
+  else
+    cabextract $file &amp;&gt; /dev/null
+    echo &quot;done&quot;
+    success=true
+    break
+  fi
+  rm -f $file
+ done
+done
+
+if [ &quot;x$success&quot; != &quot;x&quot; ]; then 
+  for i in *.[Tt][Tt][CFcf]; do
+    lower=`echo $i|tr [:upper:] [:lower:]`
+    test &quot;$i&quot; != &quot;$lower&quot; &amp;&amp; mv $i $lower
+  done
+  chmod 644 *.tt[cf]
+  # impact.ttf already in agfa-fonts package
+  test -s /usr/share/fonts/truetype/impact.ttf &amp;&amp; rm impact.ttf
+  mv -f *.tt[cf] /usr/share/fonts/truetype
+  /usr/sbin/fonts-config
+  echo &quot;*** Fonts installed. ***&quot;
+else
+  echo &quot;*** No Fonts installed. ***&quot;
+fi
+
+popd &amp;&gt; /dev/null
+      </do>
+      <suse:freshens>
+        <suse:entry kind="package" name="glibc"/>
+      </suse:freshens>
+    </script>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-flash-player-2359.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-flash-player-2359.xml
new file mode 100644 (file)
index 0000000..7330ec0
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="0ea8e66732bf0740881da44bc2526b0e"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="flash-player-2359"
+    timestamp="1165540363"
+    engine="1.0">
+  <yum:name>flash-player</yum:name>
+  <summary lang="en">flash-player: Security upgrade to 7.0.69</summary>
+  <summary lang="de">flash-player: Security Upgrade auf Version 7.0.69</summary>
+  <description lang="en">This security update brings the Adobe Flash Player to
+version 7.0.69. It\r fixes the following security problem:\r
+
+CVE-2006-5330: CRLF injection vulnerability in Adobe Flash
+Player\r allows remote attackers to modify HTTP headers of
+client requests\r and conduct HTTP Request Splitting attacks
+via CRLF sequences in\r arguments to the ActionScript
+functions (1) XML.addRequestHeader and (2)
+XML.contentType. NOTE: the flexibility of the attack varies
+depending\r on the type of web browser being used.
+</description>
+  <description lang="de">Dieses Securityupgrade bringt den Adobe Flash Player auf
+Version 7.0.69.\r Dieses Update behebt damit folgendes
+Sicherheitsproblem:\r
+
+CVE-2006-5330: Ein CRLF Injectionsangriff in Adobe Flash
+Player\r erlaubt entfernten Angreifern die HTTP Header von
+Client Anfragen zu verändern\r und damit HTTP Request
+Splitting Angriffe auszuführen. Dies geschieht\r durch
+Einschleusen von CRLF Sequenzen in die ActionScript
+Funktionen (1) XML.addRequestHeader\r und (2)
+XML.contentType. Note: Die Flexibilität dieses Angriffs ist
+abhängig\r vom benutzten Webbrowser.
+</description>
+  <yum:version ver="2359" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="atom" name="flash-player" epoch="0" ver="7.0.69.0" rel="1.1" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>flash-player</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="7.0.69.0" rel="1.1"/>
+      <checksum type="sha" pkgid="YES">e0add7ff093ece07c6967f658073bfd74607fb64</checksum>
+      <time file="1165572820" build="1165540363"/>
+      <size package="990457" installed="2164975" archive="2165716"/>
+      <location href="rpm/i586/flash-player-7.0.69.0-1.1.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="flash-player" epoch="0" ver="7.0.69.0" rel="1.1" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="flash-player"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/flash-player-7.0.69.0-1.1.i586.patch.rpm"/>
+          <checksum type="sha">8a6adc0ca575d8a56c3a1ba2d88e8438939debe9</checksum>
+          <time file="1165574182" build="1165540363"/>
+          <size package="990592" archive="2164560"/>
+          <base-version epoch="0" ver="7.0.68.0" rel="16"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-glabels-2348.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-glabels-2348.xml
new file mode 100644 (file)
index 0000000..1b38be2
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="94977d0dc5b5998b09101f56ba9ce27a"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="glabels-2348"
+    timestamp="1165501960"
+    engine="1.0">
+  <yum:name>glabels</yum:name>
+  <summary lang="en">If you installed glabels, you should update.</summary>
+  <summary lang="de">If you installed glabels, you should update.</summary>
+  <description lang="en">If you cannot install glabels or experienced problem with
+file types (MIME) or you cannot find glabels in the menu,
+you should update.
+</description>
+  <description lang="de">If you cannot install glabels or experienced problem with
+file types (MIME) or you cannot find glabels in the menu,
+you should update.
+</description>
+  <yum:version ver="2348" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="atom" name="glabels" epoch="0" ver="2.0.4" rel="30.2" flags="EQ"/>
+  </rpm:requires>
+  <category>recommended</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>glabels</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="2.0.4" rel="30.2"/>
+      <checksum type="sha" pkgid="YES">34adf06a0c4873b9d53b4634beb8bee458b45767</checksum>
+      <time file="1165536261" build="1165501960"/>
+      <size package="983124" installed="2257356" archive="2271256"/>
+      <location href="rpm/i586/glabels-2.0.4-30.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="glabels" epoch="0" ver="2.0.4" rel="30.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="glabels"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/glabels-2.0.4-30.2.i586.patch.rpm"/>
+          <checksum type="sha">225d498134c8f369051005b808ee662917e48667</checksum>
+          <time file="1165538099" build="1165501960"/>
+          <size package="15910" archive="2424"/>
+          <base-version epoch="0" ver="2.0.4" rel="30"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>glabels</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="2.0.4" rel="30.2"/>
+      <checksum type="sha" pkgid="YES">40f24dc53e8f800fc1383b0ac1b357a7134f8eea</checksum>
+      <time file="1165536201" build="1165503062"/>
+      <size package="1023074" installed="2349352" archive="2363264"/>
+      <location href="rpm/x86_64/glabels-2.0.4-30.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="glabels" epoch="0" ver="2.0.4" rel="30.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="glabels"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/glabels-2.0.4-30.2.x86_64.patch.rpm"/>
+          <checksum type="sha">a505aef1188ba45022a6c1241a14f569fa19b0a8</checksum>
+          <time file="1165538101" build="1165503062"/>
+          <size package="16423" archive="2424"/>
+          <base-version epoch="0" ver="2.0.4" rel="30"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-gv-2350.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-gv-2350.xml
new file mode 100644 (file)
index 0000000..d96af97
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="8975f0d388b1ef6cf1058b8c743a304a"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="gv-2350"
+    timestamp="1165492324"
+    engine="1.0">
+  <yum:name>gv</yum:name>
+  <summary lang="en">gv: Additional fix for previous security update</summary>
+  <summary lang="de">gv: Weiterer Fix für vorheriges Sicherheitsupdate</summary>
+  <description lang="en">The previous &quot;gv&quot; update to fix a stack overflow did not
+completely fix\r the problem spotted. An attacker could
+still cause the handling to use\r up all system memory, or
+open windows much wider than the X display and\r crash. Code
+execution however was not possible.
+</description>
+  <description lang="de">Das vorherige &quot;gv&quot; Update hat zwar den berichteten
+Stacküberlauf behoben, \r es konnte aber immer noch eine
+fehlerhaftes PS den Viewer zum Absturz bringen\r und/oder
+den Systemspeicher aufbrauchen. Das Ausführen von Schadcode
+war\r allerdings nicht mehr möglich.
+</description>
+  <yum:version ver="2350" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="atom" name="gv" epoch="0" ver="3.5.8" rel="1184.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>gv</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="3.5.8" rel="1184.2"/>
+      <checksum type="sha" pkgid="YES">ef3e9360d99cd36ba269af1abd2e8ac963c8c838</checksum>
+      <time file="1165536209" build="1165492324"/>
+      <size package="230028" installed="537756" archive="543948"/>
+      <location href="rpm/i586/gv-3.5.8-1184.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="gv" epoch="0" ver="3.5.8" rel="1184.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="gv"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/gv-3.5.8-1184.2.i586.patch.rpm"/>
+          <checksum type="sha">d76be82ebb51285250baaf698cd6649803b35794</checksum>
+          <time file="1165538134" build="1165492324"/>
+          <size package="111809" archive="256776"/>
+          <base-version epoch="0" ver="3.5.8" rel="1184"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>gv</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="3.5.8" rel="1184.2"/>
+      <checksum type="sha" pkgid="YES">508bdeacfb23a0a210a27e3ed164f58b5c5c4590</checksum>
+      <time file="1165536257" build="1165492338"/>
+      <size package="242827" installed="566264" archive="572456"/>
+      <location href="rpm/ppc/gv-3.5.8-1184.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="gv" epoch="0" ver="3.5.8" rel="1184.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="gv"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/gv-3.5.8-1184.2.ppc.patch.rpm"/>
+          <checksum type="sha">a77d288b75a8821227d0402639effd99f194c12f</checksum>
+          <time file="1165538136" build="1165492338"/>
+          <size package="124741" archive="285284"/>
+          <base-version epoch="0" ver="3.5.8" rel="1184"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>gv</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="3.5.8" rel="1184.2"/>
+      <checksum type="sha" pkgid="YES">d1ceecc9b2442fe298040690bf91ae224cc2bfd4</checksum>
+      <time file="1165536168" build="1165492270"/>
+      <size package="244258" installed="562996" archive="569188"/>
+      <location href="rpm/x86_64/gv-3.5.8-1184.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="gv" epoch="0" ver="3.5.8" rel="1184.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="gv"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/gv-3.5.8-1184.2.x86_64.patch.rpm"/>
+          <checksum type="sha">9424c4ba6f46d26bb1ae1d33fae80061b968ccb3</checksum>
+          <time file="1165538137" build="1165492270"/>
+          <size package="125854" archive="282016"/>
+          <base-version epoch="0" ver="3.5.8" rel="1184"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-openssl-2349.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-openssl-2349.xml
new file mode 100644 (file)
index 0000000..74c74ce
--- /dev/null
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="b7dfa4c9cfaf8657e2602bbbcf92ec30"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="openssl-2349"
+    timestamp="1165493634"
+    engine="1.0">
+  <yum:name>openssl</yum:name>
+  <summary lang="en">Security update for OpenSSL</summary>
+  <summary lang="de">Sicherheitsupdate für OpenSSL</summary>
+  <description lang="en">A previous openssl update (CVE-2006-2940) introduced
+another bug that can lead to a crash by providing a large
+prime number. An uninitialized pointer is freed during
+error handling. This bug allows remote attackers to crash
+services that use openssl.
+</description>
+  <description lang="de">Ein vorheriges Sicherheitsupdate von OpenSSL
+(CVE-2006-2940) hat einen weiteren Fehler verursacht, der
+von entfernten Angreifern ausgenutzt werden kann, um
+Applikationen, die OpenSSL benutzen, zum Absturz zu bringen.
+</description>
+  <yum:version ver="2349" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="atom" name="openssl" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+    <rpm:entry kind="atom" name="openssl-32bit" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+    <rpm:entry kind="atom" name="openssl-64bit" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+    <rpm:entry kind="atom" name="openssl-devel" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+    <rpm:entry kind="atom" name="openssl-devel-32bit" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+    <rpm:entry kind="atom" name="openssl-devel-64bit" epoch="0" ver="0.9.8d" rel="17.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">a923a481bc975916121811645c9957303c90172c</checksum>
+      <time file="1165536196" build="1165493634"/>
+      <size package="1190812" installed="2767741" archive="2787168"/>
+      <location href="rpm/i586/openssl-0.9.8d-17.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/openssl-0.9.8d-17.2.i586.patch.rpm"/>
+          <checksum type="sha">2936166db09a46956c80a4ac4f69038f6f470cb0</checksum>
+          <time file="1165538106" build="1165493634"/>
+          <size package="838816" archive="1999084"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">3751ba88267a45f6bae72285bc3295f1550ad80e</checksum>
+      <time file="1165536248" build="1165493907"/>
+      <size package="1242242" installed="3031173" archive="3050600"/>
+      <location href="rpm/ppc/openssl-0.9.8d-17.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/openssl-0.9.8d-17.2.ppc.patch.rpm"/>
+          <checksum type="sha">5fc8bbefa21ffa770c810d26e2ffec6bb2cf5397</checksum>
+          <time file="1165538109" build="1165493907"/>
+          <size package="872746" archive="2126292"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">0a6e313ab112570584d82725dbb1fb81862bfdf0</checksum>
+      <time file="1165536159" build="1165493227"/>
+      <size package="1278381" installed="3079113" archive="3098568"/>
+      <location href="rpm/x86_64/openssl-0.9.8d-17.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/openssl-0.9.8d-17.2.x86_64.patch.rpm"/>
+          <checksum type="sha">1a69d600b7689a7d73eb02c9a8393aeea6be7159</checksum>
+          <time file="1165538111" build="1165493227"/>
+          <size package="906139" archive="2204696"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-32bit</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">485659fe4f32303bb951e557367564f920eb1740</checksum>
+      <time file="1165536197" build="1165493667"/>
+      <size package="687177" installed="1664224" archive="1666028"/>
+      <location href="rpm/x86_64/openssl-32bit-0.9.8d-17.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-32bit" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-32bit"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/openssl-32bit-0.9.8d-17.2.x86_64.patch.rpm"/>
+          <checksum type="sha">559c13e02265269a9f08ff0d1aeebb895dd90530</checksum>
+          <time file="1165538113" build="1165493667"/>
+          <size package="674841" archive="1601976"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-64bit</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">96d57cc3d4539e0355374526af16c0d4a4919ea6</checksum>
+      <time file="1165536216" build="1165494120"/>
+      <size package="797366" installed="2586832" archive="2588664"/>
+      <location href="rpm/ppc/openssl-64bit-0.9.8d-17.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-64bit" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-64bit"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/openssl-64bit-0.9.8d-17.2.ppc.patch.rpm"/>
+          <checksum type="sha">06f6c63e668e7ba4869efa100502272d986fcdbc</checksum>
+          <time file="1165538116" build="1165494120"/>
+          <size package="796277" archive="2581984"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-devel</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">9f6a44015ad97680e9f93d0edefa1d533940479c</checksum>
+      <time file="1165536197" build="1165493634"/>
+      <size package="930588" installed="3937193" archive="3948464"/>
+      <location href="rpm/i586/openssl-devel-0.9.8d-17.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-devel" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-devel"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/openssl-devel-0.9.8d-17.2.i586.patch.rpm"/>
+          <checksum type="sha">8b2815cb139ab9d99849ad1bf11fd193e48c599a</checksum>
+          <time file="1165538119" build="1165493634"/>
+          <size package="637241" archive="2351696"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-devel</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">aefc1215e506133131739f86a77eaef17e598dd6</checksum>
+      <time file="1165536249" build="1165493907"/>
+      <size package="986896" installed="4033294" archive="4044564"/>
+      <location href="rpm/ppc/openssl-devel-0.9.8d-17.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-devel" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-devel"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/openssl-devel-0.9.8d-17.2.ppc.patch.rpm"/>
+          <checksum type="sha">6b065d695dcd9e9c26d9fcde0dc37757c3c68a94</checksum>
+          <time file="1165538121" build="1165493907"/>
+          <size package="686833" archive="2446944"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-devel</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">72d8cd8e5273cc13d821e3179b49c1ca1225d471</checksum>
+      <time file="1165536160" build="1165493227"/>
+      <size package="939300" installed="4853257" archive="4864540"/>
+      <location href="rpm/x86_64/openssl-devel-0.9.8d-17.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-devel" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-devel"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/openssl-devel-0.9.8d-17.2.x86_64.patch.rpm"/>
+          <checksum type="sha">64a038710ac2e2ba9918b48dcbf8fcf0aef143c7</checksum>
+          <time file="1165538123" build="1165493227"/>
+          <size package="644833" archive="3135336"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-devel-32bit</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">acbbb17d623d1a509f1811ab75297a6015d4ee8e</checksum>
+      <time file="1165536197" build="1165493668"/>
+      <size package="742019" installed="2753796" archive="2754492"/>
+      <location href="rpm/x86_64/openssl-devel-32bit-0.9.8d-17.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-devel-32bit" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-devel-32bit"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/openssl-devel-32bit-0.9.8d-17.2.x86_64.patch.rpm"/>
+          <checksum type="sha">7c47e37c419d0556a1bdf8e50b7ebee845dea752</checksum>
+          <time file="1165538125" build="1165493668"/>
+          <size package="631757" archive="2351696"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>openssl-devel-64bit</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="0.9.8d" rel="17.2"/>
+      <checksum type="sha" pkgid="YES">e9b398feb88d4fbe24a0a52f20e9d20feb5caf49</checksum>
+      <time file="1165536217" build="1165494124"/>
+      <size package="801166" installed="4124288" archive="4124988"/>
+      <location href="rpm/ppc/openssl-devel-64bit-0.9.8d-17.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="openssl-devel-64bit" epoch="0" ver="0.9.8d" rel="17.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="openssl-devel-64bit"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/openssl-devel-64bit-0.9.8d-17.2.ppc.patch.rpm"/>
+          <checksum type="sha">177a83d2b430effeeadaf884ada00b3e09862c66</checksum>
+          <time file="1165538128" build="1165494124"/>
+          <size package="683533" archive="3552068"/>
+          <base-version epoch="0" ver="0.9.8d" rel="17"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patch-tar-2351.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patch-tar-2351.xml
new file mode 100644 (file)
index 0000000..ad3fda4
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--PATCHINFO id="d71c6eacf4460c45dd8ec1d3e2be6a98"!-->
+<patch
+    xmlns="http://novell.com/package/metadata/suse/patch"
+    xmlns:yum="http://linux.duke.edu/metadata/common"
+    xmlns:rpm="http://linux.duke.edu/metadata/rpm"
+    xmlns:suse="http://novell.com/package/metadata/suse/common"
+    patchid="tar-2351"
+    timestamp="1165492446"
+    engine="1.0">
+  <yum:name>tar</yum:name>
+  <summary lang="en">tar: Securityupdate to fix symlink traversal</summary>
+  <summary lang="de">tar: Sicherheitsupdate das behebt ein Symlinkproblem behebt</summary>
+  <description lang="en">This security update fixes a directory traversal in tar,
+where unpacked symlinks could\r be followed outside of the
+directory where the tar file is unpacked. (CVE-2006-6097)\r
+
+This feature was made optional and needs to be enabled with
+a commandline option.
+</description>
+  <description lang="de">Dieses Sicherheitsupdate behebt ein Problem, wo beim
+Entpacken eines TAR Archives dieses\r durch Symlinks aus dem
+aktuellen Verzeichnis ausbrechen konnte. (CVE-2006-6097)\r
+
+Das alte obsolete Feature in GNU Tar wurde optional gemacht
+und kann für alte TAR Archive\r mit einer
+Kommandozeilenoption angeschaltet werden.
+</description>
+  <yum:version ver="2351" rel="0"/>
+  <rpm:requires>
+    <rpm:entry kind="atom" name="tar" epoch="0" ver="1.15.1" rel="42.2" flags="EQ"/>
+  </rpm:requires>
+  <category>security</category>
+  <atoms>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>tar</name>
+      <arch>i586</arch>
+      <version epoch="0" ver="1.15.1" rel="42.2"/>
+      <checksum type="sha" pkgid="YES">7842746e863cbad136ce916a9720ff5913a5bf66</checksum>
+      <time file="1165536210" build="1165492446"/>
+      <size package="643349" installed="1491226" archive="1498148"/>
+      <location href="rpm/i586/tar-1.15.1-42.2.i586.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="tar" epoch="0" ver="1.15.1" rel="42.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="tar"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/i586/tar-1.15.1-42.2.i586.patch.rpm"/>
+          <checksum type="sha">f944b5b3c727df8ab7995a0cb64aa8239ab285f1</checksum>
+          <time file="1165538141" build="1165492446"/>
+          <size package="107779" archive="198684"/>
+          <base-version epoch="0" ver="1.15.1" rel="42"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>tar</name>
+      <arch>ppc</arch>
+      <version epoch="0" ver="1.15.1" rel="42.2"/>
+      <checksum type="sha" pkgid="YES">def753a1b7fe65e49653e22ae6d42b329306be53</checksum>
+      <time file="1165536257" build="1165492414"/>
+      <size package="649921" installed="1532230" archive="1539152"/>
+      <location href="rpm/ppc/tar-1.15.1-42.2.ppc.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="tar" epoch="0" ver="1.15.1" rel="42.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="tar"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/ppc/tar-1.15.1-42.2.ppc.patch.rpm"/>
+          <checksum type="sha">2ad27c3ec52ac94fc599372fe310f946dff91b53</checksum>
+          <time file="1165538142" build="1165492414"/>
+          <size package="118362" archive="239688"/>
+          <base-version epoch="0" ver="1.15.1" rel="42"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+    <package xmlns="http://linux.duke.edu/metadata/common" type="rpm">
+      <name>tar</name>
+      <arch>x86_64</arch>
+      <version epoch="0" ver="1.15.1" rel="42.2"/>
+      <checksum type="sha" pkgid="YES">8fe39477a30f01b18d6c1f2b76a61b96f07a8969</checksum>
+      <time file="1165536168" build="1165492249"/>
+      <size package="654218" installed="1512106" archive="1519028"/>
+      <location href="rpm/x86_64/tar-1.15.1-42.2.x86_64.rpm"/>
+      <format>
+        <rpm:requires>
+          <rpm:entry kind="package" name="tar" epoch="0" ver="1.15.1" rel="42.2" flags="GE"/>
+        </rpm:requires>
+        <suse:freshens>
+          <suse:entry kind="package" name="tar"/>
+        </suse:freshens>
+      </format>
+      <pkgfiles xmlns="http://novell.com/package/metadata/suse/patch">
+        <patchrpm>
+          <location href="rpm/x86_64/tar-1.15.1-42.2.x86_64.patch.rpm"/>
+          <checksum type="sha">c2ba500358a3bb3144ef43ba12001724dee7c7ec</checksum>
+          <time file="1165538143" build="1165492249"/>
+          <size package="117658" archive="219564"/>
+          <base-version epoch="0" ver="1.15.1" rel="42"/>
+        </patchrpm>
+      </pkgfiles>
+    </package>
+  </atoms>
+</patch>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/patches.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/patches.xml
new file mode 100644 (file)
index 0000000..6d66963
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<patches xmlns="http://novell.com/package/metadata/suse/patches">
+  <patch id="fetchmsttfonts.sh-2333">
+    <checksum type="sha">6b72b4f3617d0d51af28399c0f5e0af401440245</checksum>
+    <location href="repodata/patch-fetchmsttfonts.sh-2333.xml"/>
+  </patch>
+  <patch id="glabels-2348">
+    <checksum type="sha">b02ba598d8ed5f8a31859d3b34e72e1ddecbe894</checksum>
+    <location href="repodata/patch-glabels-2348.xml"/>
+  </patch>
+  <patch id="openssl-2349">
+    <checksum type="sha">321ee41de68be4e83dfb74559c14300a59b85ccf</checksum>
+    <location href="repodata/patch-openssl-2349.xml"/>
+  </patch>
+  <patch id="gv-2350">
+    <checksum type="sha">ec9e8a3f3ce2588cecd84ab95ec910d41db5d74b</checksum>
+    <location href="repodata/patch-gv-2350.xml"/>
+  </patch>
+  <patch id="tar-2351">
+    <checksum type="sha">d803372cd5d844ee01ab6fb3d1b4332391fa1206</checksum>
+    <location href="repodata/patch-tar-2351.xml"/>
+  </patch>
+  <patch id="flash-player-2359">
+    <checksum type="sha">c2de5dd35ec2dcccc118d9d7f539e768dfd5ec50</checksum>
+    <location href="repodata/patch-flash-player-2359.xml"/>
+  </patch>
+</patches>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/primary.xml.gz b/tests/repo/yum/data/10.2-updates-subset/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..d016c87
Binary files /dev/null and b/tests/repo/yum/data/10.2-updates-subset/repodata/primary.xml.gz differ
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml b/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..2c7e82c
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="patches">
+    <location href="repodata/patches.xml"/>
+    <checksum type="sha">23ed066c97c23c557121aaf9c57e5f3f429e4a23</checksum>
+    <timestamp>1176468240</timestamp>
+    <open-checksum type="sha">23ed066c97c23c557121aaf9c57e5f3f429e4a23</open-checksum>
+  </data>
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">7f83e2e248d8966d119471c58ef41e7a065c9e1a</checksum>
+    <timestamp>1176468292</timestamp>
+    <open-checksum type="sha">b2b013f0426018c6cc9dbb30a06fa2bab0aaeea8</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">73588a1c655a58b2251146675ab64cce8cb45a26</checksum>
+    <timestamp>1176468284</timestamp>
+    <open-checksum type="sha">68e66f72b932235eeee5d5638d3cba2ec674c208</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">8a7622d1200067c399e483c4ca19fb382ae1f24e</checksum>
+    <timestamp>1176468286</timestamp>
+    <open-checksum type="sha">0bc279d4c287dfea715805e349744122f412e3a6</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.asc b/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..db30fb6
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.9.22 (GNU/Linux)
+
+iD8DBQBGzqIbm+zCtd2wN1YRAslJAJ9eQ+6wpaKRVkr5nrQ4vij6ge0QpgCfRoT2
+clYiEftDMuaEpZeRaBQodyc=
+=I83X
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.key b/tests/repo/yum/data/10.2-updates-subset/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..8e52028
--- /dev/null
@@ -0,0 +1,24 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+=5nY9
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/yum/data/extensions/repodata/filelists.xml.gz b/tests/repo/yum/data/extensions/repodata/filelists.xml.gz
new file mode 100644 (file)
index 0000000..537e052
Binary files /dev/null and b/tests/repo/yum/data/extensions/repodata/filelists.xml.gz differ
diff --git a/tests/repo/yum/data/extensions/repodata/other.xml.gz b/tests/repo/yum/data/extensions/repodata/other.xml.gz
new file mode 100644 (file)
index 0000000..0ceffee
Binary files /dev/null and b/tests/repo/yum/data/extensions/repodata/other.xml.gz differ
diff --git a/tests/repo/yum/data/extensions/repodata/primary.xml.gz b/tests/repo/yum/data/extensions/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..8d34f01
Binary files /dev/null and b/tests/repo/yum/data/extensions/repodata/primary.xml.gz differ
diff --git a/tests/repo/yum/data/extensions/repodata/repomd.xml b/tests/repo/yum/data/extensions/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..03af328
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">603807e12e7418911fa9f158ef66b9c20f1df567</checksum>
+    <timestamp>1215823452</timestamp>
+    <open-checksum type="sha">34cde52910a161664291bd93e8a9cd24f751af6b</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">06660c856facf17822102d09d849d42fad79d28e</checksum>
+    <timestamp>1215823453</timestamp>
+    <open-checksum type="sha">fdabcfd9c056109ab25acf0644823bdec94b6cfd</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">2eccb910f933ee28fd5f5223e05eab6c5b08db67</checksum>
+    <timestamp>1215823454</timestamp>
+    <open-checksum type="sha">c6bd3c6099de74d97ed045163235be10ffa8a85f</open-checksum>
+  </data>
+  
+  <data type="susedata">
+    <location href="repodata/susedata.xml.gz"/>
+    <checksum type="sha">359396be40992603aecf6e2832839111e09eb080</checksum>
+    <timestamp>1227279057.0</timestamp>
+    <open-checksum type="sha">b17c055bef95bca397faffdf028cfa91dd1b24bc</open-checksum>
+  </data>
+  
+  <data type="suseinfo">
+    <location href="repodata/suseinfo.xml.gz"/>
+    <checksum type="sha">e0b9149c1b7f48c952e9b3ea996669d8d0d9e1e8</checksum>
+    <timestamp>1227279057.0</timestamp>
+    <open-checksum type="sha">b17c055bef95bca397faffdf028cfa91dd1b24bc</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/repo/yum/data/extensions/repodata/susedata.xml.gz b/tests/repo/yum/data/extensions/repodata/susedata.xml.gz
new file mode 100644 (file)
index 0000000..49754c0
Binary files /dev/null and b/tests/repo/yum/data/extensions/repodata/susedata.xml.gz differ
diff --git a/tests/repo/yum/data/extensions/repodata/suseinfo.xml.gz b/tests/repo/yum/data/extensions/repodata/suseinfo.xml.gz
new file mode 100644 (file)
index 0000000..d2f1474
Binary files /dev/null and b/tests/repo/yum/data/extensions/repodata/suseinfo.xml.gz differ
diff --git a/tests/sat/AttrMatcher_test.cc b/tests/sat/AttrMatcher_test.cc
new file mode 100644 (file)
index 0000000..98a679c
--- /dev/null
@@ -0,0 +1,158 @@
+#include "TestSetup.h"
+#include <zypp/sat/LookupAttr.h>
+#include <zypp/sat/AttrMatcher.h>
+#include <zypp/ResObjects.h>
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Matcher
+//
+///////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(Match_default)
+{
+  Match m;
+  BOOST_CHECK( !m ); // eval in boolean context
+  BOOST_CHECK_EQUAL( m, Match::NOTHING );
+  BOOST_CHECK_EQUAL( m.get(), 0 );
+
+  // set the mode part
+  BOOST_CHECK_EQUAL( m |= Match::STRING,       Match::STRING );
+
+  m.setModeSubstring();
+  BOOST_CHECK_EQUAL( m,                                Match::SUBSTRING );
+
+  m.setMode( Match::GLOB );
+  BOOST_CHECK_EQUAL( m,                        Match::GLOB );
+
+  BOOST_CHECK_EQUAL( m = Match::REGEX,                 Match::REGEX );
+
+  BOOST_CHECK( m.isModeRegex() );
+  m |= Match::NOCASE | Match::FILES;
+  BOOST_CHECK_EQUAL( m, Match::REGEX | Match::NOCASE | Match::FILES );
+
+  BOOST_CHECK( m.testAnyOf( Match::SUBSTRING | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( !m.test( Match::SUBSTRING | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m.test( Match::REGEX | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m.test( Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m != (Match::NOCASE | Match::FILES) );
+  BOOST_CHECK_EQUAL( m.flags(),Match::NOCASE | Match::FILES );
+
+  m -= Match::NOCASE; // remove flags
+  BOOST_CHECK( m.test( Match::REGEX | Match::FILES ) );
+  m -= Match::REGEX;
+  BOOST_CHECK_EQUAL( m, Match::FILES );
+}
+
+BOOST_AUTO_TEST_CASE(Match_operator)
+{
+  // Test whether implicit conversions from enum Match::Mode to
+  // Matcher work. There must be no difference in using mode and flag
+  // constants. These tests usually fail at compiletime, if some operator
+  // overload is missing.
+  //
+  // E.G.:
+  // inline Match operator|( const Match & lhs, const Match & rhs )
+  //  this does not cover (REGEX|SUBSTRING), because if both arguments
+  //  are enum Mode the compiler might want to use operator|(int,int)
+  //  instead.
+
+  Match m( Match::GLOB );
+  m = Match::GLOB;
+
+  m |= Match::GLOB;
+  m = Match::SUBSTRING | Match::GLOB;
+
+  m -= Match::GLOB;
+  m = Match::SUBSTRING - Match::GLOB;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : AttrMatcher
+//
+///////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_defaultconstructed)
+{
+  sat::AttrMatcher m;
+  BOOST_CHECK_EQUAL( m.flags(), Match::NOTHING );
+  BOOST_CHECK( !m );   // eval in boolean context
+  BOOST_CHECK( m.searchstring().empty() );
+  BOOST_CHECK_EQUAL( m.flags(), Match() );
+  // matches nothing:
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( " " ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( !m( "default" ) );
+
+  m.setSearchstring( "fau" );
+  BOOST_CHECK( m );    // eval in boolean context
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_OTHER)
+{
+  sat::AttrMatcher m( "fau", Match::OTHER );
+  BOOST_CHECK_THROW( m.compile(), MatchUnknownModeException );
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_STRING)
+{
+  sat::AttrMatcher m( "fau" );
+  BOOST_CHECK_EQUAL( m.flags(), Match::STRING );
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( m( "fau" ) );
+  BOOST_CHECK( !m( "default" ) );
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_STRINGSTART)
+{
+  sat::AttrMatcher m( "fau", Match::STRINGSTART );
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( m( "fau" ) );
+  BOOST_CHECK( m( "fault" ) );
+  BOOST_CHECK( !m( "default" ) );
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_STRINGEND)
+{
+  sat::AttrMatcher m( "fau", Match::STRINGEND );
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( m( "fau" ) );
+  BOOST_CHECK( m( "defau" ) );
+  BOOST_CHECK( !m( "default" ) );
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_REGEX)
+{
+  sat::AttrMatcher m( "fau" );
+
+  BOOST_CHECK( !m.isCompiled() );
+  BOOST_CHECK_NO_THROW( m.compile() );
+
+  m.setSearchstring( "wa[" );
+  BOOST_CHECK( !m.isCompiled() );
+  m.setFlags( Match::REGEX );
+  BOOST_CHECK( !m.isCompiled() );
+  BOOST_CHECK_THROW( m.compile(), MatchInvalidRegexException );
+  BOOST_CHECK( !m.isCompiled() );
+
+  m.setSearchstring( "wa[a]" );
+  BOOST_CHECK_NO_THROW( m.compile() );
+  BOOST_CHECK( m.isCompiled() );
+
+  BOOST_CHECK( !m( "was" ) );
+  BOOST_CHECK( !m( "qwasq" ) );
+  BOOST_CHECK( m( "qwaaq" ) );
+}
+
+#if 0
+BOOST_AUTO_TEST_CASE(AttrMatcher_)
+{
+  base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
+  MIL << "GO" << endl;
+}
+#endif
diff --git a/tests/sat/CMakeLists.txt b/tests/sat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..90e1319
--- /dev/null
@@ -0,0 +1,7 @@
+
+# to find the KeyRingTest receiver
+INCLUDE_DIRECTORIES( ${LIBZYPP_SOURCE_DIR}/tests/zypp )
+
+ADD_TESTS(Solvable SolvParsing WhatProvides WhatObsoletes LookupAttr AttrMatcher IdString)
+
+
diff --git a/tests/sat/IdString_test.cc b/tests/sat/IdString_test.cc
new file mode 100644 (file)
index 0000000..900ff91
--- /dev/null
@@ -0,0 +1,59 @@
+#include "TestSetup.h"
+#include <zypp/sat/LookupAttr.h>
+
+BOOST_AUTO_TEST_CASE(idstring)
+{
+  // id 0 ==> NULL
+  // id 1 ==> ""
+  // evaluates id in a boolean context
+  IdString a0( 0 );
+  IdString a1( 1 );
+
+  BOOST_CHECK_EQUAL( a0.id(), 0 );
+  BOOST_CHECK_EQUAL( a1.id(), 1 );
+
+  BOOST_CHECK( !a0 );
+  BOOST_CHECK( a1 );
+
+  BOOST_CHECK( !(a0 == a1) );
+  BOOST_CHECK(  (a0 != a1) );
+  BOOST_CHECK(  (a0 < a1) );
+  BOOST_CHECK(  (a1 > a0) );
+  BOOST_CHECK( !(a0 >= a1) );
+  BOOST_CHECK( !(a1 <= a0) );
+
+  BOOST_CHECK_EQUAL( a0.compare( (const char *)0 ), 0 );
+  BOOST_CHECK_EQUAL( a0.compare( "" ), -1 );
+  BOOST_CHECK_EQUAL( a0.compare( a1 ), -1 );
+
+  BOOST_CHECK_EQUAL( a1.compare( (const char *)0 ), 1 );
+  BOOST_CHECK_EQUAL( a1.compare( "" ), 0 );
+  BOOST_CHECK_EQUAL( a1.compare( a0 ), 1 );
+}
+
+BOOST_AUTO_TEST_CASE(idstringtype)
+{
+  sat::SolvAttr a0( sat::SolvAttr::allAttr );
+  sat::SolvAttr a1( sat::SolvAttr::noAttr );
+
+  BOOST_CHECK_EQUAL( a0.id(), 0 );
+  BOOST_CHECK_EQUAL( a1.id(), 1 );
+
+  BOOST_CHECK( !a0 );
+  BOOST_CHECK( !a1 ); // evaluates empty string (id 0/1) in a boolean context
+
+  BOOST_CHECK( !(a0 == a1) );
+  BOOST_CHECK(  (a0 != a1) );
+  BOOST_CHECK(  (a0 < a1) );
+  BOOST_CHECK(  (a1 > a0) );
+  BOOST_CHECK( !(a0 >= a1) );
+  BOOST_CHECK( !(a1 <= a0) );
+
+  BOOST_CHECK_EQUAL( a0.compare( (const char *)0 ), 0 );
+  BOOST_CHECK_EQUAL( a0.compare( "" ), -1 );
+  BOOST_CHECK_EQUAL( a0.compare( a1 ), -1 );
+
+  BOOST_CHECK_EQUAL( a1.compare( (const char *)0 ), 1 );
+  BOOST_CHECK_EQUAL( a1.compare( "" ), 0 );
+  BOOST_CHECK_EQUAL( a1.compare( a0 ), 1 );
+}
diff --git a/tests/sat/LookupAttr_test.cc b/tests/sat/LookupAttr_test.cc
new file mode 100644 (file)
index 0000000..d7dd937
--- /dev/null
@@ -0,0 +1,234 @@
+#include "TestSetup.h"
+#include <zypp/sat/LookupAttr.h>
+#include <zypp/sat/AttrMatcher.h>
+#include <zypp/ResObjects.h>
+
+static TestSetup test( Arch_x86_64 );
+
+// Must be the first test!
+BOOST_AUTO_TEST_CASE(bnc_435838)
+{
+  // On the fly check that findSystemRepo does not
+  // cause loading the SystemRepo. check 2 times.
+  BOOST_REQUIRE( ! test.satpool().findSystemRepo() );
+  BOOST_REQUIRE( ! test.satpool().findSystemRepo() );
+
+  // empty @system to pool
+  test.satpool().systemRepo();
+  BOOST_REQUIRE( test.satpool().findSystemRepo() );
+
+  // bnc_435838 crashes if iterating a just created repo.
+  sat::LookupAttr q( sat::SolvAttr::name );
+  for_( it, q.begin(),q.end() )
+    ;
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_init)
+{
+  test.loadTarget(); // initialize and load target
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1" );
+  test.loadRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
+  test.loadRepo( TESTS_SRC_DIR "/data/11.0-update" );
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_defaultconstructed)
+{
+  sat::LookupAttr q;
+  BOOST_CHECK( q.empty() );
+  BOOST_CHECK( q.size() == 0 );
+  BOOST_CHECK_EQUAL( q.begin(), q.end() );
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_nonexistingattr)
+{
+  sat::LookupAttr q( sat::SolvAttr("nonexistingattr") );
+  BOOST_CHECK( q.empty() );
+  BOOST_CHECK( q.size() == 0 );
+  BOOST_CHECK_EQUAL( q.begin(), q.end() );
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_existingattr)
+{
+  sat::LookupAttr q( sat::SolvAttr::name );
+  BOOST_CHECK( ! q.empty() );
+  BOOST_CHECK( q.size() != 0 );
+  BOOST_CHECK_NE( q.begin(), q.end() );
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_existingattr_matcher)
+{
+  sat::LookupAttr q( sat::SolvAttr::name );
+
+  BOOST_CHECK_THROW( q.setAttrMatcher( sat::AttrMatcher("[]ypper",Match::REGEX) ), MatchInvalidRegexException );
+  BOOST_CHECK( ! q.attrMatcher() );
+  BOOST_CHECK_NO_THROW( q.setAttrMatcher( sat::AttrMatcher("[zZ]ypper",Match::REGEX) ) );
+  BOOST_CHECK( q.attrMatcher() );
+
+  BOOST_CHECK_EQUAL( q.size(), 9 );
+  for_(it,q.begin(),q.end())
+  { cout << it << endl;}
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_iterate_solvables)
+{
+  // sat::SolvAttr::name query should visit each solvable once.
+  // So query size and containersize are to be equal if we query
+  // pool/repo/solvable. Quick check whether the iterators
+  // position info matches the result.
+
+  sat::Pool satpool( test.satpool() );
+
+  {
+    // iterate all:
+    sat::LookupAttr q( sat::SolvAttr::name );
+    BOOST_CHECK_EQUAL( q.size(), satpool.solvablesSize() );
+    // quick test whether iterator positions actually matches the result:
+    for_( res, q.begin(), q.end() )
+    {
+      BOOST_CHECK_EQUAL( res.inRepo(), res.inSolvable().repository() );
+      BOOST_CHECK_EQUAL( res.inSolvAttr(), sat::SolvAttr::name );
+    }
+  }
+  {
+    unsigned total = 0;
+    for_( it, satpool.reposBegin(), satpool.reposEnd() )
+    {
+      // iterate one repo:
+      sat::LookupAttr q( sat::SolvAttr::name, *it );
+      BOOST_CHECK_EQUAL( q.size(), it->solvablesSize() );
+      total += q.size();
+      // test result actually matches the repo:
+      for_( res, q.begin(), q.end() )
+      {
+        BOOST_CHECK_EQUAL( res.inRepo(), *it );
+        BOOST_CHECK_EQUAL( res.inRepo(), res.inSolvable().repository() );
+        BOOST_CHECK_EQUAL( res.inSolvAttr(), sat::SolvAttr::name );
+      }
+    }
+    BOOST_CHECK_EQUAL( total, satpool.solvablesSize() );
+  }
+  {
+    unsigned total = 0;
+    for_( it, satpool.solvablesBegin(), satpool.solvablesEnd() )
+    {
+      // iterate one solvable:
+      sat::LookupAttr q( sat::SolvAttr::name, *it );
+      BOOST_CHECK_EQUAL( q.size(), 1 );
+      total += q.size();
+      // test result actually matches the solvable:
+      for_( res, q.begin(), q.end() )
+      {
+        BOOST_CHECK_EQUAL( res.inSolvable(), *it );
+        BOOST_CHECK_EQUAL( res.inRepo(), res.inSolvable().repository() );
+        BOOST_CHECK_EQUAL( res.inSolvAttr(), sat::SolvAttr::name );
+     }
+    }
+    BOOST_CHECK_EQUAL( total, satpool.solvablesSize() );
+  }
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_itetate_all_attributes)
+{
+  sat::Pool satpool( test.satpool() );
+
+  // iterate all:
+  sat::LookupAttr all( sat::SolvAttr::allAttr );
+
+  {
+    unsigned total = 0;
+    for_( it, satpool.reposBegin(), satpool.reposEnd() )
+    {
+      // iterate one repo:
+      sat::LookupAttr q( sat::SolvAttr::allAttr, *it );
+      total += q.size();
+    }
+    BOOST_CHECK_EQUAL( total, all.size() );
+  }
+  {
+    unsigned total = 0;
+    for_( it, satpool.solvablesBegin(), satpool.solvablesEnd() )
+    {
+      // iterate one solvable:
+      sat::LookupAttr q( sat::SolvAttr::allAttr, *it );
+      total += q.size();
+    }
+    BOOST_CHECK_EQUAL( total, all.size() );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_solvable_attribute_substructure)
+{
+  sat::LookupAttr q( sat::SolvAttr::updateReference );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  for_( res, q.begin(), q.end() )
+  {
+    BOOST_CHECK( ! res.subEmpty() );
+    BOOST_CHECK_EQUAL( res.subSize(), 4 );
+
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::allAttr ), res.subBegin() );
+    BOOST_CHECK_EQUAL( res.subFind( "" ),                     res.subBegin() );
+
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReference ), res.subEnd() );
+    BOOST_CHECK_EQUAL( res.subFind( "noval" ),                        res.subEnd() );
+
+    BOOST_CHECK_NE( res.subFind( sat::SolvAttr::updateReferenceType ),  res.subEnd() );
+    BOOST_CHECK_NE( res.subFind( sat::SolvAttr::updateReferenceHref ),  res.subEnd() );
+    BOOST_CHECK_NE( res.subFind( sat::SolvAttr::updateReferenceId ),    res.subEnd() );
+    BOOST_CHECK_NE( res.subFind( sat::SolvAttr::updateReferenceTitle ), res.subEnd() );
+
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceType ),  res.subFind( "type" ) );
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceHref ),  res.subFind( "href" ) );
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceId ),    res.subFind( "id" ) );
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceTitle ), res.subFind( "title" ) );
+
+    // repeatedly calling subBegin() is ok:
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceType ).subBegin(),  res.subBegin() );
+  }
+
+  // search substructure id without parent-structure works for wellknown structures:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  // search id in parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId, sat::SolvAttr::updateReference );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  // search id in any parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId, sat::SolvAttr::allAttr );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  // search any id in parent-structure: (4 ids per updateReference)
+  q = sat::LookupAttr( sat::SolvAttr::allAttr, sat::SolvAttr::updateReference );
+  BOOST_CHECK_EQUAL( q.size(), 1212 );
+
+  // search any id in any parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::allAttr, sat::SolvAttr::allAttr );
+  BOOST_CHECK_EQUAL( q.size(), 10473 );
+}
+
+BOOST_AUTO_TEST_CASE(LookupAttr_repoattr)
+{
+  sat::LookupAttr q( sat::SolvAttr::repositoryAddedFileProvides, sat::LookupAttr::REPO_ATTR );
+  BOOST_CHECK( ! q.empty() );
+  BOOST_CHECK_EQUAL( q.size(), 264 );
+
+  sat::LookupRepoAttr p( sat::SolvAttr::repositoryAddedFileProvides );
+  BOOST_CHECK( ! p.empty() );
+  BOOST_REQUIRE_EQUAL( p.size(), q.size() );
+
+  sat::LookupRepoAttr::iterator pit( p.begin() );
+  for_( qit, q.begin(), q.end() )
+  {
+    BOOST_CHECK_EQUAL( qit, pit );
+    ++pit;
+  }
+}
+
+#if 0
+BOOST_AUTO_TEST_CASE(LookupAttr_)
+{
+  base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
+  MIL << "GO" << endl;
+}
+#endif
diff --git a/tests/sat/SolvParsing_test.cc b/tests/sat/SolvParsing_test.cc
new file mode 100644 (file)
index 0000000..e5f3dcf
--- /dev/null
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/TmpPath.h"
+#include "zypp/RepoManager.h"
+#include "zypp/base/Easy.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/Package.h"
+#include "zypp/sat/Solvable.h"
+
+// allows us to control signature
+// callbacks
+#include "KeyRingTestReceiver.h"
+
+#define BOOST_TEST_MODULE ResObject
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+
+
+/*
+ * this test test that the attributes
+ * from the metadata are preserved into
+ * the final object
+ *
+ * so the test covers both satsolver-tools
+ * right insertion and parsing
+ * and libzypp ResObject and friends data
+ * extraction from solv files
+ */
+
+
+// init the solv
+static void init_pool_yum()
+{
+  Pathname dir(TESTS_SRC_DIR);
+  dir += "/repo/yum/data/10.2-updates-subset";
+
+  ZYpp::Ptr z = getZYpp();
+  ZConfig::instance().setSystemArchitecture(Arch("i586"));
+
+  filesystem::TmpDir tmp;
+  RepoManagerOptions opts = RepoManagerOptions::makeTestSetup(tmp.path());
+  RepoManager mgr(opts);
+
+  KeyRingTestReceiver keyring_callbacks;
+  KeyRingTestSignalReceiver receiver;
+
+  // disable sgnature checking
+  keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+  keyring_callbacks.answerAcceptVerFailed(true);
+  keyring_callbacks.answerAcceptUnknownKey(true);
+
+  RepoInfo info;
+  info.setAlias("updates");
+  info.addBaseUrl(dir.asUrl());
+  mgr.buildCache(info);
+  mgr.loadFromCache(info);
+}
+
+BOOST_AUTO_TEST_CASE(attributes)
+{
+    init_pool_yum();
+    MIL << sat::Pool::instance();
+    Repository r = sat::Pool::instance().reposFind("updates");
+
+    int c = 0;
+
+    for ( Repository::SolvableIterator it = r.solvablesBegin();
+          it != r.solvablesEnd();
+          ++it )
+    {
+        sat::Solvable s = *it;
+        //MIL << s.ident() << endl;
+        if ( s.ident() == "openssl-devel" )
+        {
+            c++;
+            Package::Ptr p = asKind<Package>(makeResObject(s));
+            BOOST_CHECK(p);
+
+            //solvable 5 (6):
+            //name: openssl-devel 0.9.8d-17.2 i586
+            BOOST_CHECK_EQUAL(p->name(), "openssl-devel");
+            //vendor: SUSE LINUX Products GmbH, Nuernberg, Germany
+            BOOST_CHECK_EQUAL(p->vendor(), "SUSE LINUX Products GmbH, Nuernberg, Germany");
+            //solvable:checksum: 9f6a44015ad97680e9f93d0edefa1d533940479c
+            BOOST_CHECK_EQUAL(p->checksum(), CheckSum::sha1("9f6a44015ad97680e9f93d0edefa1d533940479c"));
+            //solvable:summary:
+            BOOST_CHECK_EQUAL(p->summary(), "Include Files and Libraries mandatory for Development.");
+            //solvable:description: This package contains all necessary include files and libraries needed
+            //to develop applications that require these.
+            BOOST_CHECK_EQUAL(p->description(), "This package contains all necessary include files and libraries needed\nto develop applications that require these.");
+            //solvable:authors: Mark J. Cox <mark@openssl.org>
+            //Ralf S. Engelschall <rse@openssl.org>
+            //Dr. Stephen <Henson steve@openssl.org>
+            //Ben Laurie <ben@openssl.org>
+            //Bodo Moeller <bodo@openssl.org>
+            //Ulf Moeller <ulf@openssl.org>
+            //Holger Reif <holger@openssl.org>
+            //Paul C. Sutton <paul@openssl.org>
+            //solvable:packager: http://bugs.opensuse.org
+            //solvable:url: http://www.openssl.org/
+            //solvable:buildtime: 1165493634
+            //solvable:installsize: 3845
+            BOOST_CHECK_EQUAL(p->installSize(), ByteCount(3845, ByteCount::K));
+            //solvable:downloadsize: 909
+            BOOST_CHECK_EQUAL(p->downloadSize(), ByteCount(909, ByteCount::K));
+            //solvable:mediadir: rpm/i586
+            //solvable:mediafile: openssl-devel-0.9.8d-17.2.i586.rpm
+            //solvable:license: BSD License and BSD-like, Other License(s), see package
+            //solvable:group: Development/Libraries/C and C++
+            BOOST_CHECK_EQUAL(p->group(), "Development/Libraries/C and C++");
+            //solvable:sourcearch: src
+            //solvable:sourceevr: (void)
+            //solvable:sourcename: openssl
+            //solvable:headerend: 34861
+
+        }
+    }
+
+    // check that we actually found all testeable
+    // resolvables
+    BOOST_CHECK_EQUAL(c, 1);
+
+
+}
diff --git a/tests/sat/Solvable_test.cc b/tests/sat/Solvable_test.cc
new file mode 100644 (file)
index 0000000..4f3396a
--- /dev/null
@@ -0,0 +1,206 @@
+#include <stdio.h>
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Easy.h"
+#include "zypp/Pattern.h"
+#include "zypp/sat/Solvable.h"
+#include "TestSetup.h"
+
+
+#define BOOST_TEST_MODULE Solvable
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+
+
+BOOST_AUTO_TEST_CASE(test_init)
+{
+  TestSetup test( Arch_x86_64 );
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+  test.loadRepo( TESTS_SRC_DIR "/data/11.0-update", "update" );
+}
+
+
+BOOST_AUTO_TEST_CASE(attributes)
+{
+    MIL << sat::Pool::instance();
+    Repository r = sat::Pool::instance().reposFind("opensuse");
+
+    int c = 0;
+
+    for ( Repository::SolvableIterator it = r.solvablesBegin();
+          it != r.solvablesEnd();
+          ++it )
+    {
+        sat::Solvable s = *it;
+        //MIL << s.ident() << endl;
+        if ( s.ident() == "pattern:apparmor" )
+        {
+            c++;
+
+            //  solvable 21795 (21796):
+            // name: pattern:apparmor 11.0-67 i586
+            // vendor: SUSE LINUX Products GmbH, Nuernberg, Germany
+            // provides:
+            //   pattern:apparmor = 11.0-67
+            // requires:
+            //   pattern:basesystem
+            //   apparmor-parser
+            //   audit
+            //   apparmor-profiles
+            // recommends:
+            //   yast2-apparmor
+            //   apparmor-utils
+            //   pattern:apparmor_opt
+            // solvable:category: Base Technologies
+            // solvable:icon: yast-software
+            // solvable:summary: Novell AppArmor
+            // solvable:description: Novell AppArmor is an application security framework that provides mandatory access control for programs. It protects from exploitation of software flaws and compromised systems. It offers an advanced tool set that automates the development of per-program application security without requiring additional knowledge.
+            // solvable:isvisible: 1
+            // solvable:order: 1030
+            Pattern::Ptr p = asKind<Pattern>(makeResObject(s));
+            BOOST_CHECK(p);
+            BOOST_CHECK_EQUAL(p->name(), "apparmor");
+            BOOST_CHECK_EQUAL(p->vendor(), "SUSE LINUX Products GmbH, Nuernberg, Germany");
+            BOOST_CHECK_EQUAL(p->category(), "Base Technologies");
+            BOOST_CHECK_EQUAL(p->summary(), "Novell AppArmor");
+            BOOST_CHECK_EQUAL(p->icon(), "pattern-apparmor");
+            BOOST_CHECK_EQUAL(p->userVisible(), true);
+            BOOST_CHECK_EQUAL(p->isDefault(), false);
+        }
+        if ( s.ident() == "pattern:default" )
+        {
+            c++;
+            Pattern::Ptr p = asKind<Pattern>(makeResObject(s));
+            BOOST_CHECK(p);
+            BOOST_CHECK_EQUAL(p->userVisible(), false);
+        }
+    }
+
+    // check that we actually found all testeable
+    // resolvables
+    BOOST_CHECK_EQUAL(c, 2);
+}
+
+BOOST_AUTO_TEST_CASE(asString)
+{
+  BOOST_CHECK_EQUAL( sat::Solvable(0).asString(), "noSolvable" );
+  BOOST_CHECK_EQUAL( sat::Solvable(1).asString(), "systemSolvable" );
+  BOOST_CHECK_EQUAL( sat::Solvable(2).asString(), "product:openSUSE-11.1.x86_64" );
+  BOOST_CHECK_EQUAL( sat::Solvable(3693).asString(), "autoyast2-2.16.19-0.1.src" );
+  BOOST_CHECK_EQUAL( sat::Solvable(19222).asString(), "noSolvable" );
+#if 0
+  Repository r = sat::Pool::instance().reposFind("update");
+  for_( it, r.solvablesBegin(), r.solvablesEnd() )
+  {
+    BOOST_CHECK_EQUAL( (*it).asString(), str::numstring((*it).id()) );
+  }
+#endif
+}
+
+BOOST_AUTO_TEST_CASE(SplitIdent)
+{
+  sat::Solvable::SplitIdent split;
+  BOOST_CHECK_EQUAL( split.ident(), IdString() );
+  BOOST_CHECK_EQUAL( split.kind(), ResKind() );
+  BOOST_CHECK_EQUAL( split.name(), IdString() );
+
+  // - kind defaults to package
+  // - package and srcpackage have NO namespaced ident.
+
+  split = sat::Solvable::SplitIdent(   "foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "foo" );
+
+  split = sat::Solvable::SplitIdent(   "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   "package:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::pattern );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   "srcpackage:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" ); // !!!
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::srcpackage );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  // now split from kind,name
+  // - kind spec in name wins!
+
+  split = sat::Solvable::SplitIdent(   ResKind::package,       "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::pattern,       "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::pattern );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::srcpackage,    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::srcpackage );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::package,       "package:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::pattern,       "package:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::srcpackage,    "package:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::package );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::package,       "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::pattern );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::pattern,       "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::pattern );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::srcpackage,    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "pattern:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::pattern );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::package,       "srcpackage:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::srcpackage );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::pattern,       "srcpackage:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::srcpackage );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+  split = sat::Solvable::SplitIdent(   ResKind::srcpackage,    "srcpackage:nokind:foo" );
+  BOOST_CHECK_EQUAL( split.ident(),    "nokind:foo" );
+  BOOST_CHECK_EQUAL( split.kind(),     ResKind::srcpackage );
+  BOOST_CHECK_EQUAL( split.name(),     "nokind:foo" );
+
+}
+
+
diff --git a/tests/sat/WhatObsoletes_test.cc b/tests/sat/WhatObsoletes_test.cc
new file mode 100644 (file)
index 0000000..f9dff54
--- /dev/null
@@ -0,0 +1,35 @@
+#include "TestSetup.h"
+#include <zypp/sat/WhatObsoletes.h>
+
+namespace zypp
+{
+  namespace sat
+  {
+    // Obsoletes may either match against provides, or names.
+    // Configuration depends on the behaviour of rpm.
+    extern bool obsoleteUsesProvides;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(WhatObsoletes)
+{
+  TestSetup test( Arch_x86_64 );
+  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCWhatObsoletes" );
+
+  sat::Solvable test1( 2 );
+  BOOST_REQUIRE_EQUAL( test1.name(), "test1" );
+
+  {
+    sat::obsoleteUsesProvides = true;
+    sat::WhatObsoletes w( test1 );
+    BOOST_REQUIRE( w.size() == 2 );
+    // (3)goaway-1-1.i586(@System)
+    // (5)meetoo-1-1.i586(@System)
+  }
+  {
+    sat::obsoleteUsesProvides = false;
+    sat::WhatObsoletes w( test1 );
+    BOOST_REQUIRE( w.size() == 1 );
+    // (3)goaway-1-1.i586(@System)
+  }
+}
\ No newline at end of file
diff --git a/tests/sat/WhatProvides_test.cc b/tests/sat/WhatProvides_test.cc
new file mode 100644 (file)
index 0000000..090cfa5
--- /dev/null
@@ -0,0 +1,62 @@
+#include "TestSetup.h"
+
+BOOST_AUTO_TEST_CASE(WhatProvides)
+{
+  TestSetup test( Arch_x86_64 );
+  test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
+
+  {
+    sat::WhatProvides q( Capability("zypper") );
+    BOOST_CHECK( ! q.empty() );
+    BOOST_CHECK( q.size() == 1 );
+  }
+
+  {
+    sat::WhatProvides q( Capability("zypper.x86_64 == 0.12.5-1.1") );
+    BOOST_CHECK( ! q.empty() );
+    BOOST_CHECK( q.size() == 1 );
+  }
+
+  {
+    sat::WhatProvides q( Capability("zypper.i586 == 0.12.5-1.1") );
+    BOOST_CHECK( q.empty() );
+    BOOST_CHECK( q.size() == 0 );
+  }
+
+  {
+    // sat::WhatProvides::const_iterator requires proper
+    // copyctor and assignment. If they break
+    sat::WhatProvides q;
+    BOOST_CHECK( q.begin() == q.begin() );
+    BOOST_CHECK( q.begin() == q.end()  );
+
+    q = sat::WhatProvides( Capability("zypper.x86_64 == 0.12.5-1.1") );
+    // q no longer empty
+    BOOST_CHECK( q.begin() == q.begin() );
+    BOOST_CHECK( q.begin() != q.end() );
+
+    sat::WhatProvides::const_iterator a;
+    BOOST_CHECK( a == q.end() );
+
+    sat::WhatProvides::const_iterator b( q.begin() );
+    BOOST_CHECK( b == q.begin() );
+
+//     SEC << LABELED(q.begin()) << endl;
+//     SEC << LABELED(q.end()) << endl;
+//     SEC << LABELED(a) << endl;
+//     SEC << LABELED(b) << endl;
+
+    {
+      a = q.begin();
+      BOOST_CHECK( a == q.begin() );
+//       SEC << LABELED(a) << endl;
+//       SEC << LABELED(b) << endl;
+
+      a = b;
+      BOOST_CHECK( a == b );
+//       SEC << LABELED(a) << endl;
+//       SEC << LABELED(b) << endl;
+    }
+    BOOST_CHECK( a == q.begin() );
+  }
+}
diff --git a/tests/zypp/Arch_test.cc b/tests/zypp/Arch_test.cc
new file mode 100644 (file)
index 0000000..db6c738
--- /dev/null
@@ -0,0 +1,80 @@
+// Arch.cc
+//
+// tests for Arch
+//
+
+#include <iostream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/Arch.h"
+
+// Boost.Test
+#include <boost/test/floating_point_comparison.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using boost::test_tools::close_at_tolerance;
+
+using namespace std;
+using namespace zypp;
+
+/******************************************************************
+**
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+**
+**      DESCRIPTION :
+*/
+BOOST_AUTO_TEST_CASE(arch_test)
+{
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  BOOST_REQUIRE( Arch()   == Arch_noarch );
+  BOOST_REQUIRE( Arch("") == Arch_empty );
+  BOOST_REQUIRE( ! Arch_noarch.empty() );
+  BOOST_REQUIRE( Arch_empty.empty() );
+  BOOST_REQUIRE( Arch_noarch.isBuiltIn() );
+  BOOST_REQUIRE( ! Arch_empty.isBuiltIn() );
+  BOOST_REQUIRE( Arch_empty != Arch_noarch );
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  BOOST_CHECK_EQUAL( Arch("i386"), Arch_i386 );
+  BOOST_CHECK_EQUAL( Arch("i386").idStr(), "i386" );
+  BOOST_CHECK_EQUAL( Arch("i386").asString(), "i386" );
+  BOOST_REQUIRE( Arch_i386.isBuiltIn() );
+
+  BOOST_CHECK_EQUAL( Arch("FOO").idStr(), "FOO" );
+  BOOST_CHECK_EQUAL( Arch("FOO").asString(), "FOO" );
+  BOOST_REQUIRE( ! Arch("FOO").isBuiltIn() );
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  BOOST_REQUIRE( Arch_noarch.compatibleWith( Arch_noarch ) );
+  BOOST_REQUIRE( Arch_noarch.compatibleWith( Arch_i386 ) );
+  BOOST_REQUIRE( Arch_noarch.compatibleWith( Arch_x86_64 ) );
+
+  BOOST_REQUIRE( ! Arch_i386.compatibleWith( Arch_noarch ) );
+  BOOST_REQUIRE( Arch_i386.compatibleWith( Arch_i386 ) );
+  BOOST_REQUIRE( Arch_i386.compatibleWith( Arch_x86_64 ) );
+
+  BOOST_REQUIRE( ! Arch_x86_64.compatibleWith( Arch_noarch ) );
+  BOOST_REQUIRE( ! Arch_x86_64.compatibleWith( Arch_i386 ) );
+  BOOST_REQUIRE( Arch_x86_64.compatibleWith( Arch_x86_64 ) );
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  BOOST_CHECK_EQUAL( Arch::baseArch( Arch_x86_64 ), Arch_x86_64 );
+  BOOST_CHECK_EQUAL( Arch::baseArch( Arch_i686 ), Arch_i386 );
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  BOOST_REQUIRE( Arch_i386.compare( Arch_noarch ) >  0 );
+  BOOST_REQUIRE( Arch_i386.compare( Arch_i386 )   == 0 );
+  BOOST_REQUIRE( Arch_i386.compare( Arch_x86_64 ) <  0 );
+}
diff --git a/tests/zypp/CMakeLists.txt b/tests/zypp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2154389
--- /dev/null
@@ -0,0 +1,40 @@
+ADD_SUBDIRECTORY(base)
+
+# set the vendor.d paths correctly for the tests
+SET(VENDOR_D "${LIBZYPP_SOURCE_DIR}/tests/zypp/data/Vendor/vendors.d")
+FILE(MAKE_DIRECTORY "${LIBZYPP_BINARY_DIR}/tests/zypp/data/Vendor")
+CONFIGURE_FILE(${LIBZYPP_SOURCE_DIR}/tests/zypp/data/Vendor/zypp2.conf.cmake ${LIBZYPP_BINARY_DIR}/tests/zypp/data/Vendor/zypp2.conf @ONLY)
+
+ADD_TESTS(
+  Arch
+  Capabilities
+  CheckSum
+  Date
+  Dup
+  Digest
+  Deltarpm
+  Edition
+  Fetcher
+  FileChecker
+  InstanceId
+  KeyRing
+  Locks
+  MediaSetAccess
+  PathInfo
+  PluginFrame
+  PoolQuery
+  ProgressData
+  PublicKey
+  RWPtr
+  RepoInfo
+  RepoManager
+  RepoStatus
+  ResKind
+  ResStatus
+  Selectable
+  Target
+  Url
+  Vendor
+  Vendor2
+)
+
diff --git a/tests/zypp/Capabilities_test.cc b/tests/zypp/Capabilities_test.cc
new file mode 100644 (file)
index 0000000..0434fa9
--- /dev/null
@@ -0,0 +1,188 @@
+// Capabilities.cc
+//
+// tests for Capabilities
+//
+#include <iostream>
+#include <string>
+
+// Boost.Test
+#include <boost/test/floating_point_comparison.hpp>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "TestSetup.h"
+#include "zypp/Arch.h"
+#include "zypp/Capability.h"
+#include "zypp/Capabilities.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using boost::test_tools::close_at_tolerance;
+
+using namespace std;
+using namespace zypp;
+
+static TestSetup test( Arch_x86_64 );
+
+BOOST_AUTO_TEST_CASE(capabilities_test)
+{
+  //////////////////////////////////////////////////////////////////////
+  // Id 0 and 1 are nor equal, but share the same representation ""/NOCAP
+  //////////////////////////////////////////////////////////////////////
+
+  Capability c0( 0 );  // id 0
+  Capability c1( 1 );  // id 1
+  Capability cD;       // default constructed empty
+  Capability cE( "" ); // empty
+
+  BOOST_CHECK_EQUAL( c0.id(), 0 );
+  BOOST_CHECK_EQUAL( c1.id(), 1 );
+  BOOST_CHECK_EQUAL( Capability().id(), 1 );   // default constructed empty
+  BOOST_CHECK_EQUAL( Capability("").id(), 1 ); // empty
+
+  BOOST_CHECK_EQUAL( c0.asString(), "" );
+  BOOST_CHECK_EQUAL( c1.asString(), "" );
+
+  BOOST_CHECK_EQUAL( c0.empty(), true );
+  BOOST_CHECK_EQUAL( c1.empty(), true );
+
+  BOOST_CHECK_EQUAL( c0.detail().kind(), CapDetail::NOCAP );
+  BOOST_CHECK_EQUAL( c1.detail().kind(), CapDetail::NOCAP );
+
+  BOOST_CHECK_EQUAL( ( c0 == c1 ), false );
+  BOOST_CHECK_EQUAL( Capability::matches( c0, c1 ), CapMatch::yes );
+
+  //////////////////////////////////////////////////////////////////////
+  // skipping internal marker in Capabilities
+  //////////////////////////////////////////////////////////////////////
+
+  Capability r( "req" );
+  Capability p( "prereq" );
+
+  sat::detail::IdType caps[10];
+  caps[0] = r.id();
+  caps[1] = sat::detail::solvablePrereqMarker;
+  caps[2] = p.id();
+  caps[3] = 0;
+
+  // Capabilities with and without prereq (skip marker in ++)
+  Capabilities c( caps );
+  //cout << c << endl;
+  BOOST_CHECK_EQUAL( c.size(), 2 );
+  Capabilities::const_iterator it( c.begin() );
+  BOOST_CHECK_EQUAL( *it, r );
+  BOOST_CHECK_EQUAL( it.tagged(), false );
+  ++it;
+  BOOST_CHECK_EQUAL( *it, p );
+  BOOST_CHECK_EQUAL( it.tagged(), true );
+
+  // Capabilities with prereq only (skip marker in ctor)
+  c = Capabilities( caps+1 );
+  //cout << c << endl;
+  BOOST_CHECK_EQUAL( c.size(), 1 );
+  it = c.begin();
+  BOOST_CHECK_EQUAL( *it, p );
+  BOOST_CHECK_EQUAL( it.tagged(), true );
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  //////////////////////////////////////////////////////////////////////
+  Capability n( "na.me" );
+  Capability na( "na.me.i386" );
+  Capability noe( "na.me == 1" );
+  Capability naoe( "na.me.i386 == 1" );
+
+  BOOST_CHECK_EQUAL( n.detail().kind(), CapDetail::NAMED );
+  BOOST_CHECK_EQUAL( na.detail().kind(), CapDetail::NAMED );
+  BOOST_CHECK_EQUAL( noe.detail().kind(), CapDetail::VERSIONED );
+  BOOST_CHECK_EQUAL( naoe.detail().kind(), CapDetail::VERSIONED );
+
+  BOOST_CHECK_EQUAL( n.detail().hasArch(), false );
+  BOOST_CHECK_EQUAL( na.detail().hasArch(), true );
+  BOOST_CHECK_EQUAL( noe.detail().hasArch(), false );
+  BOOST_CHECK_EQUAL( naoe.detail().hasArch(), true );
+
+  BOOST_CHECK      ( n.detail().arch().empty() );
+  BOOST_CHECK_EQUAL( na.detail().arch(), Arch_i386.idStr() );
+  BOOST_CHECK      ( noe.detail().arch().empty() );
+  BOOST_CHECK_EQUAL( naoe.detail().arch(), Arch_i386.idStr() );
+
+  BOOST_CHECK_EQUAL( Capability( "",     "na.me", "",   "" ), n );
+  BOOST_CHECK_EQUAL( Capability( "i386", "na.me", "",   "" ), na );
+  BOOST_CHECK_EQUAL( Capability( "",     "na.me", "==", "1" ), noe );
+  BOOST_CHECK_EQUAL( Capability( "i386", "na.me", "==", "1" ), naoe );
+
+  // explicit arch
+  BOOST_CHECK_EQUAL( Capability( Arch_i386, "na.me" ), na );
+  BOOST_CHECK_EQUAL( Capability( Arch_i386, "na.me == 1" ), naoe );
+}
+
+BOOST_AUTO_TEST_CASE(guessPackageSpec)
+{
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "" ), Capability( "", "", "", "" ) );
+
+  // Remember: 'name OP ver-rel':      name may match provides
+  //           'name-ver-rel':         name may match package names only
+
+  // With no libzypp in the pool, no guess should succeed:
+  BOOST_REQUIRE( sat::WhatProvides(Capability("libzypp")).empty() );
+
+  // these must be guessed
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp-1-2" ),            Capability( "",         "libzypp-1-2",  "",     "" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp-1-2.i586" ),       Capability( "i586",     "libzypp-1-2",  "",     "" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp.i586-1-2" ),       Capability( "",         "libzypp.i586-1-2", "", "" ) );
+  // these are unambiguous
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "patch:swmgmt=12" ),                Capability( "",         "swmgmt",       "=",    "12", ResKind::patch ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp=0:1.0.2-2" ),      Capability( "",         "libzypp",      "=",    "0:1.0.2-2" ) );
+
+  // now load some repo providing libzypp and see how the guessing changes:
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+  BOOST_REQUIRE( ! sat::WhatProvides(Capability("libzypp")).empty() );
+
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp-1-2" ),            Capability( "",         "libzypp",      "=",    "1-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp-1-2.i586" ),       Capability( "i586",     "libzypp",      "=",    "1-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp.i586-1-2" ),       Capability( "i586",     "libzypp",      "=",    "1-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp<=1.0.2-2" ),       Capability( "",         "libzypp",      "<=",   "1.0.2-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp<=1:1.0.2-2" ),     Capability( "",         "libzypp",      "<=",   "1:1.0.2-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp-0:1.0.2-2" ),      Capability( "",         "libzypp",      "=",    "0:1.0.2-2" ) );
+
+  // now with yast2-packagemanager which is just a provides. Guess must not lead to libzypp.
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "yast2-packagemanager-1-2" ),       Capability( "",         "yast2-packagemanager-1-2",     "",     "" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "yast2-packagemanager-1-2.i586" ),  Capability( "i586",     "yast2-packagemanager-1-2",     "",     "" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "yast2-packagemanager.i586-1-2" ),  Capability( "",         "yast2-packagemanager.i586-1-2","",     "" ) );
+  // these are unambiguous
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "yast2-packagemanager<=1.0.2-2" ),  Capability( "",         "yast2-packagemanager",         "<=",   "1.0.2-2" ) );
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "yast2-packagemanager<=1:1.0.2-2" ),        Capability( "",         "yast2-packagemanager",         "<=",   "1:1.0.2-2" ) );
+
+
+  // Double arch spec: the trailing one succeeds, the other one gets part of the name.
+  // As "libzypp.i586' is not in the pool, guessing fails. Result is a named cap.
+  BOOST_CHECK_EQUAL( Capability::guessPackageSpec( "libzypp.i586-1-2.ppc" ),
+                     Capability( "ppc",  "libzypp.i586-1-2", "", "" ) );
+
+  // Now check prepended kind specs.
+
+  Capability cap( "package:libzypp=1-2" );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "libzypp",              "=",    "1-2", ResKind::package ) );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "package:libzypp",      "=",    "1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "package:libzypp",      "=",    "1-2", ResKind::pattern ) ); // known name prefix wins
+  // 'package:' is the default
+  BOOST_CHECK_EQUAL( cap, Capability( "libzypp=1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "libzypp",              "=",    "1-2" ) );
+
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "package:libzypp-1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "package:libzypp=1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "libzypp-1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "libzypp=1-2" ) );
+
+
+  cap = Capability( "pattern:32bit=1-2" );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "32bit",                "=",    "1-2", ResKind::pattern ) );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "pattern:32bit",        "=",    "1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability( "",      "pattern:32bit",        "=",    "1-2", ResKind::package ) ); // known name prefix wins
+
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "pattern:32bit-1-2" ) );
+  BOOST_CHECK_EQUAL( cap, Capability::guessPackageSpec( "pattern:32bit=1-2" ) );
+}
+
+
diff --git a/tests/zypp/CheckSum_test.cc b/tests/zypp/CheckSum_test.cc
new file mode 100644 (file)
index 0000000..d875e61
--- /dev/null
@@ -0,0 +1,25 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZYpp.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+
+
+// most frequently you implement test cases as a free functions
+BOOST_AUTO_TEST_CASE(checksum_test)
+{
+  BOOST_CHECK_THROW( CheckSum( "sha1", "dsdsads" ), Exception ); // wrong size
+  BOOST_CHECK_THROW( CheckSum( "sha256", "dsdsads" ), Exception ); // wrong size
+  BOOST_CHECK_THROW( CheckSum( "md5", "dsdsads" ), Exception ); // wrong size
+}
diff --git a/tests/zypp/Date_test.cc b/tests/zypp/Date_test.cc
new file mode 100644 (file)
index 0000000..eff850c
--- /dev/null
@@ -0,0 +1,10 @@
+#include "zypp/Date.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE(date_test)
+{
+  std::string format = "%Y-%m-%d %H:%M:%S";
+  std::string date = "2009-02-14 00:31:30";
+  BOOST_CHECK_EQUAL(zypp::Date(date,format).form(format), date);
+}
diff --git a/tests/zypp/Deltarpm_test.cc b/tests/zypp/Deltarpm_test.cc
new file mode 100644 (file)
index 0000000..77e1e67
--- /dev/null
@@ -0,0 +1,69 @@
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+#include "zypp/RepoManager.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/repo/DeltaCandidates.h"
+#include "zypp/repo/PackageDelta.h"
+#include "KeyRingTestReceiver.h"
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::repo;
+using namespace zypp::filesystem;
+
+#define TEST_DIR TESTS_SRC_DIR "/zypp/data/Delta"
+
+BOOST_AUTO_TEST_CASE(delta)
+{
+  KeyRing::setDefaultAccept( KeyRing::ACCEPT_UNKNOWNKEY | KeyRing::ACCEPT_UNSIGNED_FILE );
+
+  TmpDir rootdir;
+  RepoManager rm( RepoManagerOptions::makeTestSetup( rootdir ) );
+
+  RepoInfo updates;
+  updates.setAlias("updates");
+  updates.addBaseUrl( Pathname(TEST_DIR).asUrl() );
+
+  try
+  {
+    rm.buildCache(updates);
+    rm.loadFromCache(updates);
+  }
+  catch (const Exception & e)
+  {
+    BOOST_FAIL( string("Problem getting the data: ")+ e.msg()) ;
+  }
+  sat::Pool pool(sat::Pool::instance());
+
+  repo::DeltaCandidates dc(list<Repository>(pool.reposBegin(),pool.reposEnd()), "libzypp");
+
+  std::list<packagedelta::DeltaRpm> deltas = dc.deltaRpms(0);
+  for_ (it,deltas.begin(),deltas.end())
+  {
+    BOOST_CHECK(it->name() == "libzypp");
+    BOOST_CHECK(it->edition() == Edition("4.21.3-2"));
+    BOOST_CHECK(it->arch() == "i386");
+    BOOST_CHECK(it->baseversion().edition().match(Edition("4.21.3-1"))
+      ||it->baseversion().edition().match(Edition("4.21.2-3")));
+
+    cout << it->name() << " - " << it->edition() << " - " <<  it->arch()
+      << " base: " << it->baseversion().edition() << endl;
+
+    cout << (it->edition() == "4.21.3-2") << endl;              // fine
+    cout << (it->edition() == Edition("4.21.3-2")) << endl;     // fine
+    cout << (it->edition().match(Edition("4.21.3-2")) == 0) << endl; // match returns -1,0,1
+    cout << (it->edition().match("4.21.3-2") == 0) << endl;          // match returns -1,0,1
+  }
+}
diff --git a/tests/zypp/Digest_test.cc b/tests/zypp/Digest_test.cc
new file mode 100644 (file)
index 0000000..19e83c0
--- /dev/null
@@ -0,0 +1,36 @@
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/PathInfo.h"
+#include "zypp/Digest.h"
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+/**
+ * Test case for
+ * static std::string digest(const std::string& name, std::istream& is, size_t bufsize = 4096);
+ */
+BOOST_AUTO_TEST_CASE(digest)
+{
+  string data("I will test the checksum of this");
+  stringstream str1(data);
+  stringstream str2(data);
+  stringstream str3(data);
+
+  BOOST_CHECK_EQUAL( Digest::digest( "sha1", str1 ), "142df4277c326f3549520478c188cab6e3b5d042" ); 
+  BOOST_CHECK_EQUAL( Digest::digest( "md5", str2 ), "f139a810b84d82d1f29fc53c5e59beae" ); 
+  // FIXME i think it should throw
+  BOOST_CHECK_EQUAL( Digest::digest( "lalala", str3) , "" ); 
+}
diff --git a/tests/zypp/Dup_test.cc b/tests/zypp/Dup_test.cc
new file mode 100644 (file)
index 0000000..248a03d
--- /dev/null
@@ -0,0 +1,63 @@
+#include "TestSetup.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResPoolProxy.h"
+#include "zypp/pool/PoolStats.h"
+#include "zypp/ui/Selectable.h"
+
+#define BOOST_TEST_MODULE Dup
+
+/////////////////////////////////////////////////////////////////////////////
+
+static TestSetup test;
+
+template <class _Iterator>
+std::ostream & vdumpPoolStats( std::ostream & str, _Iterator begin_r, _Iterator end_r )
+{
+  pool::PoolStats stats;
+  for_( it, begin_r, end_r )
+  {
+    str << *it << endl;
+    stats( *it );
+  }
+  return str << stats;
+}
+
+bool upgrade()
+{
+  bool rres = false;
+  {
+    rres = getZYpp()->resolver()->doUpgrade();
+  }
+  if ( ! rres )
+  {
+    ERR << "upgrade " << rres << endl;
+    getZYpp()->resolver()->problems();
+    return false;
+  }
+  MIL << "upgrade " << rres << endl;
+  vdumpPoolStats( USR << "Transacting:"<< endl,
+                  make_filter_begin<resfilter::ByTransact>(test.pool()),
+                  make_filter_end<resfilter::ByTransact>(test.pool()) ) << endl;
+  return true;
+}
+
+
+BOOST_AUTO_TEST_CASE(testcase_init)
+{
+  //zypp::base::LogControl::instance().logToStdErr();
+  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCdup" );
+  dumpRange( USR, test.pool().knownRepositoriesBegin(),
+                  test.pool().knownRepositoriesEnd() ) << endl;
+  USR << "pool: " << test.pool() << endl;
+  BOOST_REQUIRE( upgrade() );
+}
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(orphaned)
+{
+  ResPoolProxy proxy( test.poolProxy() );
+  BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "glibc" )->status(),              ui::S_KeepInstalled );
+  BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "release-package" )->status(),    ui::S_AutoUpdate );
+  BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "dropped_required" )->status(),   ui::S_KeepInstalled );
+  BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "dropped" )->status(),            ui::S_AutoDel );
+}
diff --git a/tests/zypp/Edition_test.cc b/tests/zypp/Edition_test.cc
new file mode 100644 (file)
index 0000000..09bca5b
--- /dev/null
@@ -0,0 +1,39 @@
+// Edition.cc
+//
+// tests for Edition
+//
+
+#include "zypp/base/Logger.h"
+#include "zypp/Edition.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+
+BOOST_AUTO_TEST_CASE(edition)
+{
+  Edition _ed1 ("1");
+  Edition _ed2 ("1.1");
+  Edition _ed3 ("1:1");
+  Edition _ed4 ("2:1-1");
+
+  BOOST_CHECK_EQUAL(_ed2.version(), "1.1");
+  BOOST_CHECK_EQUAL(_ed2.release(), "");
+  BOOST_CHECK_EQUAL(_ed2.epoch(), 0U);
+  BOOST_CHECK_EQUAL(_ed4.epoch(), 2U);
+
+  BOOST_CHECK_EQUAL(_ed1, Edition ("1", ""));
+  BOOST_CHECK_EQUAL(_ed2, Edition ("1.1", ""));
+  BOOST_CHECK_EQUAL(_ed2, Edition ("1_1", "")); // Edition strings may differ in separator (non alphanum)
+  BOOST_CHECK_EQUAL(_ed2, Edition ("0:1.1")); // epoch 0 is no epoch
+  BOOST_CHECK_EQUAL(_ed3, Edition ("1", "", "1"));
+  BOOST_CHECK_EQUAL(_ed3, Edition ("1", "", 1));
+  BOOST_CHECK_EQUAL(_ed4, Edition ("1", "1", 2));
+
+  BOOST_CHECK_EQUAL( Edition::compare("1:1-1","2:1-1"), -1 );
+  BOOST_CHECK_EQUAL( Edition::compare("2:1-1","2:1-1"), 0 );
+  BOOST_CHECK_EQUAL( Edition::compare("3:1-1","2:1-1"), 1 );
+}
diff --git a/tests/zypp/Fetcher_test.cc b/tests/zypp/Fetcher_test.cc
new file mode 100644 (file)
index 0000000..0adcb6f
--- /dev/null
@@ -0,0 +1,374 @@
+#include "TestSetup.h"
+
+#include "zypp/MediaSetAccess.h"
+#include "zypp/Fetcher.h"
+
+#include "WebServer.h"
+
+#define BOOST_TEST_MODULE fetcher_test
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/zypp/data/Fetcher/remote-site")
+
+BOOST_AUTO_TEST_SUITE( fetcher_test );
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuedir_noindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // Now test that without trusting it, it should throw
+  // do the test by trusting the SHA1SUMS file signature key
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      fetcher.enqueueDir(OnMediaLocation("/complexdir"), true);
+      fetcher.start( dest.path(), media );
+      fetcher.reset();
+
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2/subdir2-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file2.txt").isExist() );
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuedir_autoindex_untrustedkey)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key
+  {
+      filesystem::TmpDir dest;
+
+      // add the key as untrusted, which is the same as not adding it and
+      // let autodiscovery to add it
+
+      Fetcher fetcher;
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDir(OnMediaLocation("/complexdir"), true);
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir/subdir2").isExist() );
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir/subdir2/subdir2-file1.txt").isExist() );
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file2.txt").isExist() );
+
+      fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuedir_autoindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key
+  {
+      filesystem::TmpDir dest;
+
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir/subdir1/SHA1SUMS.key"), true);
+      Fetcher fetcher;
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDir(OnMediaLocation("/complexdir"), true);
+      fetcher.start( dest.path(), media );
+
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2/subdir2-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file2.txt").isExist() );
+
+      fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueue_digested_dir_autoindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key but with a broken file
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir-broken/subdir1/SHA1SUMS.key"), true);
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDigestedDir(OnMediaLocation("/complexdir-broken"), true);
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), FileCheckException);
+      fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuebrokendir_noindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key but with a broken file
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir-broken/subdir1/SHA1SUMS.key"), true);
+      fetcher.enqueueDir(OnMediaLocation("/complexdir-broken"), true);
+      // this should not throw as we provided no indexes and the
+      // enqueue is not digested
+      fetcher.start( dest.path(), media );
+      fetcher.reset();
+
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir2").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir2/subdir2-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir1/subdir1-file2.txt").isExist() );
+
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuebrokendir_index)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key but with a broken file
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir-broken/subdir1/SHA1SUMS.key"), true);
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDir(OnMediaLocation("/complexdir-broken"), true);
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+      fetcher.reset();
+  }
+}
+
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueue_digesteddir_brokendir_with_index)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key but with a broken file
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir-broken/subdir1/SHA1SUMS.key"), true);
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDigestedDir(OnMediaLocation("/complexdir-broken"), true);
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+      fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueue_digested_broken_with_autoindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key but with a broken file
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir-broken/subdir1/SHA1SUMS.key"), true);
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDigested(OnMediaLocation("/complexdir-broken/subdir1/subdir1-file1.txt"));
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+      fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueue_digested_with_autoindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  // do the test by trusting the SHA1SUMS file signature key with a good file
+  // checksum in auto discovered index
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      // add the key as trusted
+      getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/complexdir/subdir1/SHA1SUMS.key"), true);
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDigested(OnMediaLocation("/complexdir/subdir1/subdir1-file1.txt"));
+      fetcher.start( dest.path(), media );
+      fetcher.reset();
+  }
+}
+
+
+BOOST_AUTO_TEST_CASE(fetcher_enqueuefile_noindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  {
+      filesystem::TmpDir dest;
+      Fetcher fetcher;
+      fetcher.enqueue(OnMediaLocation("/file-1.txt"));
+      fetcher.start( dest.path(), media );
+      BOOST_CHECK( PathInfo(dest.path() + "/file-1.txt").isExist() );
+  }
+
+  //MIL << fetcher;
+}
+
+BOOST_AUTO_TEST_CASE(fetcher_simple)
+{
+    MediaSetAccess media( (DATADIR).asUrl(), "/" );
+    Fetcher fetcher;
+
+    {
+        filesystem::TmpDir dest;
+        OnMediaLocation loc("/complexdir/subdir1/subdir1-file1.txt");
+        loc.setChecksum(CheckSum::sha1("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15"));
+        fetcher.enqueueDigested(loc);
+        fetcher.start(dest.path(), media);
+        fetcher.reset();
+        // now we break the checksum and it should fail
+        loc.setChecksum(CheckSum::sha1("f1d2d2f924e986ac86fdf7b36c94bcdf32beec16"));
+        fetcher.enqueueDigested(loc);
+        BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+        fetcher.reset();
+
+    }
+}
+
+BOOST_AUTO_TEST_CASE(content_index)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  Fetcher fetcher;
+
+  // test transfering one file by setting the index
+  {
+        filesystem::TmpDir dest;
+        OnMediaLocation loc("/contentindex/subdir1/subdir1-file1.txt");
+        // trust the key manually
+        getZYpp()->keyRing()->importKey(PublicKey(DATADIR + "/contentindex/content.key"), true);
+        fetcher.addIndex(OnMediaLocation("/contentindex/content", 1));
+        fetcher.enqueue(loc);
+        fetcher.start(dest.path(), media);
+        fetcher.reset();
+        BOOST_CHECK( PathInfo(dest.path() + "/contentindex/subdir1/subdir1-file1.txt").isExist() );
+  }
+
+}
+
+BOOST_AUTO_TEST_CASE(enqueue_broken_content_index)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  Fetcher fetcher;
+  filesystem::TmpDir dest;
+  {
+        OnMediaLocation loc("/contentindex-broken-digest/subdir1/subdir1-file1.txt",1);
+        // key was already imported as trusted
+        fetcher.addIndex(OnMediaLocation("/contentindex-broken-digest/content", 1));
+        fetcher.enqueue(loc);
+        fetcher.start(dest.path(), media);
+        fetcher.reset();
+        BOOST_CHECK( PathInfo(dest.path() + "/contentindex-broken-digest/subdir1/subdir1-file1.txt").isExist() );
+
+        // now retrieve a file that is modified, so the checksum has to fail
+        loc = OnMediaLocation("/contentindex-broken-digest/subdir1/subdir1-file2.txt",1);
+        fetcher.addIndex(OnMediaLocation("/contentindex-broken-digest/content", 1));
+        fetcher.enqueue(loc);
+        BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), Exception);
+        fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(enqueue_digested_images_file_content_autoindex)
+{
+  MediaSetAccess media( ( DATADIR + "/images-file").asUrl(), "/" );
+  Fetcher fetcher;
+  filesystem::TmpDir dest;
+  {
+        OnMediaLocation loc("/images/images.xml",1);
+        fetcher.setOptions( Fetcher::AutoAddIndexes );
+        fetcher.enqueueDigested(loc);
+        fetcher.start(dest.path(), media);
+        fetcher.reset();
+        BOOST_CHECK( PathInfo(dest.path() + "/images/images.xml").isExist() );
+        fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(enqueue_digested_images_file_content_autoindex_unsigned)
+{
+  MediaSetAccess media( ( DATADIR + "/images-file-unsigned").asUrl(), "/" );
+  Fetcher fetcher;
+  filesystem::TmpDir dest;
+  {
+        OnMediaLocation loc("/images/images.xml",1);
+        fetcher.setOptions( Fetcher::AutoAddIndexes );
+        fetcher.enqueueDigested(loc);
+        // it should throw because unsigned file throws
+        BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), FileCheckException);
+        fetcher.reset();
+        // the target file was NOT transfered
+        BOOST_CHECK( ! PathInfo(dest.path() + "/images/images.xml").isExist() );
+        fetcher.reset();
+  }
+}
+
+BOOST_AUTO_TEST_CASE(enqueue_broken_content_noindex)
+{
+  MediaSetAccess media( ( DATADIR).asUrl(), "/" );
+  Fetcher fetcher;
+
+  {
+        filesystem::TmpDir dest;
+        OnMediaLocation loc("/contentindex-broken-digest/subdir1/subdir1-file1.txt",1);
+        // key was already imported as trusted
+        fetcher.enqueue(loc);
+        fetcher.start(dest.path(), media);
+        fetcher.reset();
+        // now retrieve a file that is modified, so the checksum has to fail
+        loc = OnMediaLocation("/contentindex-broken-digest/subdir1/subdir1-file2.txt",1);
+        fetcher.enqueue(loc);
+        fetcher.start( dest.path(), media );
+        fetcher.reset();
+        BOOST_CHECK( PathInfo(dest.path() + "/contentindex-broken-digest/subdir1/subdir1-file2.txt").isExist() );
+
+  }
+}
+
+
+BOOST_AUTO_TEST_CASE(enqueuedir_http)
+{
+    WebServer web((Pathname(TESTS_SRC_DIR) + "/zypp/data/Fetcher/remote-site").c_str(), 10001);
+    web.start();
+
+  // at this point the key is already trusted
+  {
+      MediaSetAccess media( web.url(), "/" );
+      Fetcher fetcher;
+      filesystem::TmpDir dest;
+
+      // auto add the SHA1SUMS
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDir(OnMediaLocation("/complexdir"), true);
+      fetcher.start( dest.path(), media );
+
+      fetcher.reset();
+
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir2/subdir2-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir/subdir1/subdir1-file2.txt").isExist() );
+  }
+
+  // test broken tree
+  {
+      MediaSetAccess media( web.url(), "/" );
+      Fetcher fetcher;
+      filesystem::TmpDir dest;
+
+      // auto add the SHA1SUMS
+      fetcher.setOptions( Fetcher::AutoAddIndexes );
+      fetcher.enqueueDir(OnMediaLocation("/complexdir-broken"), true);
+      // should throw because wrong checksum
+      BOOST_CHECK_THROW( fetcher.start( dest.path(), media ), FileCheckException);
+      fetcher.reset();
+
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir2").isExist() );
+
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir-broken/subdir2/subdir2-file1.txt").isExist() );
+
+      // this one got transfered before the failure, so it is there
+      BOOST_CHECK( PathInfo(dest.path() + "/complexdir-broken/subdir1/subdir1-file1.txt").isExist() );
+      BOOST_CHECK( ! PathInfo(dest.path() + "/complexdir-broken/subdir1/subdir1-file2.txt").isExist() );
+
+      fetcher.reset();
+  }
+  
+  web.stop();
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/zypp/FileChecker_test.cc b/tests/zypp/FileChecker_test.cc
new file mode 100644 (file)
index 0000000..dffd17b
--- /dev/null
@@ -0,0 +1,90 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/KeyRing.h"
+#include "zypp/PublicKey.h"
+#include "zypp/TmpPath.h"
+
+#include "zypp/FileChecker.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "KeyRingTestReceiver.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/zypp/data/FileChecker")
+
+BOOST_AUTO_TEST_CASE(keyring_test)
+{
+  Pathname file( Pathname(DATADIR) + "hello.txt" );
+  Pathname file2( Pathname(DATADIR) + "hello2.txt" );
+  Pathname pubkey( Pathname(DATADIR) + "hello.txt.key" );
+  Pathname signature( Pathname(DATADIR) + "hello.txt.asc" );
+  
+  /**
+   * 1st scenario, the signature does
+   * match
+   */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+    SignatureFileChecker sigchecker( signature );
+    sigchecker.addPublicKey(pubkey);
+    sigchecker(file); 
+  }
+  
+  /**
+   * second scenario, the signature does not
+   * match, an exception has to be thrown
+   */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+    SignatureFileChecker sigchecker( signature );
+    sigchecker.addPublicKey(pubkey);
+    
+    BOOST_CHECK_THROW( sigchecker(file2), zypp::Exception );
+
+  }
+  
+}
+
+BOOST_AUTO_TEST_CASE(checksum_test)
+{
+  Pathname file( Pathname(DATADIR) + "hello.txt" );
+  Pathname file2( Pathname(DATADIR) + "hello2.txt" );
+  Pathname pubkey( Pathname(DATADIR) + "hello.txt.key" );
+  Pathname signature( Pathname(DATADIR) + "hello.txt.asc" );
+  
+  /**
+   * 1st scenario, checksum matches
+   */
+  {
+    ChecksumFileChecker checker( CheckSum("sha1", "f2105202a0f017ab818b670d04982a89f55f090b") );
+    checker(file);
+  }
+  
+  /**
+   * 1st scenario, checksum does not matches
+   */
+  {
+    ChecksumFileChecker checker( CheckSum("sha1", "f2105202a0f017ab818b670d04982a89f55f090b") );
+    BOOST_CHECK_THROW( checker(file2), zypp::FileCheckException );
+  }
+}
+
diff --git a/tests/zypp/InstanceId_test.cc b/tests/zypp/InstanceId_test.cc
new file mode 100644 (file)
index 0000000..248056b
--- /dev/null
@@ -0,0 +1,43 @@
+#include "TestSetup.h"
+#include "zypp/InstanceId.h"
+
+#define BOOST_TEST_MODULE InstanceId
+
+/////////////////////////////////////////////////////////////////////////////
+static TestSetup test( Arch_x86_64 );
+
+BOOST_AUTO_TEST_CASE(pool_query_init)
+{
+  // Abuse;) vbox as System repo:
+  test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(default_constructed)
+{
+  InstanceId instanceId;
+  BOOST_CHECK_EQUAL( instanceId.getNamespace(), std::string() );
+
+  BOOST_CHECK_EQUAL( instanceId.isSystemId( "System" ), false );
+  BOOST_CHECK_EQUAL( instanceId.isSystemId( "@System" ), true );
+
+  BOOST_CHECK_EQUAL( instanceId(""), PoolItem() );
+  BOOST_CHECK_EQUAL( instanceId(PoolItem()), "" );
+}
+
+BOOST_AUTO_TEST_CASE(convert)
+{
+  InstanceId instanceId;
+  instanceId.setNamespace( "SUSE" );
+  BOOST_CHECK_EQUAL( instanceId.getNamespace(), "SUSE" );
+
+  ResPool pool( ResPool::instance() );
+  for_( it, pool.begin(), pool.end() )
+  {
+    std::cout << instanceId(*it) << endl;
+    BOOST_CHECK_EQUAL( instanceId(instanceId(*it)), *it );
+  }
+}
diff --git a/tests/zypp/KeyRingTestReceiver.h b/tests/zypp/KeyRingTestReceiver.h
new file mode 100644 (file)
index 0000000..dce6744
--- /dev/null
@@ -0,0 +1,142 @@
+
+#ifndef _ZYPP_KEYRING_TEST_RECEIVER_H
+#define _ZYPP_KEYRING_TEST_RECEIVER_H
+
+#include "zypp/Callback.h"
+#include "zypp/KeyRing.h"
+#include "zypp/PublicKey.h"
+#include "zypp/KeyContext.h"
+
+/**
+ * Keyring Callback Receiver with some features
+ * Allows to simulate and configure user answer
+ * Can record which callbacks were called
+ */
+struct KeyRingTestReceiver : public zypp::callback::ReceiveReport<zypp::KeyRingReport>
+{
+  KeyRingTestReceiver()
+  {
+    reset();
+    connect();
+  }
+
+  void reset()
+  {
+    _answer_accept_unknown_key = false;
+    _answer_accept_key = KeyRingReport::KEY_DONT_TRUST;
+    _answer_ver_failed = false;
+    _answer_accept_unsigned_file = false;
+    _asked_user_to_accept_unknown_key = false;
+    _asked_user_to_accept_key = false;
+    _asked_user_to_accept_ver_failed = false;
+    _asked_user_to_accept_unsigned_file = false;
+  }
+  
+  ~KeyRingTestReceiver()
+  {
+    disconnect();
+  }
+  
+  void answerAcceptVerFailed( bool answer )
+  { _answer_ver_failed = answer; }
+  
+  bool askedAcceptVerFailed() const
+  { return _asked_user_to_accept_ver_failed; }
+  
+  void answerAcceptUnknownKey( bool answer )
+  { _answer_accept_unknown_key = answer; }
+  
+  bool askedAcceptUnknownKey() const
+  { return _asked_user_to_accept_unknown_key; }
+  
+  void answerAcceptKey( KeyRingReport::KeyTrust answer )
+  { _answer_accept_key = answer; }
+  
+  bool askedAcceptKey() const
+  { return _asked_user_to_accept_key; }
+
+  void answerAcceptUnsignedFile( bool answer )
+  { _answer_accept_unsigned_file = answer; }
+  
+  bool askedAcceptUnsignedFile() const
+  { return _asked_user_to_accept_unsigned_file; }
+    
+  virtual bool askUserToAcceptUnsignedFile( const std::string &file, const zypp::KeyContext &keycontext )
+  {
+    MIL << std::endl;
+    _asked_user_to_accept_unsigned_file = true;
+    return _answer_accept_unsigned_file;
+  }
+  
+  virtual bool askUserToAcceptUnknownKey( const std::string &file, const std::string &id, const zypp::KeyContext &keycontext )
+  {
+    MIL << std::endl;
+    _asked_user_to_accept_unknown_key = true;
+    return _answer_accept_unknown_key;
+  }
+
+  virtual KeyRingReport::KeyTrust askUserToAcceptKey( const zypp::PublicKey &key, const zypp::KeyContext &keycontext )
+  {
+    MIL << std::endl;
+    _asked_user_to_accept_key = true;
+    return _answer_accept_key;
+  }
+
+  virtual bool askUserToAcceptVerificationFailed( const std::string &file,  const zypp::PublicKey &key, const zypp::KeyContext &keycontext  )
+  {
+    MIL << std::endl;
+    _asked_user_to_accept_ver_failed = true;
+    return _answer_ver_failed;
+  }
+  
+  // how to answer
+  bool _answer_accept_unknown_key;
+  KeyRingReport::KeyTrust _answer_accept_key;
+  bool _answer_ver_failed;
+  bool _answer_accept_unsigned_file;
+  
+  // we use this variables to check that the
+  // callbacks were called
+  bool _asked_user_to_accept_unknown_key;
+  bool _asked_user_to_accept_key;
+  bool _asked_user_to_accept_ver_failed;
+  bool _asked_user_to_accept_unsigned_file;
+};
+
+/**
+ * Keyring Signal Receiver with some features
+ * Allows to simulate and configure user answer
+ * Can record which callbacks were called
+ */
+struct KeyRingTestSignalReceiver : zypp::callback::ReceiveReport<zypp::KeyRingSignals>
+{
+  KeyRingTestSignalReceiver(/*RpmDb &rpmdb*/)
+  : _trusted_key_added_called(false)
+  {
+    MIL << "KeyRing signals enabled" << std::endl;
+    connect();
+  }
+
+  ~KeyRingTestSignalReceiver()
+  {
+    disconnect();
+  }
+
+  virtual void trustedKeyAdded( const zypp::PublicKey &key )
+  {
+    MIL << "TEST: trusted key added to zypp Keyring. Syncronizing keys with fake rpm keyring" << std::endl;
+    _trusted_key_added_called = true;
+    //std::cout << "trusted key added to zypp Keyring. Syncronizing keys with rpm keyring" << std::endl;
+    //_rpmdb.importZyppKeyRingTrustedKeys();
+    //_rpmdb.exportTrustedKeysInZyppKeyRing();
+  }
+
+  virtual void trustedKeyRemoved( const zypp::PublicKey &key  )
+  {
+  }
+  
+  bool _trusted_key_added_called;
+  
+};
+
+#endif
diff --git a/tests/zypp/KeyRing_test.cc b/tests/zypp/KeyRing_test.cc
new file mode 100644 (file)
index 0000000..a29854c
--- /dev/null
@@ -0,0 +1,211 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/KeyRing.h"
+#include "zypp/PublicKey.h"
+#include "zypp/TmpPath.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "KeyRingTestReceiver.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) +  "/zypp/data/KeyRing")
+
+BOOST_AUTO_TEST_CASE(keyring_test)
+{
+  PublicKey key( Pathname(DATADIR) + "public.asc" );
+
+ /** 
+  * scenario #1
+  * import a not trusted key
+  * ask for accept, answer yes 'temporarily'
+  */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+  
+    keyring.importKey( key, false );
+    
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 1 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+    
+    BOOST_CHECK_MESSAGE( keyring.isKeyKnown( key.id() ), "Imported untrusted key should be known");
+    BOOST_CHECK_MESSAGE( ! keyring.isKeyTrusted( key.id() ), "Imported untrusted key should be untrusted");
+    
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+    bool to_continue = keyring.verifyFileSignatureWorkflow( DATADIR + "repomd.xml", "Blah Blah", DATADIR + "repomd.xml.asc");
+  
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnknownKey(), "Should not ask for unknown key, it was known");
+    BOOST_CHECK_MESSAGE( keyring_callbacks.askedAcceptKey(), "Verify Signature Workflow with only 1 untrusted key should ask user wether to trust and/or import");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptVerFailed(), "The signature validates");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnsignedFile(), "It is a signed file, so dont ask the opposite");
+    
+    BOOST_CHECK_MESSAGE( to_continue, "We did not import, but we trusted and signature validates.");
+  }
+
+  /** 
+  * scenario #1.1
+  * import a not trusted key
+  * ask to accept, answer yes 'temporarily'
+  * vorrupt the file and check
+  */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+    
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+  
+    keyring.importKey( key, false );
+    
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+
+    // now we will recheck with a corrupted file
+    bool to_continue = keyring.verifyFileSignatureWorkflow( DATADIR + "repomd.xml.corrupted", "Blah Blah", DATADIR + "repomd.xml.asc");
+    
+    // check wether the user got the right questions
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnknownKey(), "Should not ask for unknown key, it was known");
+    BOOST_CHECK_MESSAGE( keyring_callbacks.askedAcceptKey(), "Verify Signature Workflow with only 1 untrusted key should ask user wether to trust and/or import");
+    BOOST_CHECK_MESSAGE( keyring_callbacks.askedAcceptVerFailed(), "The signature does not validates");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnsignedFile(), "It is a signed file, so dont ask the opposite");
+
+    BOOST_CHECK_MESSAGE( ! to_continue, "We did not continue with a corrupted file");
+  }
+  
+   /** 
+  * scenario #1.2
+  * import a not trusted key
+  * ask for trust, answer yes
+  * ask for import, answer no
+  * check without signature
+  */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+    
+    keyring.importKey( key, false );
+    
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+    // now we will recheck with a unsigned file
+    bool to_continue = keyring.verifyFileSignatureWorkflow( DATADIR + "repomd.xml", "Blah Blah", Pathname() );
+    
+    // check wether the user got the right questions
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnknownKey(), "Should not ask for unknown key, it was known");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptKey(), "No signature, no key to trust");
+    BOOST_CHECK_MESSAGE( keyring_callbacks.askedAcceptUnsignedFile(), "Ask the user wether to accept an unsigned file");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptVerFailed(), "There is no signature to verify");
+    
+    BOOST_CHECK_MESSAGE( ! to_continue, "We did not continue with a unsigned file");
+  }
+  
+ /** scenario #2
+  * empty keyring
+  * should ask for unknown key
+  * answer no
+  */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+
+    BOOST_CHECK_MESSAGE( ! keyring.isKeyKnown( key.id() ), "empty keyring has not known keys");
+
+    //keyring_callbacks.answerAcceptUnknownKey(true);
+    bool to_continue = keyring.verifyFileSignatureWorkflow( DATADIR + "repomd.xml", "Blah Blah", DATADIR + "repomd.xml.asc");
+    BOOST_CHECK_MESSAGE(keyring_callbacks.askedAcceptUnknownKey(), "Should ask to accept unknown key, empty keyring");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptKey(), "Unknown key cant be trusted");
+    BOOST_CHECK_MESSAGE( ! to_continue, "We answered no to accept unknown key");
+  }
+
+  /** scenario #3
+  * import trusted key
+  * should ask nothing
+  * should emit signal
+  */
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+    
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 0 );
+  
+    keyring.importKey( key, true );
+
+    BOOST_CHECK_EQUAL( receiver._trusted_key_added_called, true );
+    
+    BOOST_CHECK_EQUAL( keyring.publicKeys().size(), (unsigned) 0 );
+    BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), (unsigned) 1 );
+    
+    BOOST_CHECK_MESSAGE( keyring.isKeyKnown( key.id() ), "Imported trusted key should be known");
+    BOOST_CHECK_MESSAGE( keyring.isKeyTrusted( key.id() ), "Imported trusted key should be trusted");
+    
+    bool to_continue = keyring.verifyFileSignatureWorkflow( DATADIR + "repomd.xml", "Blah Blah", DATADIR + "repomd.xml.asc");
+  
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnknownKey(), "Should not ask for unknown key, it was known");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptKey(), "Verify Signature Workflow with only 1 untrusted key should ask user wether to trust and/or import");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptVerFailed(), "The signature validates");
+    BOOST_CHECK_MESSAGE( ! keyring_callbacks.askedAcceptUnsignedFile(), "It is a signed file, so dont ask the opposite");
+    
+    BOOST_CHECK_MESSAGE( to_continue, "We did not import, but we trusted and signature validates.");
+  }
+  //keyring.importKey( key, true );
+  //BOOST_CHECK_EQUAL( receiver._trusted_key_added_called, true );
+  //BOOST_CHECK_EQUAL( keyring.trustedPublicKeys().size(), 1 );
+
+  /* check signature id can be extracted */
+  
+}
+
+BOOST_AUTO_TEST_CASE(signature_test)
+{
+  PublicKey key( DATADIR + "public.asc" );
+
+  {
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+    // base sandbox for playing
+    TmpDir tmp_dir;
+    KeyRing keyring( tmp_dir.path() );
+    
+    BOOST_CHECK_EQUAL( keyring.readSignatureKeyId( DATADIR + "repomd.xml.asc" ), "BD61D89BD98821BE" );
+    BOOST_CHECK_THROW( keyring.readSignatureKeyId(Pathname()), Exception );
+    TmpFile tmp;
+    BOOST_CHECK_EQUAL( keyring.readSignatureKeyId(tmp.path()), "" );
+
+    keyring.importKey(key);
+
+    BOOST_CHECK(keyring.verifyFileSignature( DATADIR + "repomd.xml", DATADIR + "repomd.xml.asc"));
+    BOOST_CHECK( ! keyring.verifyFileSignature( DATADIR + "repomd.xml.corrupted", DATADIR + "repomd.xml.asc"));
+  }
+}
+
+
diff --git a/tests/zypp/Locks_test.cc b/tests/zypp/Locks_test.cc
new file mode 100644 (file)
index 0000000..aac487b
--- /dev/null
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include <iostream>
+#include <iterator>
+#include <boost/test/auto_unit_test.hpp>
+#include <list>
+
+#include "zypp/PoolQuery.h"
+#include "zypp/PoolQueryUtil.tcc"
+#include "zypp/TmpPath.h"
+#include "zypp/Locks.h"
+#include "TestSetup.h"
+
+#define BOOST_TEST_MODULE Locks
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+
+bool isLocked( const sat::Solvable & solvable )
+{
+  zypp::PoolItem pi( zypp::ResPool::instance().find( solvable ) );
+  if( pi.status().isLocked() )
+    return true;
+  return false;
+}
+
+BOOST_AUTO_TEST_CASE(pool_query_init)
+{
+  TestSetup test( Arch_x86_64 );
+  //test.loadTarget(); // initialize and load target
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "@System" );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//  0xx basic queries
+/////////////////////////////////////////////////////////////////////////////
+
+// default query + one search string
+// q.addString("foo");
+// result: all resolvables having at least one attribute matching foo
+BOOST_AUTO_TEST_CASE(locks_1)
+{
+  cout << "****001****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  Locks::instance().addLock(q);
+  for_(it,q.begin(),q.end())
+  {
+    BOOST_CHECK(isLocked(*it));
+  }
+  Locks::instance().removeLock(q); //clear before next test
+}
+
+BOOST_AUTO_TEST_CASE(locks_save_load)
+{
+  cout << "****save/load****"  << endl;
+  Pathname src(TESTS_SRC_DIR);
+    src += "zypp/data/Locks/locks";
+  Locks::instance().readAndApply(src);
+  PoolQuery q;
+  q.addString("zypper");
+  for_(it,q.begin(),q.end())
+  {
+    BOOST_CHECK(isLocked(*it));
+  }
+#if 1 
+  filesystem::TmpFile testfile;
+  //Pathname testfile(TESTS_SRC_DIR);
+    //  testfile += "/zypp/data/Locks/testlocks";
+  Locks::instance().removeLock(q); 
+  Locks::instance().save(testfile);
+  Locks::instance().readAndApply(testfile);
+  //now unlocked
+  for_(it,q.begin(),q.end())
+  {
+    BOOST_CHECK(!isLocked(*it));
+  }
+  BOOST_CHECK(Locks::instance().size()==0);
+#endif
+}
+
+BOOST_AUTO_TEST_CASE(locks_save_without_redundancy)
+{
+  cout << "****save without redundancy****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  Locks& locks = Locks::instance();
+  locks.addLock(q);
+  locks.addLock(q);
+  locks.merge();
+  BOOST_CHECK( locks.size()==1 );
+  locks.addLock(q);
+  locks.merge();
+  BOOST_CHECK( locks.size()==1 );
+  locks.removeLock(q);
+  locks.merge();
+  BOOST_CHECK( locks.size() == 0 );
+}
+
+BOOST_AUTO_TEST_CASE( locks_empty )
+{
+  cout << "****test and clear empty locks****"  << endl;
+  PoolQuery q;
+  q.addString("foo-bar-nonexist");
+  Locks& locks = Locks::instance();
+  locks.addLock(q);
+  locks.merge(); //only need merge list
+  BOOST_CHECK( locks.existEmpty() );
+  locks.removeEmpty();
+  BOOST_CHECK( locks.size() == 0 );
+}
diff --git a/tests/zypp/MediaSetAccess_test.cc b/tests/zypp/MediaSetAccess_test.cc
new file mode 100644 (file)
index 0000000..33dea03
--- /dev/null
@@ -0,0 +1,267 @@
+#include <stdio.h>
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/parameterized_test.hpp>
+#include <boost/test/unit_test_log.hpp>
+
+#include "zypp/MediaSetAccess.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+#include "WebServer.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using namespace zypp;
+using namespace boost::unit_test;
+using namespace zypp::filesystem;
+
+class SimpleVerifier : public media::MediaVerifierBase
+{
+public:
+
+  SimpleVerifier( const std::string &id )
+  {
+    _media_id = id;
+  }
+
+  virtual bool isDesiredMedia(const media::MediaAccessRef &ref)
+  {
+    return ref->doesFileExist(Pathname("/x." + _media_id ));
+  }
+
+private:
+  std::string _media_id;
+};
+
+bool check_file_exists(const Pathname &path)
+{
+  FILE *file;
+
+  if ((file = fopen(path.asString().c_str(), "r")) == NULL) return false;
+
+  fclose(file);
+  return true;
+}
+
+/*
+ * Check how MediaSetAccess::rewriteUrl() works.
+ */
+BOOST_AUTO_TEST_CASE(msa_url_rewrite)
+{
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/CD1.iso"), 1).asString(),
+    Url("iso:/?iso=/path/to/CD1.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/CD1.iso"), 2).asString(),
+    Url("iso:/?iso=/path/to/CD2.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/CD1.iso"), 13).asString(),
+    Url("iso:/?iso=/path/to/CD13.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/cd1.iso"), 2).asString(),
+    Url("iso:/?iso=/path/to/cd2.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/cd2.iso"), 1).asString(),
+    Url("iso:/?iso=/path/to/cd1.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("iso:/?iso=/path/to/dvd1.iso"), 2).asString(),
+    Url("iso:/?iso=/path/to/dvd2.iso").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("dir:/path/to/CD1"), 2).asString(),
+    Url("dir:/path/to/CD2").asString());
+
+  // trailing slash check
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("dir:/path/to/CD1/"), 2).asString(),
+    Url("dir:/path/to/CD2/").asString());
+
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("nfs://nfs-server/exported/path/to/dvd1"), 2).asString(),
+    Url("nfs://nfs-server/exported/path/to/dvd2").asString());
+
+  // single media check  shouldn't this fail somehow??
+  BOOST_CHECK_EQUAL(
+    MediaSetAccess::rewriteUrl(Url("http://ftp.opensuse.org/pub/opensuse/distribution/SL-OSS-factory/inst-source"), 2).asString(),
+    Url("http://ftp.opensuse.org/pub/opensuse/distribution/SL-OSS-factory/inst-source").asString());
+}
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) / "/zypp/data/mediasetaccess")
+
+/*
+ *
+ * test data dir structure:
+ *
+ * .
+ * |-- src1
+ * |   |-- cd1
+ * |   |   |-- dir
+ * |   |   |   |-- file1
+ * |   |   |   |-- file2
+ * |   |   |   `-- subdir
+ * |   |   |       `-- file
+ * |   |   `-- test.txt
+ * |   |-- cd2
+ * |   |   `-- test.txt
+ * |   `-- cd3
+ * |       `-- test.txt
+ * `-- src2
+ *     `-- test.txt
+ *
+ */
+
+/*
+ * Provide files from set without verifiers.
+ */
+BOOST_AUTO_TEST_CASE(msa_provide_files_set)
+{
+  Url url = (DATADIR + "/src1/cd1").asUrl();
+  MediaSetAccess setaccess(url);
+
+  Pathname file1 = setaccess.provideFile("/test.txt", 1);
+  BOOST_CHECK(check_file_exists(file1) == true);
+
+  Pathname file2 = setaccess.provideFile("/test.txt", 2);
+  BOOST_CHECK(check_file_exists(file2) == true);
+
+  Pathname file3 = setaccess.provideFile("/test.txt", 3);
+  BOOST_CHECK(check_file_exists(file3) == true);
+}
+
+/*
+ * Provide files from set with verifiers.
+ */
+BOOST_AUTO_TEST_CASE(msa_provide_files_set_verified)
+{
+  Url url = (DATADIR + "/src1/cd1").asUrl();
+  MediaSetAccess setaccess(url);
+
+  setaccess.setVerifier(1, media::MediaVerifierRef(new SimpleVerifier("media1")));
+  setaccess.setVerifier(2, media::MediaVerifierRef(new SimpleVerifier("media2")));
+  setaccess.setVerifier(3, media::MediaVerifierRef(new SimpleVerifier("media3")));
+
+  // provide file from media1
+  Pathname file1 = setaccess.provideFile("/test.txt", 1);
+  BOOST_CHECK(check_file_exists(file1) == true);
+
+  // provide file from invalid media
+  BOOST_CHECK_THROW(setaccess.provideFile("/test.txt", 2),
+                    media::MediaNotDesiredException);
+
+  // provide file from media3
+  Pathname file3 = setaccess.provideFile("/test.txt", 3);
+  BOOST_CHECK(check_file_exists(file3) == true);
+}
+
+/*
+ * Provide file from single media with verifier.
+ */
+BOOST_AUTO_TEST_CASE(msa_provide_files_single)
+{
+  Url url = (DATADIR + "/src2").asUrl();
+  MediaSetAccess setaccess(url);
+  setaccess.setVerifier(1, media::MediaVerifierRef(new SimpleVerifier("media")));
+
+  // provide file from media
+  Pathname file = setaccess.provideFile("/test.txt", 1);
+  BOOST_CHECK(check_file_exists(file) == true);
+
+  // provide non-existent file
+  // (default answer from callback should be ABORT)
+  BOOST_CHECK_THROW(setaccess.provideFile("/imnothere", 2),
+                    media::MediaFileNotFoundException);
+}
+
+/*
+ * Provide directory from src/cd1.
+ */
+BOOST_AUTO_TEST_CASE(msa_provide_dir)
+{
+  Url url = (DATADIR + "/src1/cd1").asUrl();
+
+  MediaSetAccess setaccess(url);
+
+  Pathname dir = setaccess.provideDir("/dir", false, 1);
+
+  Pathname file1 = dir + "/file1";
+  BOOST_CHECK(check_file_exists(file1) == true);
+
+  Pathname file2 = dir + "/file2";
+  BOOST_CHECK(check_file_exists(file2) == true);
+
+  // provide non-existent dir
+  // (default answer from callback should be ABORT)
+  BOOST_CHECK_THROW(setaccess.provideDir("/imnothere", 2),
+                    media::MediaFileNotFoundException);
+
+  // This can't be properly tested with 'dir' schema, probably only curl
+  // schemas (http, ftp) where download is actually needed.
+  // Other schemas just get mounted onto a local dir and the whole subtree
+  // is automatically available that way.
+  // BOOST_CHECK(check_file_exists(dir + "/subdir/file") == false);
+  // BOOST_CHECK(check_file_exists(dir + "/subdir") == false);
+}
+
+
+/*
+ * Provide directory from src/cd1 (recursively).
+ */
+BOOST_AUTO_TEST_CASE(msa_provide_dirtree)
+{
+  Url url = (DATADIR + "/src1/cd1").asUrl();
+  MediaSetAccess setaccess(url);
+
+  Pathname dir = setaccess.provideDir("/dir", true, 1);
+
+  Pathname file1 = dir + "/file1";
+  BOOST_CHECK(check_file_exists(file1) == true);
+
+  Pathname file2 = dir + "/file2";
+  BOOST_CHECK(check_file_exists(file2) == true);
+
+  Pathname file3 = dir + "/subdir/file";
+  BOOST_CHECK(check_file_exists(file3) == true);
+}
+
+/*
+ * file exists local
+ */
+BOOST_AUTO_TEST_CASE(msa_file_exist_local)
+{
+  Url url = (DATADIR + "/src1/cd1").asUrl();
+  MediaSetAccess setaccess(url);
+
+  BOOST_CHECK(setaccess.doesFileExist("/test.txt"));
+  BOOST_CHECK(!setaccess.doesFileExist("/testBADNAME.txt"));
+}
+
+/*
+ * file exists remote
+ */
+BOOST_AUTO_TEST_CASE(msa_remote_tests)
+{
+  WebServer web( DATADIR / "/src1/cd1", 10002 );
+  web.start();
+  MediaSetAccess setaccess( web.url(), "/" );
+
+  BOOST_CHECK(!setaccess.doesFileExist("/testBADNAME.txt"));
+  BOOST_CHECK(setaccess.doesFileExist("/test.txt"));
+
+  // check providing a file via http works
+  Pathname local = setaccess.provideFile("/test.txt");
+  BOOST_CHECK(CheckSum::sha1(sha1sum(local)) == CheckSum::sha1("2616e23301d7fcf7ac3324142f8c748cd0b6692b"));
+
+  // providing a file which does not exist should throw
+  BOOST_CHECK_THROW(setaccess.provideFile("/testBADNAME.txt"), media::MediaFileNotFoundException);
+  web.stop();
+}
+
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/zypp/PathInfo_test.cc b/tests/zypp/PathInfo_test.cc
new file mode 100644 (file)
index 0000000..148c410
--- /dev/null
@@ -0,0 +1,212 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/Exception.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+/**
+ * Test case for
+ * bool is_checksum( const Pathname & file, const CheckSum &checksum );
+ * std::string checksum( const Pathname & file, const std::string &algorithm );
+ */
+BOOST_AUTO_TEST_CASE(pathinfo_checksum_test)
+{
+  const char *buffer = "I will test the checksum of this";
+  TmpFile file;
+  ofstream str(file.path().asString().c_str(),ofstream::out);
+
+  if (!str.good())
+    ZYPP_THROW(Exception("cant open file"));
+
+  str << buffer;
+  str.flush();
+  str.close();
+
+  CheckSum file_sha1("sha1", "142df4277c326f3549520478c188cab6e3b5d042");
+  CheckSum file_md5("md5", "f139a810b84d82d1f29fc53c5e59beae");
+
+  BOOST_CHECK_EQUAL( checksum( file.path(), "sha1"), "142df4277c326f3549520478c188cab6e3b5d042" );
+  BOOST_CHECK_EQUAL( checksum( file.path(), "md5"), "f139a810b84d82d1f29fc53c5e59beae" );
+
+  BOOST_REQUIRE( is_checksum( file.path(), file_sha1 ) );
+  BOOST_REQUIRE( is_checksum( file.path(), file_md5 ) );
+}
+
+BOOST_AUTO_TEST_CASE(pathinfo_is_exist_test)
+{
+  TmpDir dir;
+  Pathname subdir("text with spaces");
+  // create a fake file
+  BOOST_CHECK_EQUAL( filesystem::mkdir(dir.path() + subdir), 0 );
+
+  Pathname filepath = (dir.path() + subdir+ "filename");
+  ofstream str(filepath.asString().c_str(),ofstream::out);
+  str << "foo bar" << endl;
+  str.flush();
+  str.close();
+
+  BOOST_CHECK( PathInfo(filepath).isExist() );
+}
+
+BOOST_AUTO_TEST_CASE(pathipathinfo_misc_test)
+{
+  TmpDir dir;
+
+  PathInfo info(dir.path());
+  BOOST_CHECK(info.isDir());
+}
+
+BOOST_AUTO_TEST_CASE(pathinfo_expandlink_test)
+{
+  TmpDir dir;
+
+  // ---- not a link
+
+  // create a file
+  Pathname file(dir / "file");
+  ofstream str(file.asString().c_str(),ofstream::out);
+  str << "foo bar" << endl;
+  str.flush();
+  str.close();
+
+  // expandlink should return the original Pathname if it does not point to a link
+  BOOST_CHECK_EQUAL( file, filesystem::expandlink(file) );
+
+  // ---- valid link
+
+  // create a (relative!) link to that file
+  Pathname link1(dir / "link1");
+  BOOST_CHECK_EQUAL( filesystem::symlink(file.basename(), link1), 0);
+
+  // does the link expand to the file?
+  BOOST_CHECK_EQUAL( file, filesystem::expandlink(link1) );
+
+  // ---- broken link
+
+  // create a link to a non-existent file
+  Pathname brokenlink(dir / "brokenlink");
+  Pathname non_existent(dir / "non-existent");
+  BOOST_CHECK_EQUAL( filesystem::symlink(non_existent, brokenlink), 0);
+  PathInfo info(brokenlink, PathInfo::LSTAT);
+  BOOST_CHECK(info.isLink());
+
+  // expandlink should return an empty Pathname for a broken link
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(brokenlink) );
+
+  // ---- cyclic link
+
+  // make the 'non-existent' a link to 'brokenlink' :O)
+  BOOST_CHECK_EQUAL( filesystem::symlink(brokenlink, non_existent), 0);
+  // expandlink should return an empty Pathname for such a cyclic link
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(brokenlink) );
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(non_existent) );
+
+  cout << brokenlink << " -> " << filesystem::expandlink(brokenlink) << endl;
+}
+
+BOOST_AUTO_TEST_CASE(test_assert_dir_file)
+{
+  TmpDir   root;
+
+  Pathname rfile( root/"file" );
+  BOOST_CHECK_EQUAL( filesystem::assert_file( rfile ), 0 );
+  BOOST_CHECK( PathInfo(rfile).isFile() );
+
+  Pathname rdir ( root/"dir" );
+  BOOST_CHECK_EQUAL( filesystem::assert_dir ( rdir ),  0 );
+  BOOST_CHECK( PathInfo(rdir).isDir() );
+
+  // empty path
+  Pathname path;
+  BOOST_CHECK_EQUAL( filesystem::assert_file( path ), ENOENT );
+  BOOST_CHECK_EQUAL( filesystem::assert_dir ( path ), ENOENT );
+
+  // for dirs:
+  // existing dir
+  path = rdir;
+  BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), 0 );
+  BOOST_CHECK( PathInfo(path).isDir() );
+  // new dirs
+  path = rdir/"sub/subsub";
+  BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), 0 );
+  BOOST_CHECK( PathInfo(path).isDir() );
+  // file in path
+  path = rfile/"sub";
+  BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), ENOTDIR );
+  BOOST_CHECK( !PathInfo(path).isDir() );
+  // path is file
+  path = rfile;
+  BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), EEXIST );
+  BOOST_CHECK( !PathInfo(path).isDir() );
+
+  // for files:
+  // existing file
+  path = rfile;
+  BOOST_CHECK_EQUAL( filesystem::assert_file( path ), 0 );
+  BOOST_CHECK( PathInfo(path).isFile() );
+  // new file
+  path = rdir/"sub/file";
+  BOOST_CHECK_EQUAL( filesystem::assert_file( path ), 0 );
+  BOOST_CHECK( PathInfo(path).isFile() );
+  // file in path
+  path = rfile/"sub/file";
+  BOOST_CHECK_EQUAL( filesystem::assert_file( path ), ENOTDIR );
+  BOOST_CHECK( ! PathInfo(path).isFile() );
+  // path is dir
+  path = rdir;
+  BOOST_CHECK_EQUAL( filesystem::assert_file( path ), EEXIST );
+  BOOST_CHECK( ! PathInfo(path).isFile() );
+}
+
+BOOST_AUTO_TEST_CASE(test_exchange)
+{
+  TmpDir root;
+  Pathname a;
+  Pathname b;
+  // paths must not be epmty:
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), EINVAL );
+  a = root/"a/p";
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), EINVAL );
+  b = root/"b/p";
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 ); // ok if both don't exist
+
+  // one path not existing:
+  filesystem::assert_file( a );
+  BOOST_CHECK( PathInfo(a).isFile() );
+  BOOST_CHECK( !PathInfo(b).isFile() );
+
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+  BOOST_CHECK( !PathInfo(a).isFile() );
+  BOOST_CHECK( PathInfo(b).isFile() );
+
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+  BOOST_CHECK( PathInfo(a).isFile() );
+  BOOST_CHECK( !PathInfo(b).isFile() );
+
+  // both paths exist:
+  filesystem::assert_dir( b );
+  BOOST_CHECK( PathInfo(b).isDir() );
+
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+  BOOST_CHECK( PathInfo(a).isDir() );
+  BOOST_CHECK( PathInfo(b).isFile() );
+
+  BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+  BOOST_CHECK( PathInfo(a).isFile() );
+  BOOST_CHECK( PathInfo(b).isDir() );
+}
diff --git a/tests/zypp/PluginFrame_test.cc b/tests/zypp/PluginFrame_test.cc
new file mode 100644 (file)
index 0000000..c64cf43
--- /dev/null
@@ -0,0 +1,168 @@
+#include <sys/types.h>
+#include <signal.h>
+
+#include <iostream>
+#include <sstream>
+
+#include "TestSetup.h"
+#include "zypp/PluginScript.h"
+
+BOOST_AUTO_TEST_CASE(InitialSettings)
+{
+  PluginScript::defaultTimeout( 3 );
+}
+
+BOOST_AUTO_TEST_CASE(PluginFrameDefaultCtor)
+{
+  PluginFrame f;
+  BOOST_CHECK_EQUAL( f.empty(), true );
+  BOOST_CHECK_EQUAL( bool(f), f.empty() );
+  BOOST_CHECK_EQUAL( f.command().empty(), true );
+  BOOST_CHECK_EQUAL( f.body().empty(), true );
+  BOOST_CHECK_EQUAL( f.headerEmpty(), true );
+  BOOST_CHECK_EQUAL( (f == f), true );
+  BOOST_CHECK_EQUAL( (f != f), false );
+}
+
+BOOST_AUTO_TEST_CASE(PluginFrameCtorAssign)
+{
+  PluginFrame f( "command" );
+  BOOST_CHECK_EQUAL( f.empty(), false );
+  BOOST_CHECK_EQUAL( bool(f), f.empty() );
+  BOOST_CHECK_EQUAL( f.command(), "command" );
+  BOOST_CHECK_EQUAL( f.body().empty(), true );
+  BOOST_CHECK_EQUAL( f.headerEmpty(), true );
+  BOOST_CHECK_EQUAL( (f == f), true );
+  BOOST_CHECK_EQUAL( (f != f), false );
+
+  PluginFrame g( "command", "body" );
+  BOOST_CHECK_EQUAL( g.empty(), false );
+  BOOST_CHECK_EQUAL( bool(g), g.empty() );
+  BOOST_CHECK_EQUAL( g.command(), "command" );
+  BOOST_CHECK_EQUAL( g.body(), "body");
+  BOOST_CHECK_EQUAL( g.headerEmpty(), true );
+  BOOST_CHECK_EQUAL( (f == g), false );
+  BOOST_CHECK_EQUAL( (f != g), true );
+
+  PluginFrame h( g );
+  BOOST_CHECK_EQUAL( (g == h), true );
+
+  h.addHeader( "" ); // empty KV in header is ok, if you like it
+  BOOST_CHECK_EQUAL( (g == h), false );
+
+  h.addHeader( "a", "a1" );
+  h.addHeader( "a", "a2" );
+  h.addHeader( "b", "b1" );
+  h.addHeader( "b", "b2" );
+  h.addHeader( "c", "c1" );
+  h.addHeader( "c", "c1" );
+  BOOST_CHECK_EQUAL( h.headerSize(), 7 );
+
+  h.setHeader( "b", "b" ); // replaces existing 'b:" headers
+  BOOST_CHECK_EQUAL( h.headerSize(), 6 );
+
+  // now write and reparse from stream:
+  std::string data;
+  {
+    std::ostringstream datas;
+    h.writeTo( datas );
+    datas.str().swap( data );
+  }
+  std::istringstream datas( data );
+  PluginFrame i( datas );
+  BOOST_CHECK_EQUAL( (h == i), true );
+}
+
+void doParse( const std::string & str_r )
+{
+  std::string data( str_r );
+  data.push_back( '\0' );
+  std::istringstream datas( data );
+   PluginFrame i( datas );
+}
+
+BOOST_AUTO_TEST_CASE(PluginFrameExceptipn)
+{
+  BOOST_CHECK_THROW( PluginFrame( "c\nc" ), PluginFrameException );
+  PluginFrame f;
+  BOOST_CHECK_THROW( f.addHeader( "c\nc" ), PluginFrameException );
+  BOOST_CHECK_THROW( f.addHeader( "c:c" ), PluginFrameException );
+  BOOST_CHECK_THROW( f.addHeader( "cc", "c\nc" ), PluginFrameException );
+
+  BOOST_CHECK_THROW( doParse( "c" ), PluginFrameException );                   // no NL after command
+  BOOST_CHECK_THROW( doParse( "c\n" ), PluginFrameException );                 // no NL after header
+  doParse( "c\n\n" );          // valid empy header and body
+  BOOST_CHECK_THROW( doParse( "c\nh:v\nbody" ), PluginFrameException );                // no NL after header
+  doParse( "c\nh:v\n\nbody" ); // valid
+  BOOST_CHECK_THROW( doParse( "c\nhv\n\nbody" ), PluginFrameException );       // no : in header
+}
+
+BOOST_AUTO_TEST_CASE(PluginScriptTest)
+{
+  PluginScript scr;
+  BOOST_CHECK_EQUAL( scr.isOpen(), false );
+  BOOST_CHECK_EQUAL( scr.getPid(), PluginScript::NotConnected );
+  BOOST_CHECK_EQUAL( scr.script(), "" );
+
+  BOOST_CHECK_THROW( scr.open( "bla" ), PluginScriptException );       // script does not exist
+  BOOST_CHECK_EQUAL( scr.isOpen(), false );                            // stay closed
+  BOOST_CHECK_EQUAL( scr.getPid(), PluginScript::NotConnected );
+  BOOST_CHECK_EQUAL( scr.script(), "" );
+
+  scr.open( "/bin/cat" );
+  BOOST_CHECK_EQUAL( scr.isOpen(), true );
+  BOOST_CHECK_EQUAL( (scr.getPid() != PluginScript::NotConnected ), true );
+  BOOST_CHECK_EQUAL( scr.script(), "/bin/cat" );                       // set after successfull open
+
+  BOOST_CHECK_THROW( scr.open( "/bin/ls" ), PluginScriptException );   // already open
+  BOOST_CHECK_EQUAL( scr.isOpen(), true );                             // stay with "/bin/cat"
+  BOOST_CHECK_EQUAL( (scr.getPid() != PluginScript::NotConnected ), true );
+  BOOST_CHECK_EQUAL( scr.script(), "/bin/cat" );
+
+  PluginFrame f;
+  scr.send( f );
+  PluginFrame r( scr.receive() );
+  BOOST_CHECK_EQUAL( f, r );
+
+  f.setCommand( "CMD" );
+  f.addHeader( "a","value" );
+  f.setBody( "foo" );
+  scr.send( f );
+  r = scr.receive();
+  BOOST_CHECK_EQUAL( f, r );
+
+  scr.close();
+  BOOST_CHECK_EQUAL( scr.isOpen(), false );
+  BOOST_CHECK_EQUAL( scr.getPid(), PluginScript::NotConnected );
+  BOOST_CHECK_EQUAL( scr.script(), "/bin/cat" );                       // not reset by close, may be reused by open()
+
+  scr.close();                                                         // no exception on dupl. close.
+}
+
+BOOST_AUTO_TEST_CASE(PluginScriptSend)
+{
+  PluginFrame f( "a" );
+  f.setBody( std::string( 1020, '0' ) );
+
+  PluginScript scr( "/bin/cat" );
+  BOOST_CHECK_THROW( scr.send( f ), PluginScriptNotConnected );
+
+  scr.open();
+  BOOST_CHECK_THROW( do { scr.send( f ); } while ( true ), PluginScriptSendTimeout );
+
+  ::kill( scr.getPid(), SIGKILL);
+  BOOST_CHECK_THROW( scr.send( f ), PluginScriptDiedUnexpectedly );
+}
+
+BOOST_AUTO_TEST_CASE(PluginScriptReceive)
+{
+  PluginFrame f( "a" );
+  f.setBody( std::string( 1020, '0' ) );
+
+  PluginScript scr( "/bin/cat" );
+  scr.open();
+  BOOST_CHECK_THROW( scr.receive(), PluginScriptReceiveTimeout );
+
+  ::kill( scr.getPid(), SIGKILL);
+  BOOST_CHECK_THROW(  scr.receive(), PluginScriptDiedUnexpectedly );
+}
diff --git a/tests/zypp/PoolQuery_test.cc b/tests/zypp/PoolQuery_test.cc
new file mode 100644 (file)
index 0000000..19a7ab4
--- /dev/null
@@ -0,0 +1,842 @@
+#include "TestSetup.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/PoolQueryUtil.tcc"
+
+#define BOOST_TEST_MODULE PoolQuery
+
+/////////////////////////////////////////////////////////////////////////////
+static TestSetup test( Arch_x86_64 );
+
+BOOST_AUTO_TEST_CASE(pool_query_init)
+{
+  // Abuse;) vbox as System repo:
+  test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
+  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
+
+  dumpRange( USR, test.pool().knownRepositoriesBegin(),
+                  test.pool().knownRepositoriesEnd() );
+  USR << "pool: " << test.pool() << endl;
+}
+/////////////////////////////////////////////////////////////////////////////
+
+static std::ofstream devNull;
+#define COUT devNull
+
+struct PrintAndCount
+{
+  PrintAndCount() : _count(0) {}
+
+  bool operator()( const sat::Solvable & solvable )
+  {
+    zypp::PoolItem pi( zypp::ResPool::instance().find( solvable ) );
+    COUT << pi.resolvable() << endl;
+    ++_count;
+    return true;
+  }
+
+  unsigned _count;
+};
+
+void dumpQ( std::ostream & str, const PoolQuery & q, bool verbose = true )
+{
+  q.begin();
+  str << q << endl;
+  unsigned nc = 0;
+  if ( 1 )
+  {
+    for_( it, q.begin(), q.end() )
+    {
+      ++nc;
+      if ( verbose )
+        str << it << endl;
+    }
+    str << "--> MATCHES: " << nc << endl;
+  }
+}
+
+
+#if 0
+BOOST_AUTO_TEST_CASE(pool_query_experiment)
+{
+  cout << "****experiment****"  << endl;
+
+  PoolQuery q;
+  q.addString("zypper");
+  q.addAttribute(sat::SolvAttr::name);
+
+  // should list 1 selectable?
+  cout << "****selectables****"  << endl;
+  for (PoolQuery::Selectable_iterator it = q.selectableBegin();
+       it != q.selectableEnd(); ++it)
+  {
+    ui::Selectable::Ptr s = *it;
+    cout << s->kind() << ":" << s->name() << " hasinstalled: " << s->installedEmpty() << endl;
+  }
+  cout << "****solvables****" << endl;
+  PrintAndCount cb;
+  std::for_each(q.begin(), q.end(), cb);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//  0xx basic queries
+/////////////////////////////////////////////////////////////////////////////
+
+// no conditions, default query
+// result: all available resolvables
+BOOST_AUTO_TEST_CASE(pool_query_000)
+{
+  cout << "****000****"  << endl;
+  PoolQuery q;
+  cout << q.size() << endl;
+  BOOST_CHECK(q.size() == 3811);
+
+  /* dumpsolv repo1.solv repo2.solv repo3.solv | \
+     grep '^name:.*\(noarch\|x86_64\|i386\|i586\|i686\|src\)$' | wc -l */
+}
+
+// default query + one search string
+// q.addString("foo");
+// result: all resolvables having at least one attribute matching foo
+BOOST_AUTO_TEST_CASE(pool_query_001)
+{
+  cout << "****001****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 11);
+}
+
+// default query + one attribute + one string
+// q.addAttribute(foo, bar);
+// should be the same as
+// q.addAttribute(foo); q.addString(bar);
+// result: resolvables with foo containing bar
+BOOST_AUTO_TEST_CASE(pool_query_002)
+{
+  cout << "****002****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  q.addAttribute(sat::SolvAttr::name);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 5);
+
+  cout << endl;
+
+  PoolQuery q1;
+  q1.addAttribute(sat::SolvAttr::name, "zypper");
+
+  BOOST_CHECK(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count == 5);
+}
+
+// kind filter
+BOOST_AUTO_TEST_CASE(pool_query_003)
+{
+  cout << "****003****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addKind(ResKind::package);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 3);
+}
+
+// match exact
+BOOST_AUTO_TEST_CASE(pool_query_004)
+{
+  cout << "****004****"  << endl;
+  PoolQuery q;
+  q.addString("vim");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchExact();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 1);
+
+  PoolQuery q1;
+  q1.addString("zypp");
+  q1.addAttribute(sat::SolvAttr::name);
+  q1.setMatchExact();
+
+  std::for_each(q1.begin(), q1.end(), PrintAndCount());
+  BOOST_CHECK(q1.empty());
+}
+
+// use globs
+BOOST_AUTO_TEST_CASE(pool_query_005)
+{
+  cout << "****005.1****"  << endl;
+  PoolQuery q;
+  q.addString("z?p*");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchGlob();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 6);
+
+  cout << "****005.2****"  << endl;
+
+  PoolQuery q1;
+  q1.addString("*zypp*");
+  q1.addAttribute(sat::SolvAttr::name);
+  q1.setMatchGlob();
+
+  BOOST_CHECK(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count == 26);
+
+  cout << "****005.3****"  << endl;
+
+  // should be the same as above
+  PoolQuery q2;
+  q2.addString("zypp");
+  q2.addAttribute(sat::SolvAttr::name);
+
+  BOOST_CHECK(q2.size() == 26);
+}
+
+// use regex
+BOOST_AUTO_TEST_CASE(pool_query_006)
+{
+  cout << "****006.1***"  << endl;
+
+  // should be the same as 005 1
+  PoolQuery q;
+  q.addString("^z.p.*");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchRegex();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 6);
+
+  cout << "****006.2***"  << endl;
+
+  PoolQuery q1;
+  q1.addString("zypper|smart");
+  q1.addAttribute(sat::SolvAttr::name);
+  q1.setMatchRegex();
+
+  BOOST_CHECK(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count == 8);
+
+  cout << "****006.3***"  << endl;
+
+  // invalid regex
+  PoolQuery q2;
+  q2.addString("zypp\\");
+  q2.setMatchRegex();
+  BOOST_CHECK_THROW(q2.begin(), Exception);
+}
+
+
+// match whole words
+BOOST_AUTO_TEST_CASE(pool_query_007)
+{
+  cout << "****007***"  << endl;
+
+  PoolQuery q;
+  q.addString("zypp");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchWord();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 6);
+}
+
+// match by installed status (basically by system vs. repo)
+BOOST_AUTO_TEST_CASE(pool_query_050)
+{
+  cout << "****050****"  << endl;
+  PoolQuery q;
+  q.addString("yasm");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchExact();
+  q.setInstalledOnly();
+
+  BOOST_CHECK_EQUAL(std::for_each(q.begin(), q.end(), PrintAndCount())._count, 4);
+
+  cout << endl;
+
+  PoolQuery q1;
+  q1.addString("zypper");
+  q1.addAttribute(sat::SolvAttr::name);
+  q1.setMatchExact();
+  q1.setUninstalledOnly();
+  BOOST_CHECK_EQUAL(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count, 5);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//  1xx multiple attribute queries
+/////////////////////////////////////////////////////////////////////////////
+
+
+BOOST_AUTO_TEST_CASE(pool_query_100)
+{
+  cout << "****100****"  << endl;
+  PoolQuery q;
+  /* This string is found sometimes only in only in summary (e.g. pgcalc)
+     and sometimes only in description (e.g. bc, lftp). We don't have
+     any package with 'revers' only in package name, but let's ignore this.
+     I didn't find a string with the same characteristics giving fewer matches
+     :-/ */
+  q.addString("revers");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addAttribute(sat::SolvAttr::summary);
+  q.addAttribute(sat::SolvAttr::description);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 5);
+
+  cout << endl;
+
+  {
+    PoolQuery q1;
+    q1.addAttribute(sat::SolvAttr::name, "zypper");
+    BOOST_CHECK_EQUAL(q1.size(),5);
+
+    PoolQuery q2;
+    q2.addAttribute(sat::SolvAttr::summary,"samba");
+    BOOST_CHECK_EQUAL(q2.size(),13);
+
+    // now summary and name in one go:
+    q1.addAttribute(sat::SolvAttr::summary,"samba");
+    BOOST_CHECK_EQUAL(q1.size(),18);
+  }
+}
+
+
+// multi attr (same value) substring matching (case sensitive and insensitive)
+BOOST_AUTO_TEST_CASE(pool_query_101)
+{
+  cout << "****101****"  << endl;
+
+  PoolQuery q;
+  q.addString("RELAX");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addAttribute(sat::SolvAttr::summary);
+  q.addAttribute(sat::SolvAttr::description);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 7);
+
+  cout << endl;
+
+  PoolQuery q2;
+  q2.addString("RELAX");
+  q2.addAttribute(sat::SolvAttr::name);
+  q2.addAttribute(sat::SolvAttr::summary);
+  q2.addAttribute(sat::SolvAttr::description);
+  q2.setCaseSensitive();
+
+  BOOST_CHECK(std::for_each(q2.begin(), q2.end(), PrintAndCount())._count == 4);
+}
+
+
+// multi attr (same value) glob matching (case sensitive and insensitive)
+BOOST_AUTO_TEST_CASE(pool_query_102)
+{
+  cout << "****102****"  << endl;
+  PoolQuery q;
+  q.addString("pack*");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addAttribute(sat::SolvAttr::summary);
+  q.setMatchGlob();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 23);
+}
+
+
+// multi attr (same value via addAttribute())
+BOOST_AUTO_TEST_CASE(pool_query_103)
+{
+  cout << "****103.1****"  << endl;
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "rest");
+  q.addAttribute(sat::SolvAttr::summary, "rest");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 14);
+
+  cout << "****103.2****"  << endl;
+
+  PoolQuery q1;
+  q1.addString("rest");
+  q1.addAttribute(sat::SolvAttr::name);
+  q1.addAttribute(sat::SolvAttr::summary);
+
+  BOOST_CHECK(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count == 14);
+//  BOOST_CHECK(q1.size() == 42);
+
+  cout << endl;
+}
+
+// multiple attributes, different search strings (one string per attrbute)
+BOOST_AUTO_TEST_CASE(pool_query_104)
+{
+  cout << "****104****"  << endl;
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "zypper");
+  q.addAttribute(sat::SolvAttr::summary, "package management");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 8);
+}
+
+// multiple attributes, different search strings (one string per attrbute), regex matching
+BOOST_AUTO_TEST_CASE(pool_query_105)
+{
+  cout << "****105****"  << endl;
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "zy..er");
+  q.addAttribute(sat::SolvAttr::summary, "package management");
+  q.setMatchRegex();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 8);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//  3xx repo filter queries (addRepo(alias_str))
+/////////////////////////////////////////////////////////////////////////////
+
+// default query + one attribute(one string) + one repo
+BOOST_AUTO_TEST_CASE(pool_query_300)
+{
+  cout << "****300****"  << endl;
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "zypper");
+  q.addRepo("zyppsvn");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 4);
+}
+
+// default query + one repo
+BOOST_AUTO_TEST_CASE(pool_query_301)
+{
+  cout << "****301****"  << endl;
+  PoolQuery q;
+  q.addRepo("zyppsvn");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 42);
+}
+
+// multiple repos + one attribute
+BOOST_AUTO_TEST_CASE(pool_query_302)
+{
+  cout << "****302****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addRepo("opensuse");
+  q.addRepo("zyppsvn");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 5);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//  4xx kind queries (addKind(ResKind))
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(pool_query_400)
+{
+  cout << "****400****"  << endl;
+  PoolQuery q;
+  q.addString("lamp_server");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addKind(ResKind::pattern);
+  q.setMatchExact();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 1);
+}
+
+// should find packages and patterns
+BOOST_AUTO_TEST_CASE(pool_query_401)
+{
+  cout << "****401****"  << endl;
+  PoolQuery q;
+  q.addString("mail*");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchGlob();
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 4);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//  5xx multiple string/attribute queries
+/////////////////////////////////////////////////////////////////////////////
+
+// multiple strings for one attribute
+BOOST_AUTO_TEST_CASE(pool_query_500)
+{
+  cout << "****500.1****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  q.addString("yast2-packager");
+  q.addAttribute(sat::SolvAttr::name);
+  q.setMatchExact();
+  // creates: ^(apt|zypper)$
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 6);
+
+  cout << "****500.2****"  << endl;
+  q.addString("*bzypp");
+  q.setMatchGlob();
+  // creates: ^(.*zy.p|yast.*package.*|.*bzypp)$
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 11);
+
+  cout << "****500.3****"  << endl;
+  PoolQuery q1;
+  q1.addString("^libsm[a-z]*[0-9]$");
+  q1.addAttribute(sat::SolvAttr::name, "bzypp$");
+  q1.addKind(ResKind::package);
+  q1.setMatchRegex();
+  // creates: (^libsm[a-z]*[0-9]$|bzypp$)
+  BOOST_CHECK(std::for_each(q1.begin(), q1.end(), PrintAndCount())._count == 5);
+
+  cout << "****500.4****"  << endl;
+  PoolQuery q2;
+  q2.addString("Thunder");
+  q2.addAttribute(sat::SolvAttr::name, "sun");
+  q2.addKind(ResKind::package);
+  q2.addRepo("opensuse");
+  q2.setCaseSensitive();
+  // creates: (sun|Thunder)
+  BOOST_CHECK(std::for_each(q2.begin(), q2.end(), PrintAndCount())._count == 3);
+
+  cout << "****500.5****"  << endl;
+  PoolQuery q3;
+  q3.addString("audio");
+  q3.addAttribute(sat::SolvAttr::name, "zip");
+  q3.addKind(ResKind::package);
+  q3.addRepo("opensuse");
+  q3.setMatchWord();
+  // creates: \b(zip|audio)\b
+  BOOST_CHECK(std::for_each(q3.begin(), q3.end(), PrintAndCount())._count == 3);
+}
+
+// multiple strings, multiple attributes, same strings
+BOOST_AUTO_TEST_CASE(pool_query_501)
+{
+  cout << "****501****"  << endl;
+  PoolQuery q;
+  q.addString("Thunder");
+  q.addString("storm");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addAttribute(sat::SolvAttr::description);
+  q.addKind(ResKind::package);
+  q.addRepo("opensuse");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 14);
+}
+
+// multiple strings, multiple attributes, same strings
+BOOST_AUTO_TEST_CASE(pool_query_502)
+{
+  cout << "****502****"  << endl;
+  PoolQuery q;
+  q.addString("weather");
+  q.addAttribute(sat::SolvAttr::name, "thunder");
+  q.addAttribute(sat::SolvAttr::description, "storm");
+  q.addKind(ResKind::package);
+  q.addRepo("opensuse");
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 13);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//  6xx queries with edition
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(pool_query_X)
+{
+  cout << "****600.1****"  << endl;
+  PoolQuery q;
+  q.addAttribute(sat::SolvAttr::name, "zypper");
+  q.setMatchExact();
+  q.setEdition(Edition("0.12.5"), Rel::GT);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 4);
+
+  cout << "****600.2****"  << endl;
+  q.setEdition(Edition("0.12.5"), Rel::LT);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 0);
+
+  cout << "****600.3****"  << endl;
+  q.setEdition(Edition("0.12.5"), Rel::LE);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 1);
+
+  cout << "****600.4****"  << endl;
+  q.setEdition(Edition("0.12.5-5"), Rel::LT);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 1);
+}
+
+//! \todo FIXME this segfaults currently - one addString() + (version or kind or installed status condition)
+/*
+BOOST_AUTO_TEST_CASE(pool_query_FIXME)
+{
+  cout << "****FIXME****"  << endl;
+  PoolQuery q;
+  q.addString("zypper");
+  q.setEdition(Edition("0.10.3-4"), Rel::GE);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 2);
+}
+*/
+
+/*
+BOOST_AUTO_TEST_CASE(pool_query_X)
+{
+  cout << "****X****"  << endl;
+  PoolQuery q;
+  q.addString("pack*");
+  q.addAttribute(sat::SolvAttr::name);
+
+  BOOST_CHECK(std::for_each(q.begin(), q.end(), PrintAndCount())._count == 28);
+}
+*/
+
+
+BOOST_AUTO_TEST_CASE(pool_query_recovery)
+{
+  Pathname testfile(TESTS_SRC_DIR);
+    testfile += "/zypp/data/PoolQuery/savedqueries";
+  cout << "****recovery****"  << endl;
+
+  std::vector<PoolQuery> queries;
+  std::insert_iterator<std::vector<PoolQuery> > ii( queries,queries.begin());
+  readPoolQueriesFromFile(testfile,ii);
+  BOOST_REQUIRE_MESSAGE(queries.size() == 2, "Bad count of read queries.");
+
+  BOOST_CHECK_EQUAL(queries[0].size(), 8);
+
+  PoolQuery q;
+  q.addString("ma*");
+  q.addRepo("opensuse");
+  q.addKind(ResKind::patch);
+  q.setMatchRegex();
+  q.setRequireAll();
+  q.setCaseSensitive();
+  q.setUninstalledOnly();
+  q.setEdition(Edition("0.8.3"),Rel::NE);
+  BOOST_CHECK(q == queries[1]);
+}
+
+BOOST_AUTO_TEST_CASE(pool_query_serialize)
+{
+  PoolQuery q;
+  q.addString("ma");
+  q.addAttribute(sat::SolvAttr::name);
+  q.addRepo("factory-nonoss");
+  q.addRepo("zypp_svn");
+
+  PoolQuery q2;
+  q2.addAttribute(sat::SolvAttr::name,"ma");
+  q2.addRepo("factory-nonoss");
+  q2.addRepo("zypp_svn");
+
+
+  //  Pathname testfile(TESTS_SRC_DIR);
+  //  testfile += "/zypp/data/PoolQuery/testqueries";
+  filesystem::TmpFile testfile;
+  cout << "****serialize****"  << endl;
+  std::vector<PoolQuery> queries;
+  queries.push_back(q);
+  queries.push_back(q2);
+  writePoolQueriesToFile(testfile,queries.begin(),queries.end());
+  BOOST_REQUIRE_MESSAGE(queries.size()==2,"Bad count of added queries.");
+
+  std::insert_iterator<std::vector<PoolQuery> > ii( queries,queries.end());
+  readPoolQueriesFromFile(testfile,ii);
+  BOOST_REQUIRE_MESSAGE(queries.size()==4,"Bad count of written/readed queries.");
+  BOOST_CHECK(queries[2] == queries[0]);
+  BOOST_CHECK(queries[3] == queries[1]);
+}
+
+// test matching
+BOOST_AUTO_TEST_CASE(pool_query_equal)
+{
+  cout << "****equal****"  << endl;
+  std::vector<PoolQuery> v;
+  {
+    PoolQuery q;
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addAttribute( sat::SolvAttr::name, "zypper" );
+    q.setMatchExact();
+    q.setCaseSensitive(true);
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addAttribute( sat::SolvAttr::name, "libzypp" );  // different
+    q.setMatchExact();
+    q.setCaseSensitive(true);
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addAttribute( sat::SolvAttr::vendor, "zypper" ); // different
+    q.setMatchExact();
+    q.setCaseSensitive(true);
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addAttribute( sat::SolvAttr::name, "zypper" );
+    q.setMatchExact();
+    q.setCaseSensitive(false); // different
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addAttribute( sat::SolvAttr::name, "zypper" );
+    q.setMatchSubstring();     // different
+    q.setCaseSensitive(true);
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addDependency( sat::SolvAttr::provides, "zypper" );
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addDependency( sat::SolvAttr::provides, "zypper", Rel::GT, Edition("1.0")  );
+    v.push_back( q );
+  }
+  {
+    PoolQuery q;
+    q.addDependency( sat::SolvAttr::provides, "zypper", Rel::GT, Edition("2.0")  );
+    v.push_back( q );
+  }
+
+  for_( li, 0U, v.size() )
+  {
+    for_( ri, 0U, v.size() )
+    {
+      COUT << li << " <> " << ri << endl;
+      bool equal( v[li] == v[ri] );
+      bool nequal( v[li] != v[ri] );
+      BOOST_CHECK_EQUAL( equal, li==ri );
+      BOOST_CHECK_EQUAL( equal, !nequal );
+    }
+  }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//  Dependency Query
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(addDependency)
+{
+  {
+    cout << "****addDependency1****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    q.addString( "libzypp" );
+    q.addDependency( sat::SolvAttr::provides, "FOO" ); // ! finds 'perl(CPAN::InfoObj)' 'foO'
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 13 );
+  }
+  {
+    cout << "****addDependency2****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    q.addString( "libzypp" );
+    q.addDependency( sat::SolvAttr::provides, "FOO", Rel::GT, Edition("5.0") );
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 7 );
+  }
+  {
+    cout << "****addDependency2a****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    q.addDependency( sat::SolvAttr::provides, "libzypp", Rel::GT, Edition("5.0") );
+    q.addAttribute( sat::SolvAttr::arch, Arch_i586.asString() ); // OR with arch i585
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 66 );
+  }
+  {
+    cout << "****addDependency2b****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    // libzypp provides yast2-packagemanager...
+    q.addDependency( sat::SolvAttr::provides, "yast2-packagemanager", Rel::GT, Edition("5.0"), Arch_i586 ); // AND with arch i585
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 2 );
+  }
+  {
+    cout << "****addDependency2c****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    // but no package named yast2-packagemanager
+    q.addDependency( sat::SolvAttr::name, "yast2-packagemanager", Rel::GT, Edition("5.0"), Arch_i586 ); // AND with arch i585
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 0 );
+  }
+  {
+    cout << "****addDependency2d****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    // libzypp provides yast2-packagemanager...
+    q.addDependency( sat::SolvAttr::provides, "yast2-packagemanager", Arch_i586 ); // AND with arch i585
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 2 );
+  }
+  {
+    cout << "****addDependency2e****"  << endl;
+    PoolQuery q;
+    q.setCaseSensitive( false );
+    q.setMatchSubstring();
+    // but no package named yast2-packagemanager
+    q.addDependency( sat::SolvAttr::name, "yast2-packagemanager", Arch_i586 ); // AND with arch i585
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 0 );
+  }
+
+  {
+    cout << "****addDependency3****"  << endl;
+    PoolQuery q;
+    // includes wine
+    q.addDependency( sat::SolvAttr::provides, "kernel" );
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 12 );
+  }
+  {
+    cout << "****addDependency4****"  << endl;
+    PoolQuery q;
+    // no wine
+    q.addDependency( sat::SolvAttr::name, "kernel" );
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 11 );
+  }
+  {
+    cout << "****addDependency5****"  << endl;
+    PoolQuery q;
+    // Capability always matches exact
+    q.addDependency( sat::SolvAttr::provides, Capability("kernel") );
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 2 );
+  }
+  {
+    cout << "****addDependency6****"  << endl;
+    PoolQuery q;
+    // non dependecy + Capability matches solvable name!
+    q.addDependency( sat::SolvAttr::summary, Capability("kernel") );
+    std::for_each(q.begin(), q.end(), PrintAndCount());
+    //dumpQ( std::cout, q );
+    BOOST_CHECK_EQUAL( q.size(), 0 ); // non dependecy
+  }
+}
+
+
diff --git a/tests/zypp/ProgressData_test.cc b/tests/zypp/ProgressData_test.cc
new file mode 100644 (file)
index 0000000..02b06fd
--- /dev/null
@@ -0,0 +1,60 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/ProgressData.h"
+
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+
+BOOST_AUTO_TEST_CASE(progressdata_test)
+{
+  {
+    ProgressData progress(100);
+    CombinedProgressData sub1rcv(progress, 80);
+    
+    ProgressData sub1progress(100);
+    sub1progress.sendTo(sub1rcv);
+    
+    // task 1 goes to 50%
+    sub1progress.set(50);
+    // which is 50% of 80% in task 1
+    BOOST_CHECK_EQUAL( progress.val(), 40 );
+  }
+  
+  {
+    ProgressData progress(40000);
+    CombinedProgressData sub2rcv(progress, 10000);
+    
+    ProgressData sub2progress(500);
+    sub2progress.sendTo(sub2rcv);
+    sub2progress.set(250);
+    
+    // which is 50% of 80% in task 1
+    BOOST_CHECK_EQUAL( progress.val(), 5000 );
+  }
+  
+  {
+    ProgressData progress(20000,60000);
+    CombinedProgressData sub2rcv(progress, 10000);
+    
+    ProgressData sub2progress(500);
+    sub2progress.sendTo(sub2rcv);
+    sub2progress.set(250);
+    
+    // which is 50% of 80% in task 1
+    BOOST_CHECK_EQUAL( progress.val(), 25000 );
+  }
+  
+}
+   
+
diff --git a/tests/zypp/PtrTypes_test.cc b/tests/zypp/PtrTypes_test.cc
new file mode 100644 (file)
index 0000000..a2d9427
--- /dev/null
@@ -0,0 +1,138 @@
+#include <iostream>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include <zypp/base/PtrTypes.h>
+#include <zypp/base/ReferenceCounted.h>
+#include <zypp/base/ProvideNumericId.h>
+
+#define BOOST_TEST_MODULE PtrTypes
+
+using std::endl;
+using namespace zypp;
+using namespace zypp::base;
+
+
+#define TRACE_TAG DBG << this->numericId() << " " << __PRETTY_FUNCTION__ << endl
+
+/** Logs Ctor, CopyCtor, Assign and Dtor. */
+template<class _Trace>
+  struct Trace : public ProvideNumericId<_Trace,unsigned>
+  {
+    Trace()                            { TRACE_TAG; }
+    Trace( const Trace & )             { TRACE_TAG; }
+    ~Trace()                           { TRACE_TAG; }
+    Trace & operator=( const Trace & ) { TRACE_TAG; return *this; }
+  };
+
+/** Data class for shared_ptr */
+struct NonIntrusive : private Trace<NonIntrusive>
+{
+  Trace<NonIntrusive>::numericId;
+};
+
+/** Data class for intrusive_ptr */
+struct Intrusive : public ReferenceCounted,
+                   private Trace<Intrusive>
+{
+  Trace<Intrusive>::numericId;
+};
+
+namespace zypp
+{
+  template<>
+    inline NonIntrusive * rwcowClone<NonIntrusive>( const NonIntrusive * rhs )
+    { return new NonIntrusive( *rhs ); }
+
+  template<>
+    inline Intrusive * rwcowClone<Intrusive>( const Intrusive * rhs )
+    { return new Intrusive( *rhs ); }
+
+}
+/******************************************************************
+**
+*/
+#define T_NULL       assert( !ptr )
+#define T_NOT_NULL   assert( ptr )
+#define T_UNIQUE     assert( ptr.unique() ); assert( ptr.use_count() < 2 )
+#define T_NOT_UNIQUE assert( !ptr.unique() ); assert( ptr.use_count() >= 2 )
+#define T_EQ(a,b)   assert( a == b )
+#define T_NE(a,b)   assert( a != b )
+
+template<class _RW>
+  void test()
+  {
+    MIL << __PRETTY_FUNCTION__ << std::endl;
+    // typedefs that should be provided:
+    typedef typename _RW::_Ptr               _Ptr;
+    typedef typename _RW::_constPtr          _constPtr;
+    typedef typename _Ptr::element_type      _Ptr_element_type;
+    typedef typename _constPtr::element_type _constPtr_element_type;
+    // initial NULL
+    _RW ptr;
+    T_NULL;
+    T_UNIQUE;
+    T_EQ(ptr,ptr);
+    // assign
+    ptr = _RW( new _Ptr_element_type );
+    T_NOT_NULL;
+    T_UNIQUE;
+    T_EQ(ptr,ptr);
+    {
+      // share
+      _RW ptr2( ptr );
+      T_NOT_NULL;
+      T_NOT_UNIQUE;
+      T_EQ(ptr,ptr2);
+      // unshare
+      ptr2.reset();
+      T_NOT_NULL;
+      T_UNIQUE;
+      T_NE(ptr,ptr2);
+      // different impl
+      ptr2.reset( new _Ptr_element_type );
+      T_NE(ptr,ptr2);
+   }
+    // assign
+    ptr.reset( 0 );
+    T_NULL;
+    T_UNIQUE;
+  }
+
+template<class _RW>
+  void cowt()
+  {
+    test<_RW>();
+    MIL << __PRETTY_FUNCTION__ << std::endl;
+    typedef typename _RW::_Ptr::element_type _Ptr_element_type;
+    // create
+    _RW ptr( new _Ptr_element_type );
+    unsigned long ptrid = ptr->numericId();
+    // share
+    _RW ptr2( ptr );
+    // clone aon access
+    unsigned long ptrid2 = ptr2->numericId();
+    assert( ptrid != ptrid2 );
+  }
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+BOOST_AUTO_TEST_CASE(basic_test)
+{
+  MIL << "===[START]=====" << endl;
+  test<RW_pointer<NonIntrusive,          rw_pointer::Shared<NonIntrusive> > >();
+  test<RW_pointer<const NonIntrusive,    rw_pointer::Shared<NonIntrusive> > >();
+  test<RW_pointer<Intrusive,             rw_pointer::Intrusive<Intrusive> > >();
+  test<RW_pointer<const Intrusive,       rw_pointer::Intrusive<Intrusive> > >();
+
+  cowt<RWCOW_pointer<NonIntrusive,       rw_pointer::Shared<NonIntrusive> > >();
+  cowt<RWCOW_pointer<const NonIntrusive, rw_pointer::Shared<NonIntrusive> > >();
+  cowt<RWCOW_pointer<Intrusive,          rw_pointer::Intrusive<Intrusive> > >();
+  cowt<RWCOW_pointer<const Intrusive,    rw_pointer::Intrusive<Intrusive> > >();
+
+  MIL << "===[DONE]=====" << endl;
+}
diff --git a/tests/zypp/PublicKey_test.cc b/tests/zypp/PublicKey_test.cc
new file mode 100644 (file)
index 0000000..f2abbca
--- /dev/null
@@ -0,0 +1,265 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/PublicKey.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Date.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+
+/*
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.2 (GNU/Linux)
+
+mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3yEKeueNWc+z/0Kvff
+4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0PFPboMvKx0FXl/A0d
+M+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xocQSVCFxcwvwCglVcO
+QliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/yE/vdvpN6lF0tmFrK
+XBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqITuZC4CWxJa9GynBE
+D3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBeGaPdNCcmfZ66rKUd
+G5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn4zLUOf6aeBAoV6NM
+CC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqajLfvkURHAeSsxXIoE
+myW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF+rQoU3VTRSBQYWNr
+YWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMRAgAiBQJA2AY+AhsD
+BQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAKypCfAJ9RuZ6ZSV7Q
+W4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+IRgQQEQIABgUCOnBe
+UgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAejACeOO1HIbActAev
+k5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9An5vfiUUE1VQnt+T/
+EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2OgSEISZxbzdXGnqIl
+cT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzneOA9gTPSr+/fT3WeV
+nwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yryk23pQUPAgJENDEqe
+U6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN8C1yPqZd5CvvznYv
+B6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJt9IXSzyohEJB6XG5
++D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ8UUTmIT2iQf07tRU
+KJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/gO7fJEpD2hd2f+pMn
+OWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZNZfDqnFDP6L12w3z
+3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271hhQBeRmmoGEKO2JRe
+lGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5ucTXstZy7vYjL6vTP4
+l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g+9V3ORzw4LvO1pwR
+YJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbSTM5LpC/U6sheLP+l0
+bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlGG6pqUN8QxGJYQnon
+l0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7mfoGx6INQjf3HGQp
+MXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMWpO+M9bHp4d+puY0L
+1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zFbpeqPQUsDv9V7CAJ
+1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspnpgCfRbYwxT3iq+9l
+/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuO
+=pJli
+-----END PGP PUBLIC KEY BLOCK-----
+*/
+
+/**
+ * Array version of the above key
+ */
+char susekey[]=
+{
+        0x2d,0x2d,0x2d,0x2d,0x2d,0x42,0x45,0x47,0x49,0x4e,0x20,0x50,0x47,
+        0x50,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,0x4b,0x45,0x59,0x20,
+        0x42,0x4c,0x4f,0x43,0x4b,0x2d,0x2d,0x2d,0x2d,0x2d,0x0a,0x56,0x65,
+        0x72,0x73,0x69,0x6f,0x6e,0x3a,0x20,0x47,0x6e,0x75,0x50,0x47,0x20,
+        0x76,0x31,0x2e,0x34,0x2e,0x32,0x20,0x28,0x47,0x4e,0x55,0x2f,0x4c,
+        0x69,0x6e,0x75,0x78,0x29,0x0a,0x0a,0x6d,0x51,0x47,0x69,0x42,0x44,
+        0x6e,0x75,0x39,0x49,0x45,0x52,0x42,0x41,0x43,0x54,0x38,0x59,0x33,
+        0x35,0x2b,0x32,0x76,0x76,0x34,0x4d,0x47,0x56,0x4b,0x69,0x4c,0x45,
+        0x4d,0x4f,0x6c,0x39,0x47,0x64,0x53,0x54,0x36,0x4d,0x43,0x6b,0x59,
+        0x53,0x33,0x79,0x45,0x4b,0x65,0x75,0x65,0x4e,0x57,0x63,0x2b,0x7a,
+        0x2f,0x30,0x4b,0x76,0x66,0x66,0x0a,0x34,0x4a,0x63,0x74,0x42,0x73,
+        0x67,0x73,0x34,0x37,0x74,0x6a,0x6d,0x69,0x49,0x39,0x73,0x6c,0x30,
+        0x65,0x48,0x6a,0x6d,0x33,0x67,0x54,0x52,0x38,0x72,0x49,0x74,0x58,
+        0x4d,0x4e,0x36,0x73,0x4a,0x45,0x55,0x48,0x57,0x7a,0x44,0x50,0x2b,
+        0x59,0x30,0x50,0x46,0x50,0x62,0x6f,0x4d,0x76,0x4b,0x78,0x30,0x46,
+        0x58,0x6c,0x2f,0x41,0x30,0x64,0x0a,0x4d,0x2b,0x48,0x46,0x72,0x72,
+        0x75,0x43,0x67,0x42,0x6c,0x57,0x74,0x36,0x46,0x41,0x2b,0x6f,0x6b,
+        0x52,0x79,0x53,0x51,0x69,0x6c,0x69,0x75,0x49,0x35,0x70,0x68,0x77,
+        0x71,0x6b,0x58,0x65,0x66,0x6c,0x39,0x41,0x68,0x6b,0x77,0x52,0x38,
+        0x78,0x6f,0x63,0x51,0x53,0x56,0x43,0x46,0x78,0x63,0x77,0x76,0x77,
+        0x43,0x67,0x6c,0x56,0x63,0x4f,0x0a,0x51,0x6c,0x69,0x48,0x75,0x38,
+        0x6a,0x77,0x52,0x51,0x48,0x78,0x6c,0x52,0x45,0x30,0x74,0x6b,0x77,
+        0x51,0x51,0x49,0x30,0x44,0x2b,0x77,0x66,0x51,0x77,0x4b,0x64,0x76,
+        0x68,0x44,0x70,0x6c,0x78,0x48,0x4a,0x35,0x6e,0x66,0x37,0x55,0x38,
+        0x63,0x2f,0x79,0x45,0x2f,0x76,0x64,0x76,0x70,0x4e,0x36,0x6c,0x46,
+        0x30,0x74,0x6d,0x46,0x72,0x4b,0x0a,0x58,0x42,0x55,0x58,0x2b,0x4b,
+        0x37,0x75,0x34,0x69,0x66,0x72,0x5a,0x6c,0x51,0x76,0x6a,0x2f,0x38,
+        0x31,0x4d,0x34,0x49,0x4e,0x6a,0x74,0x58,0x72,0x65,0x71,0x44,0x69,
+        0x4a,0x74,0x72,0x39,0x39,0x52,0x73,0x36,0x78,0x61,0x30,0x53,0x63,
+        0x5a,0x71,0x49,0x54,0x75,0x5a,0x43,0x34,0x43,0x57,0x78,0x4a,0x61,
+        0x39,0x47,0x79,0x6e,0x42,0x45,0x0a,0x44,0x33,0x2b,0x44,0x32,0x74,
+        0x31,0x56,0x2f,0x66,0x38,0x6c,0x30,0x73,0x6d,0x73,0x75,0x59,0x6f,
+        0x46,0x4f,0x46,0x37,0x49,0x62,0x34,0x39,0x49,0x6b,0x54,0x64,0x62,
+        0x74,0x77,0x41,0x54,0x68,0x6c,0x5a,0x70,0x38,0x62,0x45,0x68,0x45,
+        0x4c,0x42,0x65,0x47,0x61,0x50,0x64,0x4e,0x43,0x63,0x6d,0x66,0x5a,
+        0x36,0x36,0x72,0x4b,0x55,0x64,0x0a,0x47,0x35,0x73,0x52,0x41,0x2f,
+        0x39,0x6f,0x76,0x6e,0x63,0x31,0x6b,0x72,0x53,0x51,0x46,0x32,0x2b,
+        0x73,0x71,0x42,0x39,0x2f,0x6f,0x37,0x77,0x35,0x2f,0x71,0x32,0x71,
+        0x69,0x79,0x7a,0x77,0x4f,0x53,0x54,0x6e,0x6b,0x6a,0x74,0x42,0x55,
+        0x56,0x4b,0x6e,0x34,0x7a,0x4c,0x55,0x4f,0x66,0x36,0x61,0x65,0x42,
+        0x41,0x6f,0x56,0x36,0x4e,0x4d,0x0a,0x43,0x43,0x33,0x4b,0x6a,0x39,
+        0x61,0x5a,0x48,0x66,0x41,0x2b,0x4e,0x44,0x30,0x65,0x68,0x50,0x61,
+        0x56,0x47,0x4a,0x67,0x6a,0x61,0x56,0x4e,0x46,0x68,0x50,0x69,0x34,
+        0x78,0x30,0x65,0x37,0x42,0x55,0x4c,0x64,0x76,0x67,0x4f,0x6f,0x41,
+        0x71,0x61,0x6a,0x4c,0x66,0x76,0x6b,0x55,0x52,0x48,0x41,0x65,0x53,
+        0x73,0x78,0x58,0x49,0x6f,0x45,0x0a,0x6d,0x79,0x57,0x2f,0x78,0x43,
+        0x31,0x73,0x42,0x62,0x44,0x6b,0x44,0x55,0x49,0x42,0x53,0x78,0x35,
+        0x6f,0x65,0x6a,0x37,0x33,0x58,0x43,0x5a,0x67,0x6e,0x6a,0x2f,0x69,
+        0x6e,0x70,0x68,0x52,0x71,0x47,0x70,0x73,0x62,0x2b,0x31,0x6e,0x4b,
+        0x46,0x76,0x46,0x2b,0x72,0x51,0x6f,0x55,0x33,0x56,0x54,0x52,0x53,
+        0x42,0x51,0x59,0x57,0x4e,0x72,0x0a,0x59,0x57,0x64,0x6c,0x49,0x46,
+        0x4e,0x70,0x5a,0x32,0x35,0x70,0x62,0x6d,0x63,0x67,0x53,0x32,0x56,
+        0x35,0x49,0x44,0x78,0x69,0x64,0x57,0x6c,0x73,0x5a,0x45,0x42,0x7a,
+        0x64,0x58,0x4e,0x6c,0x4c,0x6d,0x52,0x6c,0x50,0x6f,0x68,0x69,0x42,
+        0x42,0x4d,0x52,0x41,0x67,0x41,0x69,0x42,0x51,0x4a,0x41,0x32,0x41,
+        0x59,0x2b,0x41,0x68,0x73,0x44,0x0a,0x42,0x51,0x6b,0x4f,0x62,0x64,
+        0x2b,0x39,0x42,0x41,0x73,0x48,0x41,0x77,0x49,0x44,0x46,0x51,0x49,
+        0x44,0x41,0x78,0x59,0x43,0x41,0x51,0x49,0x65,0x41,0x51,0x49,0x58,
+        0x67,0x41,0x41,0x4b,0x43,0x52,0x43,0x6f,0x54,0x74,0x72,0x6f,0x6e,
+        0x49,0x41,0x4b,0x79,0x70,0x43,0x66,0x41,0x4a,0x39,0x52,0x75,0x5a,
+        0x36,0x5a,0x53,0x56,0x37,0x51,0x0a,0x57,0x34,0x70,0x54,0x67,0x54,
+        0x49,0x78,0x51,0x2b,0x41,0x42,0x50,0x70,0x30,0x73,0x49,0x77,0x43,
+        0x66,0x66,0x47,0x39,0x62,0x43,0x4e,0x6e,0x72,0x45,0x54,0x50,0x6c,
+        0x67,0x4f,0x6e,0x2b,0x64,0x47,0x45,0x6b,0x41,0x57,0x65,0x67,0x4b,
+        0x4c,0x2b,0x49,0x52,0x67,0x51,0x51,0x45,0x51,0x49,0x41,0x42,0x67,
+        0x55,0x43,0x4f,0x6e,0x42,0x65,0x0a,0x55,0x67,0x41,0x4b,0x43,0x52,
+        0x43,0x65,0x51,0x4f,0x4d,0x51,0x41,0x41,0x71,0x72,0x70,0x4e,0x7a,
+        0x4f,0x41,0x4b,0x43,0x4c,0x35,0x31,0x32,0x46,0x5a,0x76,0x76,0x34,
+        0x56,0x5a,0x78,0x39,0x34,0x54,0x70,0x62,0x41,0x39,0x6c,0x78,0x79,
+        0x6f,0x41,0x65,0x6a,0x41,0x43,0x65,0x4f,0x4f,0x31,0x48,0x49,0x62,
+        0x41,0x63,0x74,0x41,0x65,0x76,0x0a,0x6b,0x35,0x4d,0x55,0x42,0x68,
+        0x4e,0x65,0x4c,0x5a,0x61,0x2f,0x71,0x4d,0x32,0x4a,0x41,0x52,0x55,
+        0x44,0x42,0x52,0x41,0x36,0x63,0x47,0x42,0x76,0x64,0x37,0x4c,0x6d,
+        0x41,0x44,0x30,0x6c,0x30,0x39,0x6b,0x42,0x41,0x54,0x57,0x6e,0x42,
+        0x2f,0x39,0x41,0x6e,0x35,0x76,0x66,0x69,0x55,0x55,0x45,0x31,0x56,
+        0x51,0x6e,0x74,0x2b,0x54,0x2f,0x0a,0x45,0x59,0x6b,0x6c,0x45,0x53,
+        0x33,0x74,0x58,0x58,0x61,0x4a,0x4a,0x70,0x39,0x70,0x48,0x4d,0x61,
+        0x34,0x66,0x7a,0x46,0x61,0x38,0x6a,0x50,0x56,0x74,0x76,0x35,0x55,
+        0x42,0x48,0x47,0x65,0x65,0x33,0x58,0x6f,0x55,0x4e,0x44,0x56,0x77,
+        0x4d,0x32,0x4f,0x67,0x53,0x45,0x49,0x53,0x5a,0x78,0x62,0x7a,0x64,
+        0x58,0x47,0x6e,0x71,0x49,0x6c,0x0a,0x63,0x54,0x30,0x38,0x54,0x7a,
+        0x42,0x55,0x44,0x39,0x69,0x35,0x37,0x39,0x75,0x69,0x66,0x6b,0x6c,
+        0x4c,0x73,0x6e,0x72,0x33,0x35,0x53,0x4a,0x44,0x5a,0x36,0x72,0x61,
+        0x6d,0x35,0x31,0x2f,0x43,0x57,0x4f,0x6e,0x6e,0x61,0x56,0x68,0x55,
+        0x7a,0x6e,0x65,0x4f,0x41,0x39,0x67,0x54,0x50,0x53,0x72,0x2b,0x2f,
+        0x66,0x54,0x33,0x57,0x65,0x56,0x0a,0x6e,0x77,0x4a,0x69,0x51,0x43,
+        0x51,0x33,0x30,0x6b,0x4e,0x4c,0x57,0x56,0x58,0x57,0x41,0x54,0x4d,
+        0x6e,0x73,0x6e,0x54,0x34,0x38,0x36,0x65,0x41,0x4f,0x6c,0x54,0x36,
+        0x55,0x4e,0x42,0x50,0x59,0x51,0x4c,0x70,0x55,0x70,0x72,0x46,0x35,
+        0x59,0x72,0x79,0x6b,0x32,0x33,0x70,0x51,0x55,0x50,0x41,0x67,0x4a,
+        0x45,0x4e,0x44,0x45,0x71,0x65,0x0a,0x55,0x36,0x69,0x49,0x4f,0x39,
+        0x4f,0x74,0x31,0x5a,0x50,0x74,0x42,0x30,0x6c,0x6e,0x69,0x77,0x2b,
+        0x2f,0x78,0x43,0x69,0x31,0x33,0x44,0x33,0x36,0x30,0x6f,0x31,0x74,
+        0x5a,0x44,0x59,0x4f,0x70,0x30,0x68,0x48,0x48,0x4a,0x4e,0x33,0x44,
+        0x33,0x45,0x4e,0x38,0x43,0x31,0x79,0x50,0x71,0x5a,0x64,0x35,0x43,
+        0x76,0x76,0x7a,0x6e,0x59,0x76,0x0a,0x42,0x36,0x62,0x57,0x42,0x49,
+        0x70,0x57,0x63,0x52,0x67,0x64,0x6e,0x32,0x44,0x55,0x56,0x4d,0x6d,
+        0x70,0x55,0x36,0x36,0x31,0x6a,0x77,0x71,0x47,0x6c,0x52,0x7a,0x31,
+        0x46,0x38,0x34,0x4a,0x47,0x2f,0x78,0x65,0x34,0x6a,0x47,0x75,0x7a,
+        0x67,0x70,0x4a,0x74,0x39,0x49,0x58,0x53,0x7a,0x79,0x6f,0x68,0x45,
+        0x4a,0x42,0x36,0x58,0x47,0x35,0x0a,0x2b,0x44,0x30,0x42,0x75,0x51,
+        0x49,0x4e,0x42,0x44,0x6e,0x75,0x39,0x4a,0x49,0x51,0x43,0x41,0x43,
+        0x45,0x6b,0x64,0x42,0x4e,0x36,0x4d,0x78,0x66,0x35,0x57,0x76,0x71,
+        0x44,0x57,0x6b,0x63,0x4d,0x52,0x79,0x36,0x77,0x6e,0x72,0x64,0x39,
+        0x44,0x59,0x4a,0x38,0x55,0x55,0x54,0x6d,0x49,0x54,0x32,0x69,0x51,
+        0x66,0x30,0x37,0x74,0x52,0x55,0x0a,0x4b,0x4a,0x4a,0x39,0x76,0x30,
+        0x4a,0x58,0x66,0x78,0x32,0x5a,0x34,0x64,0x30,0x38,0x49,0x51,0x53,
+        0x4d,0x4e,0x52,0x61,0x71,0x34,0x56,0x67,0x53,0x65,0x2b,0x50,0x64,
+        0x59,0x67,0x49,0x79,0x30,0x66,0x62,0x6a,0x32,0x33,0x56,0x69,0x61,
+        0x35,0x2f,0x67,0x4f,0x37,0x66,0x4a,0x45,0x70,0x44,0x32,0x68,0x64,
+        0x32,0x66,0x2b,0x70,0x4d,0x6e,0x0a,0x4f,0x57,0x76,0x48,0x32,0x72,
+        0x4f,0x4f,0x49,0x62,0x65,0x59,0x66,0x75,0x68,0x7a,0x41,0x63,0x36,
+        0x42,0x51,0x6a,0x41,0x4b,0x74,0x6d,0x67,0x52,0x30,0x45,0x52,0x55,
+        0x54,0x61,0x66,0x54,0x4d,0x39,0x57,0x62,0x36,0x46,0x31,0x33,0x43,
+        0x4e,0x5a,0x5a,0x4e,0x5a,0x66,0x44,0x71,0x6e,0x46,0x44,0x50,0x36,
+        0x4c,0x31,0x32,0x77,0x33,0x7a,0x0a,0x33,0x46,0x37,0x46,0x46,0x58,
+        0x6b,0x7a,0x30,0x37,0x52,0x73,0x33,0x41,0x49,0x74,0x6f,0x31,0x5a,
+        0x66,0x59,0x5a,0x64,0x34,0x73,0x43,0x53,0x70,0x4d,0x72,0x2f,0x30,
+        0x53,0x35,0x6e,0x4c,0x72,0x48,0x62,0x49,0x76,0x47,0x4c,0x70,0x32,
+        0x37,0x31,0x68,0x68,0x51,0x42,0x65,0x52,0x6d,0x6d,0x6f,0x47,0x45,
+        0x4b,0x4f,0x32,0x4a,0x52,0x65,0x0a,0x6c,0x47,0x67,0x55,0x4a,0x32,
+        0x43,0x55,0x7a,0x4f,0x64,0x74,0x77,0x44,0x49,0x4b,0x54,0x30,0x4c,
+        0x62,0x43,0x70,0x76,0x61,0x50,0x38,0x50,0x56,0x6e,0x59,0x46,0x35,
+        0x49,0x46,0x6f,0x59,0x4a,0x49,0x57,0x52,0x48,0x71,0x6c,0x45,0x74,
+        0x35,0x75,0x63,0x54,0x58,0x73,0x74,0x5a,0x79,0x37,0x76,0x59,0x6a,
+        0x4c,0x36,0x76,0x54,0x50,0x34,0x0a,0x6c,0x35,0x78,0x73,0x2b,0x4c,
+        0x49,0x4f,0x6b,0x4e,0x6d,0x50,0x68,0x71,0x6d,0x66,0x73,0x67,0x4c,
+        0x7a,0x56,0x6f,0x30,0x55,0x61,0x4c,0x74,0x38,0x30,0x68,0x4f,0x77,
+        0x63,0x34,0x4e,0x76,0x44,0x43,0x4f,0x4c,0x41,0x41,0x4d,0x47,0x42,
+        0x2f,0x39,0x67,0x2b,0x39,0x56,0x33,0x4f,0x52,0x7a,0x77,0x34,0x4c,
+        0x76,0x4f,0x31,0x70,0x77,0x52,0x0a,0x59,0x4a,0x71,0x66,0x44,0x4b,
+        0x55,0x71,0x2f,0x45,0x4a,0x30,0x72,0x4e,0x4d,0x4d,0x44,0x34,0x4e,
+        0x38,0x52,0x4c,0x70,0x5a,0x52,0x68,0x4b,0x48,0x4b,0x4a,0x55,0x6d,
+        0x39,0x6e,0x4e,0x48,0x4c,0x62,0x6b,0x73,0x6e,0x6c,0x5a,0x77,0x72,
+        0x62,0x53,0x54,0x4d,0x35,0x4c,0x70,0x43,0x2f,0x55,0x36,0x73,0x68,
+        0x65,0x4c,0x50,0x2b,0x6c,0x30,0x0a,0x62,0x4c,0x56,0x6f,0x71,0x30,
+        0x6c,0x6d,0x73,0x43,0x63,0x55,0x53,0x79,0x68,0x2b,0x6d,0x59,0x36,
+        0x50,0x78,0x57,0x69,0x72,0x4c,0x49,0x57,0x43,0x6e,0x2f,0x49,0x41,
+        0x5a,0x41,0x47,0x6e,0x58,0x62,0x36,0x5a,0x64,0x36,0x54,0x74,0x49,
+        0x4a,0x6c,0x47,0x47,0x36,0x70,0x71,0x55,0x4e,0x38,0x51,0x78,0x47,
+        0x4a,0x59,0x51,0x6e,0x6f,0x6e,0x0a,0x6c,0x30,0x75,0x54,0x4a,0x4b,
+        0x48,0x4a,0x45,0x4e,0x62,0x49,0x39,0x73,0x57,0x48,0x51,0x64,0x63,
+        0x54,0x74,0x42,0x4d,0x63,0x33,0x34,0x67,0x6f,0x72,0x48,0x46,0x43,
+        0x6f,0x31,0x42,0x63,0x76,0x70,0x6e,0x63,0x31,0x4c,0x46,0x4c,0x72,
+        0x57,0x6e,0x37,0x6d,0x66,0x6f,0x47,0x78,0x36,0x49,0x4e,0x51,0x6a,
+        0x66,0x33,0x48,0x47,0x51,0x70,0x0a,0x4d,0x58,0x41,0x57,0x75,0x53,
+        0x42,0x51,0x68,0x7a,0x6b,0x61,0x7a,0x59,0x36,0x76,0x61,0x57,0x46,
+        0x70,0x61,0x38,0x62,0x42,0x4a,0x2b,0x67,0x4b,0x62,0x42,0x75,0x79,
+        0x53,0x57,0x7a,0x4e,0x6d,0x33,0x72,0x46,0x74,0x54,0x35,0x48,0x52,
+        0x4b,0x4d,0x57,0x70,0x4f,0x2b,0x4d,0x39,0x62,0x48,0x70,0x34,0x64,
+        0x2b,0x70,0x75,0x59,0x30,0x4c,0x0a,0x31,0x59,0x77,0x4e,0x31,0x4f,
+        0x4d,0x61,0x74,0x63,0x4d,0x4d,0x70,0x63,0x57,0x6e,0x5a,0x70,0x69,
+        0x57,0x69,0x52,0x38,0x33,0x6f,0x69,0x33,0x32,0x2b,0x78,0x74,0x57,
+        0x55,0x59,0x32,0x55,0x37,0x41,0x65,0x33,0x38,0x6d,0x4d,0x61,0x67,
+        0x38,0x7a,0x46,0x62,0x70,0x65,0x71,0x50,0x51,0x55,0x73,0x44,0x76,
+        0x39,0x56,0x37,0x43,0x41,0x4a,0x0a,0x31,0x64,0x62,0x72,0x69,0x45,
+        0x77,0x45,0x47,0x42,0x45,0x43,0x41,0x41,0x77,0x46,0x41,0x6b,0x44,
+        0x59,0x42,0x6e,0x6f,0x46,0x43,0x51,0x35,0x74,0x33,0x2b,0x67,0x41,
+        0x43,0x67,0x6b,0x51,0x71,0x45,0x37,0x61,0x36,0x4a,0x79,0x41,0x43,
+        0x73,0x70,0x6e,0x70,0x67,0x43,0x66,0x52,0x62,0x59,0x77,0x78,0x54,
+        0x33,0x69,0x71,0x2b,0x39,0x6c,0x0a,0x2f,0x50,0x67,0x4e,0x54,0x55,
+        0x4e,0x54,0x5a,0x4f,0x6c,0x6f,0x66,0x32,0x6f,0x41,0x6e,0x32,0x35,
+        0x79,0x30,0x65,0x47,0x69,0x30,0x33,0x37,0x31,0x6a,0x61,0x70,0x39,
+        0x6b,0x4f,0x56,0x36,0x75,0x71,0x37,0x31,0x73,0x55,0x75,0x4f,0x0a,
+        0x3d,0x70,0x4a,0x6c,0x69,0x0a,0x2d,0x2d,0x2d,0x2d,0x2d,0x45,0x4e,
+        0x44,0x20,0x50,0x47,0x50,0x20,0x50,0x55,0x42,0x4c,0x49,0x43,0x20,
+        0x4b,0x45,0x59,0x20,0x42,0x4c,0x4f,0x43,0x4b,0x2d,0x2d,0x2d,0x2d,
+        0x2d,0x0a,0x00,
+};
+
+size_t _susekey_size=2173;
+size_t *susekey_size=&_susekey_size;
+
+BOOST_AUTO_TEST_CASE(publickey_test)
+{
+  BOOST_CHECK_THROW( zypp::PublicKey("nonexistant"), Exception );
+  
+  filesystem::TmpFile file;
+  ofstream str(file.path().asString().c_str(),ofstream::out);
+
+  if (!str.good())
+    ZYPP_THROW(Exception("cant open file"));
+
+  str << susekey;
+  str.flush();
+  str.close();
+  zypp::PublicKey k2(file.path());
+  
+  BOOST_CHECK_EQUAL( k2.id(), "A84EDAE89C800ACA" );
+  BOOST_CHECK_EQUAL( k2.name(), "SuSE Package Signing Key <build@suse.de>" );
+  BOOST_CHECK_EQUAL( k2.fingerprint(), "79C179B2E1C820C1890F9994A84EDAE89C800ACA" );
+  BOOST_CHECK_EQUAL( k2.created(), zypp::Date(1087899198) );
+  BOOST_REQUIRE( k2.path() != Pathname() );
+  BOOST_REQUIRE( k2 == k2 );
+  // test for a empty key
+  zypp::PublicKey empty_key;
+  BOOST_REQUIRE( ! empty_key.isValid() );
+}
+
diff --git a/tests/zypp/RWPtr_test.cc b/tests/zypp/RWPtr_test.cc
new file mode 100644 (file)
index 0000000..5953328
--- /dev/null
@@ -0,0 +1,83 @@
+#include <boost/test/auto_unit_test.hpp>
+
+#include <zypp/base/PtrTypes.h>
+#include <string>
+#include <iostream>
+
+#define BOOST_TEST_MODULE RWPtr_test
+
+struct Foo
+{
+  int _foo;
+
+  Foo(int foo=0): _foo(foo)
+  {
+    std::cerr << "created Foo(" << _foo << ")" << std::endl;
+  }
+  ~Foo()
+  {
+    std::cerr << "destroy Foo(" << _foo << ")" << std::endl;
+  }
+};
+
+#define REF_TEST(ref,msg,exp,res) \
+do { \
+  bool unique = exp; \
+  std::cerr << msg << std::endl; \
+  if( ref) { \
+    std::cerr << "ref contains object" << std::endl; \
+  } else { \
+    std::cerr << "ref contains no object" << std::endl; \
+  } \
+  std::cerr << "ref counter is " << ref.use_count() << std::endl; \
+  if( ref.unique()) { \
+    std::cerr << "ref is unique" << std::endl; \
+    if( unique) { \
+      std::cerr << "EXPECTED" << std::endl; \
+    } else { \
+      std::cerr << "NOT EXPECTED" << std::endl; \
+      res = 1; \
+    } \
+  } else { \
+    std::cerr << "ref is shared" << std::endl; \
+    if( !unique) { \
+      std::cerr << "EXPECTED" << std::endl; \
+    } else { \
+      std::cerr << "NOT EXPECTED" << std::endl; \
+      res = 1;  \
+    } \
+  } \
+  std::cerr << std::endl; \
+} while(0);
+
+BOOST_AUTO_TEST_CASE(basic_test)
+{
+  bool skip_reset = false;
+  int  result = 0;
+
+  typedef zypp::RW_pointer<Foo> FooRef;
+
+  FooRef ref;
+  REF_TEST(ref,"=== REF(nil)", true, result);
+
+  ref.reset(new Foo(42));
+  REF_TEST(ref,"=== REF(object)", true, result);
+
+  {
+    FooRef ref2(ref);
+    REF_TEST(ref,"=== REF2(REF)", false, result);
+  }
+
+  REF_TEST(ref,"=== REF(object), REF2 out of scope now", true, result);
+
+  if( !skip_reset)
+  {
+    ref.reset();
+    REF_TEST(ref,"=== REF(nil), reset()", true, result);
+  }
+
+  std::cerr << "RESULT: "
+            << (result == 0 ? "PASSED" : "FAILED")
+            << std::endl;
+}
+
diff --git a/tests/zypp/RepoInfo_test.cc b/tests/zypp/RepoInfo_test.cc
new file mode 100644 (file)
index 0000000..55ca91c
--- /dev/null
@@ -0,0 +1,49 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/RepoInfo.h"
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/parameterized_test.hpp>
+#include <boost/test/unit_test_log.hpp>
+
+#include "KeyRingTestReceiver.h"
+
+#include "WebServer.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace boost::unit_test::log;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+using namespace zypp::repo;
+
+BOOST_AUTO_TEST_CASE(repoinfo_test)
+{
+  WebServer web((Pathname(TESTS_SRC_DIR) + "/data/Mirrorlist/remote-site").c_str(), 10001);
+  web.start();
+
+  Url weburl (web.url());
+  weburl.setPathName("/metalink.xml");
+
+  RepoInfo ri;
+
+  ri.setMirrorListUrl(weburl);
+
+  BOOST_CHECK(ri.url().asString() == "ftp://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/13/x86_64/");
+
+  ostringstream ostr;
+  ri.dumpAsIniOn(ostr);
+
+  BOOST_CHECK( ostr.str().find("baseurl=") == string::npos );
+
+  web.stop();
+}
diff --git a/tests/zypp/RepoManager_test.cc b/tests/zypp/RepoManager_test.cc
new file mode 100644 (file)
index 0000000..ef225ea
--- /dev/null
@@ -0,0 +1,305 @@
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <string>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Exception.h"
+#include "zypp/KeyRing.h"
+#include "zypp/PublicKey.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+#include "zypp/ServiceInfo.h"
+
+#include "zypp/RepoManager.h"
+
+#include "TestSetup.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+
+#include "KeyRingTestReceiver.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+using namespace zypp::repo;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/zypp/data/RepoManager")
+
+#define REPODATADIR (Pathname(TESTS_SRC_DIR) + "/repo/susetags/data/addon_in_subdir")
+
+
+BOOST_AUTO_TEST_CASE(refresh_addon_in_subdir)
+{
+    KeyRingTestReceiver keyring_callbacks;
+    KeyRingTestSignalReceiver receiver;
+
+    // disable sgnature checking
+    keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+    keyring_callbacks.answerAcceptVerFailed(true);
+    keyring_callbacks.answerAcceptUnknownKey(true);
+
+    // make sure we can refresh an addon which is in a subpath in a media url
+    TestSetup test( Arch_x86_64 );
+    RepoInfo info;
+    info.setBaseUrl(Url(string("dir:") + REPODATADIR.asString()));
+    info.setPath("/updates");
+    info.setType(RepoType::YAST2);
+    info.setAlias("boooh");
+
+    test.loadRepo(info);
+
+    // take care we actually got the data
+    Repository r( test.satpool().reposFind( "boooh" ) );
+    BOOST_REQUIRE( r );
+    BOOST_CHECK_EQUAL( r.solvablesSize(), 2 );
+    BOOST_CHECK_EQUAL( r.info().type(), repo::RepoType::YAST2 );
+    BOOST_CHECK( r.info().hasLicense() );
+}
+
+BOOST_AUTO_TEST_CASE(pluginservices_test)
+{
+  TmpDir tmpCachePath;
+  RepoManagerOptions opts( RepoManagerOptions::makeTestSetup( tmpCachePath ) ) ;
+
+  filesystem::assert_dir( opts.knownReposPath );
+  filesystem::assert_dir( opts.pluginsPath / "services");
+
+  opts.pluginsPath = DATADIR + "/plugin-service-lib-1";
+  BOOST_CHECK(PathInfo(opts.pluginsPath / "services/service").isExist());
+
+  {
+    RepoManager manager(opts);
+    BOOST_REQUIRE_EQUAL(1, manager.serviceSize());
+    BOOST_CHECK(manager.repoEmpty());
+
+    ServiceInfo service(*manager.serviceBegin());
+    BOOST_CHECK_EQUAL("service", service.alias());
+    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-1/services/service", service.url().asString());
+
+    // now refresh the service
+    manager.refreshServices();
+    BOOST_CHECK_EQUAL((unsigned) 2, manager.repoSize());
+    //std::list<RepoInfo> infos;
+    //manager.getRepositoriesInService("test",
+    //  insert_iterator<std::list<RepoInfo> >(infos,infos.begin()));
+    //BOOST_CHECK_EQUAL(infos.size(), 2); // 2 from new repoindex
+  }
+
+  // Now simulate the service changed
+  opts.pluginsPath = DATADIR + "/plugin-service-lib-2";
+  {
+    RepoManager manager(opts);
+    BOOST_REQUIRE_EQUAL(1, manager.serviceSize());
+
+    ServiceInfo service(*manager.serviceBegin());
+    BOOST_CHECK_EQUAL("service", service.alias());
+    BOOST_CHECK_EQUAL( "file:" + DATADIR.asString() + "/plugin-service-lib-2/services/service", service.url().asString());
+    // now refresh the service
+    manager.refreshServices();
+    BOOST_CHECK_EQUAL((unsigned) 1, manager.repoSize());
+  }
+}
+
+// regression test for services bug
+// if you modify a service that you just
+// added and saved, the service was not associated with its
+// file internally
+BOOST_AUTO_TEST_CASE(service_file_link_bug)
+{
+  TmpDir tmpCachePath;
+  RepoManagerOptions opts( RepoManagerOptions::makeTestSetup( tmpCachePath ) ) ;
+
+  filesystem::mkdir( opts.knownReposPath );
+  filesystem::mkdir( opts.knownServicesPath );
+  RepoManager manager(opts);
+
+  //test service
+  Url urlS;
+  urlS.setPathName(DATADIR.asString());
+  urlS.setScheme("dir");
+  ServiceInfo service("test", urlS);
+  service.setEnabled(true);
+
+  manager.addService(service);
+  // now internally, service is associated with the file
+  // where it was saved
+
+  // the following line reset the file association with the bug
+  manager.modifyService(service.alias(), service);
+  // and the following modifyService fails because there is no
+  // association
+  manager.modifyService(service.alias(), service);
+}
+
+BOOST_AUTO_TEST_CASE(repomanager_test)
+{
+  TmpDir tmpCachePath;
+  RepoManagerOptions opts( RepoManagerOptions::makeTestSetup( tmpCachePath ) ) ;
+  opts.servicesTargetDistro = "sles-10-i586"; // usually determined by the Target
+
+  filesystem::mkdir( opts.knownReposPath );
+  filesystem::mkdir( opts.knownServicesPath );
+  BOOST_CHECK_EQUAL( filesystem::copy_dir_content( DATADIR + "/repos.d", opts.knownReposPath ), 0 );
+
+  RepoManager manager(opts);
+
+  list<RepoInfo> repos;
+  repos.insert(repos.end(), manager.repoBegin(), manager.repoEnd());
+  BOOST_CHECK_EQUAL(repos.size(), (unsigned) 4);
+
+  // now add a .repo file with 2 repositories in it
+  Url url;
+  url.setPathName((DATADIR + "/proprietary.repo").asString());
+  url.setScheme("file");
+
+  manager.addRepositories(url);
+
+  // check it was not overwriten the proprietary.repo file
+  BOOST_CHECK( PathInfo(opts.knownReposPath + "/proprietary.repo_1").isExist() );
+
+  // now there should be 6 repos
+  repos.clear();
+  repos.insert(repos.end(), manager.repoBegin(), manager.repoEnd());
+  BOOST_CHECK_EQUAL(repos.size(), (unsigned) 6);
+
+  RepoInfo office_dup;
+  office_dup.setAlias("office");
+  BOOST_CHECK_THROW(manager.addRepository(office_dup), RepoAlreadyExistsException);
+
+  // delete the office repo inside the propietary_1.repo
+  RepoInfo office;
+  office.setAlias("office");
+  manager.removeRepository(office);
+  // now there should be 5 repos
+  repos.clear();
+  repos.insert(repos.end(), manager.repoBegin(), manager.repoEnd());
+  BOOST_CHECK_EQUAL(repos.size(), (unsigned) 5);
+  // the file still contained one repo, so it should still exists
+  BOOST_CHECK( PathInfo(opts.knownReposPath + "/proprietary.repo_1").isExist() );
+
+  // now delete the macromedia one
+  RepoInfo macromedia;
+  macromedia.setAlias("macromedia");
+  manager.removeRepository(macromedia);
+  BOOST_CHECK_EQUAL(manager.repoSize(), (unsigned) 4);
+  // the file should not exist anymore
+  BOOST_CHECK( ! PathInfo(opts.knownReposPath + "/proprietary.repo_1").isExist() );
+
+  //test service
+
+  Url urlS;
+  urlS.setPathName(DATADIR.asString());
+  urlS.setScheme("dir");
+
+  ServiceInfo service("test", urlS);
+  service.setEnabled(true);
+
+  manager.addService(service);
+  manager.refreshServices();
+  BOOST_CHECK_EQUAL(manager.repoSize(), (unsigned) 7); // +3 from repoindex
+
+  //simulate change of repoindex.xml
+  urlS.setPathName((DATADIR+"second").asString());
+  urlS.setScheme("dir");
+  service.setUrl(urlS);
+  service.setEnabled(true);
+
+  manager.modifyService(service.alias(), service);
+  manager.refreshServices();
+  BOOST_CHECK_EQUAL(manager.repoSize(), (unsigned) 6); // -1 from new repoindex
+
+  std::list<RepoInfo> infos;
+  manager.getRepositoriesInService("test",
+    insert_iterator<std::list<RepoInfo> >(infos,infos.begin()));
+  BOOST_CHECK_EQUAL(infos.size(), 2); // 2 from new repoindex
+
+
+  // let test cache creation
+
+  RepoInfo repo;
+  repo.setAlias("foo");
+  Url repourl("dir:" + string(TESTS_SRC_DIR) + "/repo/yum/data/10.2-updates-subset");
+  //Url repourl("dir:/mounts/dist/install/stable-x86/suse");
+  //BOOST_CHECK_MESSAGE(0, repourl.asString());
+  repo.setBaseUrl(repourl);
+
+  KeyRingTestReceiver keyring_callbacks;
+  KeyRingTestSignalReceiver receiver;
+
+  // disable sgnature checking
+  keyring_callbacks.answerAcceptKey(KeyRingReport::KEY_TRUST_TEMPORARILY);
+  keyring_callbacks.answerAcceptVerFailed(true);
+  keyring_callbacks.answerAcceptUnknownKey(true);
+
+  // We have no metadata and cache yet
+  BOOST_CHECK_MESSAGE( !manager.isCached(repo), "Repo should not yet be cached" );
+
+  // This should download metadata and build the cache
+  manager.buildCache(repo);
+
+  // Now we have metadata and cache
+  BOOST_CHECK_MESSAGE( manager.isCached(repo), "Repo should be cached now" );
+
+  // Metadata are up to date
+  RepoManager::RefreshCheckStatus ref_stat = manager.checkIfToRefreshMetadata(repo, *repo.baseUrlsBegin());
+  SEC << endl << ref_stat << endl;
+  BOOST_CHECK_MESSAGE( ref_stat== RepoManager::REPO_UP_TO_DATE || ref_stat == RepoManager::REPO_CHECK_DELAYED, "Metadata should be up to date" );
+
+   // the solv file should exists now
+  Pathname base = (opts.repoCachePath / "solv" / repo.alias());
+  Pathname solvfile = base / "solv";
+  Pathname cookiefile = base / "cookie";
+  BOOST_CHECK_MESSAGE( PathInfo(solvfile).isExist(), "Solv file is created after caching: " + solvfile.asString());
+  BOOST_CHECK_MESSAGE( PathInfo(cookiefile).isExist(), "Cookie file is created after caching: " + cookiefile.asString());
+
+  MIL << "Repo already in cache, clean cache"<< endl;
+  manager.cleanCache(repo);
+
+  BOOST_CHECK_MESSAGE( !manager.isCached(repo),
+                       "Repo cache was just deleted, should not be cached now" );
+  // now cache should build normally
+  manager.buildCache(repo);
+
+  manager.loadFromCache(repo);
+
+  if ( manager.isCached(repo ) )
+  {
+    MIL << "Repo already in cache, clean cache"<< endl;
+    manager.cleanCache(repo);
+  }
+  MIL << "Parsing repository metadata..." << endl;
+  manager.buildCache(repo);
+
+
+  // now test that loading twice a repo updates
+  // it instead of duplicating the solv file
+
+
+}
+
+BOOST_AUTO_TEST_CASE(repo_seting_test)
+{
+  RepoInfo repo;
+  repo.setAlias("foo");
+  repo.addBaseUrl(string("http://test.org"));
+  BOOST_CHECK_MESSAGE( repo.keepPackages(), "http scheme is not cached");
+  repo.setBaseUrl(string("ftp://test.org"));
+  BOOST_CHECK_MESSAGE( repo.keepPackages(), "ftp scheme is not cached");
+  repo.setBaseUrl(string("smb://test.org"));
+  BOOST_CHECK_MESSAGE( !repo.keepPackages(), "smb scheme is cached");
+  repo.setBaseUrl(string("file:///test.txt"));
+  BOOST_CHECK_MESSAGE( !repo.keepPackages(), "file scheme is cached");
+  repo.setBaseUrl(string("http://test.org"));
+  BOOST_CHECK_MESSAGE( repo.keepPackages(), "cache is depend on second url.");
+}
+
+//! \todo test this
+//BOOST_AUTO_TEST_CASE(repo_dont_overwrite_external_settings_test)
+//{
+//}
diff --git a/tests/zypp/RepoStatus_test.cc b/tests/zypp/RepoStatus_test.cc
new file mode 100644 (file)
index 0000000..577befd
--- /dev/null
@@ -0,0 +1,33 @@
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/TmpPath.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/PathInfo.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+BOOST_AUTO_TEST_CASE(repostatus_test)
+{
+  TmpFile tmpPath;
+  TmpFile tmpPath2;
+  RepoStatus status;
+  RepoStatus fstatus( tmpPath );
+  RepoStatus fstatus2( tmpPath2 );
+  BOOST_CHECK_EQUAL( status.empty(), true );
+  BOOST_CHECK_EQUAL( (status&&status).empty(), true );
+
+  BOOST_CHECK_EQUAL( fstatus.empty(), false );
+  BOOST_CHECK_EQUAL( (fstatus&&status).empty(), false );
+
+  BOOST_CHECK_EQUAL( (fstatus&&status).checksum(), (status&&fstatus).checksum() );
+  BOOST_CHECK_EQUAL( (fstatus&&fstatus2).checksum(), (fstatus2&&fstatus).checksum() );
+
+}
diff --git a/tests/zypp/ResKind_test.cc b/tests/zypp/ResKind_test.cc
new file mode 100644 (file)
index 0000000..e90e506
--- /dev/null
@@ -0,0 +1,33 @@
+#include <boost/test/auto_unit_test.hpp>
+#include "zypp/base/Logger.h"
+#include "zypp/ResKind.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+
+BOOST_AUTO_TEST_CASE(reskind_test)
+{
+  // Default construced is empty ""
+  BOOST_CHECK_EQUAL( ResKind(), "" );
+  // boolean context
+  BOOST_CHECK( ! ResKind() );
+  BOOST_CHECK( ! ResKind(0) );  // id NULL
+  BOOST_CHECK( ! ResKind(1) );  // id ""
+  BOOST_CHECK( ! ResKind("") ); // ""
+  BOOST_CHECK( ResKind(2) );
+  BOOST_CHECK( ResKind("FOO") );
+  // Internal representation is lowercased
+  BOOST_CHECK_EQUAL( ResKind("FOO").asString(), "foo" );
+  // Caseinsensitive comparison
+  BOOST_CHECK_EQUAL( ResKind("FOO"), ResKind("foo") );
+  BOOST_CHECK_EQUAL( ResKind("FOO"), string("Foo") );
+  BOOST_CHECK_EQUAL( ResKind("FOO"), "Foo" );
+  BOOST_CHECK_EQUAL( ResKind("FOO"), string("foo") );
+  BOOST_CHECK_EQUAL( ResKind("FOO"), "foo" );
+  BOOST_CHECK_EQUAL( string("foo"), ResKind("FOO") );
+  BOOST_CHECK_EQUAL( "foo", ResKind("FOO") );
+
+  BOOST_CHECK_EQUAL( ResKind::compare( "FOO", "foo" ), 0 );
+
+}
diff --git a/tests/zypp/ResStatus_test.cc b/tests/zypp/ResStatus_test.cc
new file mode 100644 (file)
index 0000000..5b975e0
--- /dev/null
@@ -0,0 +1,243 @@
+#include "TestSetup.h"
+#include "zypp/ResStatus.h"
+
+#define BOOST_TEST_MODULE ResStatus
+
+BOOST_AUTO_TEST_CASE(Default)
+{
+  {
+    ResStatus s;
+    BOOST_CHECK( s.isUninstalled() );
+    BOOST_CHECK_EQUAL( s.isInstalled(), ! s.isUninstalled() );
+    BOOST_CHECK_EQUAL( s.getTransactValue(),   ResStatus::KEEP_STATE );
+    BOOST_CHECK_EQUAL( s.getTransactByValue(), ResStatus::SOLVER );
+  }
+  {
+    ResStatus s( true );
+    BOOST_CHECK( s.isInstalled() );
+    BOOST_CHECK_EQUAL( s.isInstalled(), ! s.isUninstalled() );
+    BOOST_CHECK_EQUAL( s.getTransactValue(),   ResStatus::KEEP_STATE );
+    BOOST_CHECK_EQUAL( s.getTransactByValue(), ResStatus::SOLVER );
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// tools
+////////////////////////////////////////////////////////////////////////////////
+template<class _Tp>
+inline const _Tp & max( const _Tp & lhs, const _Tp & rhs )
+{ return lhs < rhs ? rhs : lhs; }
+
+template<class _Tp, int N>
+inline _Tp * begin( _Tp (& _array)[N] ) { return _array; }
+
+template<class _Tp, int N>
+inline _Tp * end( _Tp (& _array)[N] ) { return _array + (sizeof(_array)/sizeof(_Tp)); }
+
+ResStatus::TransactByValue transactByValues[] = {
+  ResStatus::USER, ResStatus::APPL_HIGH, ResStatus::APPL_LOW, ResStatus::SOLVER
+};
+
+ResStatus::TransactValue transactValues[] = {
+  ResStatus::TRANSACT, ResStatus::KEEP_STATE, ResStatus::LOCKED
+};
+
+bool transactTo[] = {
+  true, false
+};
+
+// Status transition like setTransact, setLock, setSoftTransact
+typedef bool (ResStatus::* Transition)( bool, ResStatus::TransactByValue );
+
+// Result evaluation
+typedef void (* Evaluate)( ResStatus::TransactValue, ResStatus::TransactByValue, /* fromState, fromBy */
+                           bool,                     ResStatus::TransactByValue, /* toState,   toBy */
+                           bool,                     ResStatus );                /* done,      result */
+
+// build status and return whether the comination is supported. (e.g currently no LOCKED state below APPL_HIGH)
+inline bool initStatus( ResStatus::TransactValue fromState, ResStatus::TransactByValue fromBy, ResStatus & from )
+{
+  from = ResStatus();
+  if ( fromState == ResStatus::KEEP_STATE )
+  {
+    from.setSoftLock( fromBy );
+  }
+  else
+  {
+    from.setTransactValue( fromState, fromBy );
+    if ( fromState == ResStatus::LOCKED && ! from.isLocked() )
+      return false; // no lock at this level (by now just USER APPL_HIGH)
+  }
+  return true;
+}
+
+void testTable( Transition transition, Evaluate evaluate )
+{
+  // Table: For each causer combination (fromBy -> toBy) invoke transition:
+  //
+  //    bool ok = ResStatus(fromState,fromBy).transition( toState, toBy )
+  //
+  // And evaluate the result.
+  //
+  for ( ResStatus::TransactByValue * toBy = begin( transactByValues ); toBy != end( transactByValues ); ++toBy )
+  {
+    for ( ResStatus::TransactByValue * fromBy = begin( transactByValues ); fromBy != end( transactByValues ); ++fromBy )
+    {
+      INT << "=== " << *fromBy << " ==> " << *toBy << " ===" << endl;
+      for ( ResStatus::TransactValue * fromState = begin( transactValues ); fromState != end( transactValues ); ++fromState )
+      {
+        ResStatus from;
+        if ( ! initStatus( *fromState, *fromBy, from ) )
+        {
+          //WAR << "Unsupported ResStatus(" << *fromState << "," << *fromBy << ")" << endl;
+          continue; // Unsupported ResStatus
+        }
+        for ( bool * toState = begin( transactTo ); toState != end( transactTo ); ++toState )
+        {
+          ResStatus result( from );
+          bool done = (result.*transition)( *toState, *toBy );
+          if ( ! done )
+            BOOST_CHECK_EQUAL( from, result ); // status stays unchaged on failure!
+          evaluate( *fromState, *fromBy, *toState, *toBy, done, result );
+        }
+      }
+    }
+  }
+}
+
+// BOOST_CHECK_EQUAL or BOOST_REQUIRE_EQUAL
+#define X BOOST_CHECK_EQUAL
+
+
+// Transition must succeeds always
+#define CHECK_DONE_ALWAYS       X( done, true ); if ( ! done ) return
+
+// Transition succeeds if same or higher TransactByValue
+#define CHECK_DONE_IFCAUSER     X( done, toBy >= fromBy ); if ( ! done ) return
+
+// Transition succeeds if a locker (APPL_HIGH or USER)
+#define CHECK_DONE_ALWAYS_IFLOCKER     X( done, toBy >= ResStatus::APPL_HIGH ); if ( ! done ) return
+
+// Transition succeeds if a locker (APPL_HIGH or USER) and  same or higher TransactByValue
+#define CHECK_DONE_IFCAUSER_ISLOCKER     X( done, toBy >= max(fromBy,ResStatus::APPL_HIGH) ); if ( ! done ) return
+
+
+// Expected target state after transistion
+#define CHECK_STATE(NEW)        X( result.getTransactValue(), ResStatus::NEW )
+
+
+// Transition result: Remember the causer (i.e. may downgrade superior causer of previous state)
+#define CHECK_CAUSER_SET        X( result.getTransactByValue(), toBy )
+
+// Transition result: Remember a superior causer
+#define CHECK_CAUSER_RAISED     X( result.getTransactByValue(), max(fromBy,toBy) )
+
+// Transition result: Causer stays the same
+#define CHECK_CAUSER_STAYS      X( result.getTransactByValue(), fromBy )
+
+// Transition result: Causer reset to least (SOLVER) level.
+#define CHECK_CAUSER_TO_SOLVER  X( result.getTransactByValue(), ResStatus::SOLVER )
+
+
+////////////////////////////////////////////////////////////////////////////////
+// test cases (see BOOST_AUTO_TEST_CASE(transition))
+////////////////////////////////////////////////////////////////////////////////
+// All tests below should define 3 checks, abbrev. by defines
+//
+//    CHECK_DONE_*:         When does the tranaction succeed? (return if not)
+//    CHECK_STATE( NEXT ):  The state the transition leads to (if successfull)
+//    CHECK_CAUSER_*:       Changes to the remembered causer (if successfull)
+//
+
+#define DOCHECK( FROMSTATE, TOSTATE, C_DONE, C_STATE, C_CAUSER ) \
+       if ( ResStatus::FROMSTATE == fromState && TOSTATE == toState ) { C_DONE; CHECK_STATE( C_STATE ); C_CAUSER; }
+
+void evaluateSetTransact( ResStatus::TransactValue fromState, ResStatus::TransactByValue fromBy,
+                          bool                     toState,   ResStatus::TransactByValue toBy,
+                          bool                     done,      ResStatus                  result )
+{
+  ResStatus from;
+  initStatus( fromState, fromBy, from );
+  MIL << from << " =setTransact("<<toState<<","<<toBy<<")=>\t" << done << ":" << result << endl;
+
+  DOCHECK( TRANSACT,   true,   CHECK_DONE_ALWAYS,      TRANSACT,       CHECK_CAUSER_RAISED     );
+  DOCHECK( TRANSACT,   false,  CHECK_DONE_IFCAUSER,    KEEP_STATE,     CHECK_CAUSER_RAISED     ); // from transact into softlock
+  DOCHECK( KEEP_STATE, true,   CHECK_DONE_ALWAYS,      TRANSACT,       CHECK_CAUSER_SET        );
+  DOCHECK( KEEP_STATE, false,  CHECK_DONE_ALWAYS,      KEEP_STATE,     CHECK_CAUSER_STAYS      ); // keep is not raised to softlock
+  DOCHECK( LOCKED,     true,   CHECK_DONE_IFCAUSER,    TRANSACT,       CHECK_CAUSER_SET        );
+  DOCHECK( LOCKED,     false,  CHECK_DONE_ALWAYS,      LOCKED,         CHECK_CAUSER_STAYS      );
+}
+
+void evaluateSetSoftTransact( ResStatus::TransactValue fromState, ResStatus::TransactByValue fromBy,
+                             bool                     toState,   ResStatus::TransactByValue toBy,
+                             bool                     done,      ResStatus                  result )
+{
+  ResStatus from;
+  initStatus( fromState, fromBy, from );
+  MIL << from << " =setSoftTransact("<<toState<<","<<toBy<<")=>\t" << done << ":" << result << endl;
+
+  DOCHECK( TRANSACT,   true,   CHECK_DONE_ALWAYS,      TRANSACT,       CHECK_CAUSER_RAISED     );
+  DOCHECK( TRANSACT,   false,  CHECK_DONE_IFCAUSER,    KEEP_STATE,     CHECK_CAUSER_RAISED     ); // from transact into softlock
+  DOCHECK( KEEP_STATE, true,   CHECK_DONE_IFCAUSER,    TRANSACT,       CHECK_CAUSER_SET        ); // leaving KEEP requires sup. causer
+  DOCHECK( KEEP_STATE, false,  CHECK_DONE_ALWAYS,      KEEP_STATE,     CHECK_CAUSER_STAYS      ); // keep is not raised to softlock
+  DOCHECK( LOCKED,     true,   CHECK_DONE_IFCAUSER,    TRANSACT,       CHECK_CAUSER_SET        );
+  DOCHECK( LOCKED,     false,  CHECK_DONE_ALWAYS,      LOCKED,         CHECK_CAUSER_STAYS      );
+}
+
+// Check whether failures are ok and whether success lead to the correct state
+void evaluateSetLock( ResStatus::TransactValue fromState, ResStatus::TransactByValue fromBy,
+                      bool                     toState,   ResStatus::TransactByValue toBy,
+                      bool                     done,      ResStatus                  result )
+{
+  ResStatus from;
+  initStatus( fromState, fromBy, from );
+  MIL << from << " =setLock("<<toState<<","<<toBy<<")=>\t" << done << ":" << result << endl;
+
+  DOCHECK( TRANSACT,   true,   CHECK_DONE_IFCAUSER_ISLOCKER,   LOCKED,         CHECK_CAUSER_SET        ); // transact is 'not locked'
+  DOCHECK( TRANSACT,   false,  CHECK_DONE_ALWAYS,              TRANSACT,       CHECK_CAUSER_STAYS      );
+  DOCHECK( KEEP_STATE, true,   CHECK_DONE_ALWAYS_IFLOCKER,     LOCKED,         CHECK_CAUSER_SET        );
+  DOCHECK( KEEP_STATE, false,  CHECK_DONE_ALWAYS,              KEEP_STATE,     CHECK_CAUSER_STAYS      );
+  DOCHECK( LOCKED,     true,   CHECK_DONE_ALWAYS,              LOCKED,         CHECK_CAUSER_RAISED     );
+  DOCHECK( LOCKED,     false,  CHECK_DONE_IFCAUSER,            KEEP_STATE,     CHECK_CAUSER_TO_SOLVER  );
+}
+
+BOOST_AUTO_TEST_CASE(transition)
+{
+  //base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "-" ) );
+  MIL << endl;
+  testTable( &ResStatus::setTransact,          &evaluateSetTransact );
+  testTable( &ResStatus::setSoftTransact,      &evaluateSetSoftTransact );
+  testTable( &ResStatus::setLock,              &evaluateSetLock );
+}
+
+
+bool WhilePoolItemSameStateIsPrivate( ResStatus ostatus, ResStatus nstatus )
+{
+  if ( nstatus == ostatus )
+    return true;
+        // some bits changed...
+  if ( nstatus.getTransactValue() != ostatus.getTransactValue()
+       && ( ! nstatus.isBySolver() // ignore solver state changes
+                  // removing a user lock also goes to bySolver
+       || ostatus.getTransactValue() == ResStatus::LOCKED ) )
+    return false;
+  if ( nstatus.isLicenceConfirmed() != ostatus.isLicenceConfirmed() )
+    return false;
+  return true;
+}
+
+BOOST_AUTO_TEST_CASE(savestate)
+{
+  ResStatus ostatus;
+  ResStatus nstatus;
+
+  BOOST_CHECK_EQUAL( WhilePoolItemSameStateIsPrivate( ostatus, nstatus ), true );
+  nstatus.setLock( true, ResStatus::USER );
+  BOOST_CHECK_EQUAL( WhilePoolItemSameStateIsPrivate( ostatus, nstatus ), false );
+  ostatus = nstatus;
+  nstatus.setLock( false, ResStatus::USER );
+  BOOST_CHECK_EQUAL( WhilePoolItemSameStateIsPrivate( ostatus, nstatus ), false );
+}
+
+
+
diff --git a/tests/zypp/Resolvable_test.cc b/tests/zypp/Resolvable_test.cc
new file mode 100644 (file)
index 0000000..cc65f7f
--- /dev/null
@@ -0,0 +1,39 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZYpp.h"
+#include "zypp/Pattern.h"
+
+#include "TestSetup.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+
+
+BOOST_AUTO_TEST_CASE(resolvable_test)
+{
+    TestSetup test( Arch_x86_64 );
+    // test.loadTarget(); // initialize and load target
+    test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
+
+    int pattern_count = 0;
+    for_( pitem, test.pool().begin(), test.pool().end() )
+    {
+        if ( isKind<Pattern>(pitem->resolvable()) )
+        {
+            //BOOST_CHECK( ! asKind<Pattern>(pitem->resolvable())->contents().empty() );
+            MIL << asKind<Pattern>(pitem->resolvable()) << endl;
+            pattern_count++;
+        }        
+    }
+    BOOST_CHECK(pattern_count > 0);
+}
diff --git a/tests/zypp/Selectable_test.cc b/tests/zypp/Selectable_test.cc
new file mode 100644 (file)
index 0000000..c0e2083
--- /dev/null
@@ -0,0 +1,332 @@
+#include "TestSetup.h"
+#include "zypp/ResPool.h"
+#include "zypp/ui/Selectable.h"
+
+#define BOOST_TEST_MODULE Selectable
+
+/////////////////////////////////////////////////////////////////////////////
+
+static TestSetup test;
+
+BOOST_AUTO_TEST_CASE(testcase_init)
+{
+//   zypp::base::LogControl::instance().logToStdErr();
+  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCSelectable" );
+
+//   dumpRange( USR, test.pool().knownRepositoriesBegin(),
+//                   test.pool().knownRepositoriesEnd() ) << endl;
+//   USR << "pool: " << test.pool() << endl;
+}
+/////////////////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(candiadate)
+{
+  ResPoolProxy poolProxy( test.poolProxy() );
+  ui::Selectable::Ptr s( poolProxy.lookup( ResKind::package, "candidate" ) );
+  //   (I 1) {
+  //   I__s_(8)candidate-1-1.i586(@System)(openSUSE)
+  // } (A 6) {
+  //   U__s_(2)candidate-4-1.x86_64(RepoHIGH)(unkown)
+  //   U__s_(3)candidate-4-1.i586(RepoHIGH)(unkown) <- (update) candidate if allowVendorChange
+  //   U__s_(6)candidate-0-1.x86_64(RepoMID)(SUSE)
+  //   U__s_(7)candidate-0-1.i586(RepoMID)(SUSE) <- candidate (highest prio matching arch and vendor)
+  //   U__s_(4)candidate-2-1.x86_64(RepoLOW)(openSUSE)
+  //   U__s_(5)candidate-2-1.i586(RepoLOW)(openSUSE)
+  // }
+  if ( ZConfig::instance().solver_allowVendorChange() )
+  {
+    BOOST_CHECK_EQUAL( s->candidateObj()->repoInfo().alias(), "RepoHIGH" );
+    BOOST_CHECK_EQUAL( s->candidateObj()->edition(), Edition("4-1") );
+    BOOST_CHECK_EQUAL( s->candidateObj()->arch(), Arch_i586 );
+    // updateCandidate:
+    BOOST_CHECK_EQUAL( s->updateCandidateObj(), s->candidateObj() );
+  }
+  else
+  {
+    BOOST_CHECK_EQUAL( s->candidateObj()->repoInfo().alias(), "RepoMID" );
+    BOOST_CHECK_EQUAL( s->candidateObj()->edition(), Edition("0-1") );
+    BOOST_CHECK_EQUAL( s->candidateObj()->arch(), Arch_i586 );
+    // no updateCandidate due to low version
+    BOOST_CHECK_EQUAL( s->updateCandidateObj(), PoolItem() );
+  }
+}
+
+BOOST_AUTO_TEST_CASE(candiadatenoarch)
+{
+  ResPoolProxy poolProxy( test.poolProxy() );
+  ui::Selectable::Ptr s( poolProxy.lookup( ResKind::package, "candidatenoarch" ) );
+/*[package]candidatenoarch: S_KeepInstalled
+   (I 1) {
+   I__s_(17)candidatenoarch-1-1.i586(@System)
+}  (A 8) {
+ C U__s_(4)candidatenoarch-5-1.noarch(RepoHIGH) <- candidate (arch/noarch change)
+   U__s_(5)candidatenoarch-4-1.x86_64(RepoHIGH)
+   U__s_(6)candidatenoarch-4-1.i586(RepoHIGH)
+   U__s_(7)candidatenoarch-4-1.noarch(RepoHIGH)
+   U__s_(12)candidatenoarch-0-2.noarch(RepoMID)
+   U__s_(13)candidatenoarch-0-1.x86_64(RepoMID)
+   U__s_(14)candidatenoarch-0-1.i586(RepoMID)
+   U__s_(15)candidatenoarch-0-1.noarch(RepoMID)
+}  */
+  BOOST_CHECK_EQUAL( s->candidateObj()->repoInfo().alias(), "RepoHIGH" );
+  BOOST_CHECK_EQUAL( s->candidateObj()->edition(), Edition("5-1") );
+  BOOST_CHECK_EQUAL( s->candidateObj()->arch(), Arch_noarch );
+  // no updateCandidate due to low version
+  BOOST_CHECK_EQUAL( s->updateCandidateObj(), s->candidateObj() );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Status change tests
+//
+/////////////////////////////////////////////////////////////////////////////
+
+// build ResStatus and return whether the comination is supported. (e.g currently no LOCKED state below APPL_HIGH)
+inline bool initStatus( ResStatus::TransactValue fromState, ResStatus::TransactByValue fromBy, ResStatus & from )
+{
+  from = ResStatus();
+  if ( fromState == ResStatus::KEEP_STATE )
+  {
+    from.setSoftLock( fromBy );
+  }
+  else
+  {
+    from.setTransactValue( fromState, fromBy );
+    if ( fromState == ResStatus::LOCKED && ! from.isLocked() )
+      return false; // no lock at this level (by now just USER APPL_HIGH)
+  }
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// status verification helper
+/////////////////////////////////////////////////////////////////////////////
+//     enum TransactValue
+//       {
+//         KEEP_STATE = bit::RangeValue<TransactField,0>::value,
+//         LOCKED     = bit::RangeValue<TransactField,1>::value, // locked, must not transact
+//         TRANSACT   = bit::RangeValue<TransactField,2>::value  // transact according to state
+//       };
+
+template <class _Iter>
+inline bool _all( _Iter begin_r, _Iter end_r, ResStatus::TransactValue val_r )
+{
+  for_( it, begin_r, end_r )
+  {
+    if ( it->status().getTransactValue() != val_r )
+      return false;
+  }
+  return true;
+}
+
+template <class _Iter>
+inline bool _none( _Iter begin_r, _Iter end_r, ResStatus::TransactValue val_r )
+{
+  for_( it, begin_r, end_r )
+  {
+    if ( it->status().getTransactValue() == val_r )
+      return false;
+  }
+  return true;
+}
+
+template <class _Iter>
+inline bool _atLeastOne( _Iter begin_r, _Iter end_r, ResStatus::TransactValue val_r )
+{ return ! _none( begin_r, end_r, val_r ); }
+
+inline bool _allBySolver( ui::Selectable::Ptr sel )
+{
+  for_( it, sel->installedBegin(), sel->installedEnd() )
+  {
+    if ( it->status().transacts() && ! it->status().isBySolver() )
+      return false;
+  }
+  for_( it, sel->availableBegin(), sel->availableEnd() )
+  {
+    if ( it->status().transacts() && ! it->status().isBySolver() )
+      return false;
+  }
+  return true;
+}
+
+inline bool _allInstalled( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )           { return _all( sel->installedBegin(), sel->installedEnd(), val_r ); }
+inline bool _noneInstalled( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )          { return _none( sel->installedBegin(), sel->installedEnd(), val_r ); }
+inline bool _atLeastOneInstalled( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )    { return _atLeastOne( sel->installedBegin(), sel->installedEnd(), val_r ); }
+
+inline bool _allAvailable( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )           { return _all( sel->availableBegin(), sel->availableEnd(), val_r ); }
+inline bool _noneAvailable( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )          { return _none( sel->availableBegin(), sel->availableEnd(), val_r ); }
+inline bool _atLeastOneAvailable( ui::Selectable::Ptr sel, ResStatus::TransactValue val_r )    { return _atLeastOne( sel->availableBegin(), sel->availableEnd(), val_r ); }
+
+inline bool _haveInstalled( ui::Selectable::Ptr sel )                                          { return ! sel->installedEmpty(); }
+inline bool _haveAvailable( ui::Selectable::Ptr sel )                                          { return ! sel->availableEmpty(); }
+
+inline bool _noInstalled( ui::Selectable::Ptr sel )                                            { return sel->installedEmpty(); }
+inline bool _noAvailable( ui::Selectable::Ptr sel )                                            { return sel->availableEmpty(); }
+
+#define allInstalled(V)                        _allInstalled(sel,ResStatus::V)
+#define noneInstalled(V)               _noneInstalled(sel,ResStatus::V)
+#define atLeastOneInstalled(V)         _atLeastOneInstalled(sel,ResStatus::V)
+
+#define allAvailable(V)                        _allAvailable(sel,ResStatus::V)
+#define noneAvailable(V)               _noneAvailable(sel,ResStatus::V)
+#define atLeastOneAvailable(V)         _atLeastOneAvailable(sel,ResStatus::V)
+
+#define haveInstalled                  _haveInstalled(sel)
+#define haveAvailable                  _haveAvailable(sel)
+#define noInstalled                    _noInstalled(sel)
+#define noAvailable                    _noAvailable(sel)
+
+#define allBySolver                    _allBySolver(sel)
+
+// Verify Selectable::status computes the right value.
+//
+//       S_Protected,           // Keep this unmodified ( have installedObj && S_Protected )
+//       S_Taboo,               // Keep this unmodified ( have no installedObj && S_Taboo)
+//       // requested by user:
+//       S_Del,                 // delete  installedObj ( clears S_Protected if set )
+//       S_Update,              // install candidateObj ( have installedObj, clears S_Protected if set )
+//       S_Install,             // install candidateObj ( have no installedObj, clears S_Taboo if set )
+//       // not requested by user:
+//       S_AutoDel,             // delete  installedObj
+//       S_AutoUpdate,          // install candidateObj ( have installedObj )
+//       S_AutoInstall,         // install candidateObj ( have no installedObj )
+//       // no modification:
+//       S_KeepInstalled,       // no modification      ( have installedObj && !S_Protected, clears S_Protected if set )
+//       S_NoInst,              // no modification      ( have no installedObj && !S_Taboo, clears S_Taboo if set )
+void verifyState( ui::Selectable::Ptr sel )
+{
+  ui::Status status( sel->status() );
+  SEC << dump(sel) << endl;
+  switch ( status )
+  {
+    case ui::S_Update:
+    case ui::S_AutoUpdate:
+      BOOST_CHECK( haveInstalled );
+      BOOST_CHECK( atLeastOneAvailable(TRANSACT) );
+      BOOST_CHECK_EQUAL( allBySolver, status==ui::S_AutoUpdate );
+      break;
+
+    case ui::S_Del:
+    case ui::S_AutoDel:
+      BOOST_CHECK( haveInstalled );
+      BOOST_CHECK( noneAvailable(TRANSACT) );  // else would be UPDATE
+      BOOST_CHECK( atLeastOneInstalled(TRANSACT) );
+      BOOST_CHECK_EQUAL( allBySolver, status==ui::S_AutoDel );
+      break;
+
+    case ui::S_Protected:
+      BOOST_CHECK( haveInstalled );
+      BOOST_CHECK( noneAvailable(TRANSACT) );  // else would be UPDATE
+      BOOST_CHECK( noneInstalled(TRANSACT) );  // else would be DEL
+      BOOST_CHECK( allInstalled(LOCKED) );     // implies noneInstalled(TRANSACT)
+      break;
+
+    case ui::S_KeepInstalled:
+      BOOST_CHECK( haveInstalled );
+      BOOST_CHECK( noneAvailable(TRANSACT) );  // else would be UPDATE
+      BOOST_CHECK( noneInstalled(TRANSACT) );  // else would be DEL
+      BOOST_CHECK( ! allInstalled(LOCKED) );   // else would be PROTECTED
+      break;
+
+
+    case ui::S_Install:
+    case ui::S_AutoInstall:
+      BOOST_CHECK( noInstalled );
+      BOOST_CHECK( atLeastOneAvailable(TRANSACT) );
+      BOOST_CHECK_EQUAL( allBySolver, status==ui::S_AutoInstall );
+      break;
+
+    case ui::S_Taboo:
+      BOOST_CHECK( noInstalled );
+      BOOST_CHECK( noneAvailable(TRANSACT) );  // else would be INSTALL
+      BOOST_CHECK( allAvailable(LOCKED) );     // implies noneAvailable(TRANSACT)
+      break;
+
+
+    case ui::S_NoInst:
+      BOOST_CHECK( noInstalled );
+      BOOST_CHECK( noneAvailable(TRANSACT) );  // else would be INSTALL
+      BOOST_CHECK( ! allAvailable(LOCKED) );   // else would be TABOO
+      break;
+  }
+}
+
+// Create all ResStatus combinations over a Selectables PoolItems
+struct StatusCombination
+{
+  StatusCombination()
+  {}
+  StatusCombination( ui::Selectable::Ptr sel_r )
+  {
+   _items.insert( _items.end(), sel_r->installedBegin(), sel_r->installedEnd() );
+   _items.insert( _items.end(), sel_r->availableBegin(), sel_r->availableEnd() );
+  }
+  bool next()
+  {
+    for_( i, 0U, _items.size() )
+    {
+      switch ( _items[i].status().getTransactValue() )
+      {
+        case ResStatus::KEEP_STATE:
+          _items[i].status().setTransactValue( ResStatus::LOCKED, ResStatus::USER );
+          return true;
+          break;
+        case ResStatus::LOCKED:
+          _items[i].status().setTransactValue( ResStatus::TRANSACT, ResStatus::USER );
+          return true;
+          break;
+        case ResStatus::TRANSACT:
+          _items[i].status().setTransactValue( ResStatus::KEEP_STATE, ResStatus::USER );
+          // increment next i
+          break;
+      }
+    }
+    return false; // back at the beginning
+  }
+
+  std::vector<PoolItem> _items;
+};
+
+// Create all ResStatus combinations over the Selectables PoolItems and verify the result.
+void testStatusTable( ui::Selectable::Ptr sel )
+{
+  StatusCombination comb( sel );
+  do {
+    verifyState( sel );
+  } while ( comb.next() );
+}
+
+BOOST_AUTO_TEST_CASE(status_change)
+{
+  // this verifies the Selectables computes ui::Status
+  ResPoolProxy poolProxy( test.poolProxy() );
+  poolProxy.saveState();
+  {
+    ui::Selectable::Ptr sel( poolProxy.lookup( ResKind::package, "installed_only" ) );
+    BOOST_REQUIRE( !sel->installedEmpty() );
+    BOOST_REQUIRE( sel->availableEmpty() );
+    BOOST_CHECK_EQUAL( sel->status(), ui::S_KeepInstalled );
+    testStatusTable( sel );
+  }
+  {
+    ui::Selectable::Ptr sel( poolProxy.lookup( ResKind::package, "installed_and_available" ) );
+    BOOST_REQUIRE( !sel->installedEmpty() );
+    BOOST_REQUIRE( !sel->availableEmpty() );
+    BOOST_CHECK_EQUAL( sel->status(), ui::S_KeepInstalled );
+    testStatusTable( sel );
+  }
+  {
+    ui::Selectable::Ptr sel( poolProxy.lookup( ResKind::package, "available_only" ) );
+    BOOST_REQUIRE( sel->installedEmpty() );
+    BOOST_REQUIRE( !sel->availableEmpty() );
+    BOOST_CHECK_EQUAL( sel->status(), ui::S_NoInst );
+    testStatusTable( sel );
+  }
+
+  // TODO: Test the pickStatus computation (w./w.o. multiinstall)
+  // TODO: Test status/pickStatus transactions (w./w.o. multiinstall)
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
diff --git a/tests/zypp/Signature_test.cc b/tests/zypp/Signature_test.cc
new file mode 100644 (file)
index 0000000..1fe0fc7
--- /dev/null
@@ -0,0 +1,30 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/Signature.h"
+
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+
+using namespace std;
+using namespace zypp;
+
+
+void signature_test()
+{  
+}
+
+test_suite*
+init_unit_test_suite( int, char* [] )
+{
+    test_suite* test= BOOST_TEST_SUITE( "SignaureTest" );
+    test->add( BOOST_TEST_CASE( &signature_test ), 0 /* expected zero error */ );
+    return test;
+}
+
diff --git a/tests/zypp/Target_test.cc b/tests/zypp/Target_test.cc
new file mode 100644 (file)
index 0000000..cfb6ecd
--- /dev/null
@@ -0,0 +1,49 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZYpp.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/TmpPath.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+using namespace zypp::filesystem;
+
+BOOST_AUTO_TEST_CASE(target_test)
+{
+
+    filesystem::TmpDir tmp;
+
+    ZYpp::Ptr z = getZYpp();
+
+    // create the products.d directory
+    assert_dir(tmp.path() / "/etc/products.d" );
+    BOOST_CHECK( copy( Pathname(TESTS_SRC_DIR) / "/zypp/data/Target/product.prod",  tmp.path() / "/etc/products.d/product.prod") == 0 );
+
+    // make it the base product
+    BOOST_CHECK( symlink(tmp.path() / "/etc/products.d/product.prod", tmp.path() / "/etc/products.d/baseproduct" ) == 0 );
+
+    z->initializeTarget( tmp.path() );
+
+    BOOST_CHECK( ! z->target()->anonymousUniqueId().empty() );
+    BOOST_CHECK( PathInfo( tmp.path() / "/var/lib/zypp/AnonymousUniqueId").isExist() );
+    BOOST_CHECK( PathInfo( tmp.path() / "/var/lib/zypp/AnonymousUniqueId").size() > 0 );
+
+    // now check the base product
+    BOOST_CHECK_EQUAL( z->target()->targetDistribution(), "sle-10-i586");
+    BOOST_CHECK_EQUAL( z->target()->targetDistributionRelease(), "special_edition");
+    BOOST_CHECK_EQUAL( z->target()->distributionVersion(), "10");
+
+    Target::DistributionLabel dlabel( z->target()->distributionLabel() );
+    BOOST_CHECK_EQUAL( dlabel.summary, "A cool distribution" );
+    BOOST_CHECK_EQUAL( dlabel.shortName, "" );
+}
diff --git a/tests/zypp/Url_test.cc b/tests/zypp/Url_test.cc
new file mode 100644 (file)
index 0000000..c455c87
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+** Check if the url by scheme repository works, e.g.
+** if there are some initialization order problems
+** (ViewOption) causing asString to format its string
+** differently than configured.
+*/
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/String.h"
+
+#include "zypp/Url.h"
+#include <stdexcept>
+#include <iostream>
+#include <cassert>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+using boost::unit_test::test_case;
+using namespace zypp;
+
+void testUrlAuthority( const Url & url_r,
+                      const std::string & host_r, const std::string & port_r = std::string(),
+                      const std::string & user_r = std::string(), const std::string & pass_r = std::string() )
+{
+  BOOST_CHECK_EQUAL( url_r.getUsername(),      user_r );
+  BOOST_CHECK_EQUAL( url_r.getPassword(),      pass_r );
+  BOOST_CHECK_EQUAL( url_r.getHost(),          host_r );
+  BOOST_CHECK_EQUAL( url_r.getPort(),          port_r );
+}
+
+
+BOOST_AUTO_TEST_CASE(test_ipv6_url)
+{
+    std::string str;
+    zypp::Url   url;
+
+    str = "http://[2001:DB8:0:F102::1]/64/sles11/RC1/CD1?device=eth0";
+    url = Url( str );
+    BOOST_CHECK_EQUAL( str,url.asString() );
+    testUrlAuthority( url, "[2001:DB8:0:F102::1]", "", "", "" );
+
+    // bnc#
+    str = "http://[2001:DB8:0:F102::1]:8080/64/sles11/RC1/CD1?device=eth0";
+    url = Url( str );
+    testUrlAuthority( url, "[2001:DB8:0:F102::1]", "8080", "", "" );
+
+
+    str = "http://user:pass@[2001:DB8:0:F102::1]:8080/64/sles11/RC1/CD1?device=eth0";
+    url = Url( str );
+    testUrlAuthority( url, "[2001:DB8:0:F102::1]", "8080", "user", "pass" );
+}
+
+BOOST_AUTO_TEST_CASE(test_url1)
+{
+    std::string str, one, two;
+    zypp::Url   url;
+
+
+    // asString & asCompleteString should not print "mailto://"
+    str = "mailto:feedback@example.com?subject=hello";
+    url = str;
+    BOOST_CHECK_EQUAL( str, url.asString() );
+    BOOST_CHECK_EQUAL( str, url.asCompleteString() );
+
+    // asString & asCompleteString should add empty authority
+    // "dvd://...", except we request to avoid it.
+    str = "dvd:/srv/ftp";
+    one = "dvd:///srv/ftp";
+    two = "dvd:///srv/ftp";
+    url = str;
+
+    BOOST_CHECK_EQUAL( one, url.asString() );
+    BOOST_CHECK_EQUAL( two, url.asCompleteString() );
+    BOOST_CHECK_EQUAL( str, url.asString(zypp::url::ViewOptions() -
+                                 zypp::url::ViewOption::EMPTY_AUTHORITY));
+
+    // asString shouldn't print the password, asCompleteString should
+    // further, the "//" at the begin of the path should become "/%2F"
+    str = "ftp://user:pass@localhost//srv/ftp";
+    one = "ftp://user@localhost/%2Fsrv/ftp";
+    two = "ftp://user:pass@localhost/%2Fsrv/ftp";
+    url = str;
+
+    BOOST_CHECK_EQUAL( one, url.asString() );
+    BOOST_CHECK_EQUAL( two, url.asCompleteString() );
+
+    // asString shouldn't print the password, asCompleteString should.
+    // further, the "//" at the begin of the path should be keept.
+    str = "http://user:pass@localhost//srv/ftp";
+    one = "http://user@localhost//srv/ftp";
+    two = str;
+    url = str;
+
+    BOOST_CHECK_EQUAL( one, url.asString() );
+    BOOST_CHECK_EQUAL( two, url.asCompleteString() );
+
+    str = "file:./srv/ftp";
+    BOOST_CHECK_EQUAL( zypp::Url(str).asString(), str );
+
+    str = "ftp://foo//srv/ftp";
+    BOOST_CHECK_EQUAL( zypp::Url(str).asString(), "ftp://foo/%2Fsrv/ftp" );
+
+    str = "FTP://user@local%68ost/%2f/srv/ftp";
+    BOOST_CHECK_EQUAL( zypp::Url(str).asString(), "ftp://user@localhost/%2f/srv/ftp" );
+
+    str = "http://[::1]/foo/bar";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString() );
+
+    str = "http://:@just-localhost.example.net:8080/";
+    BOOST_CHECK_EQUAL( zypp::Url(str).asString(), "http://just-localhost.example.net:8080/" );
+
+    str = "mailto:feedback@example.com?subject=hello";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString() );
+
+    str = "nfs://nfs-server/foo/bar/trala";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString() );
+
+    str = "ldap://example.net/dc=example,dc=net?cn,sn?sub?(cn=*)#x";
+    BOOST_CHECK_THROW( zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    str = "ldap://example.net/dc=example,dc=net?cn,sn?sub?(cn=*)";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString() );
+
+    // parseable but invalid, since no host avaliable
+    str = "ldap:///dc=foo,dc=bar";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString());
+    BOOST_CHECK( !zypp::Url(str).isValid());
+
+    // throws:  host is mandatory
+    str = "ftp:///foo/bar";
+    BOOST_CHECK_THROW(zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    // throws:  host is mandatory
+    str = "http:///%2f/srv/ftp";
+    BOOST_CHECK_THROW(zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    // OK, host allowed in file-url
+    str = "file://localhost/some/path";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString());
+
+    // throws:  host not allowed
+    str = "cd://localhost/some/path";
+    BOOST_CHECK_THROW(zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    // throws: no path (email)
+    str = "mailto:";
+    BOOST_CHECK_THROW(zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    // throws:  no path
+    str = "cd:";
+    BOOST_CHECK_THROW(zypp::Url(str).asString(), url::UrlNotAllowedException );
+
+    // OK, valid (no host, path is there)
+    str = "cd:///some/path";
+    BOOST_CHECK_EQUAL( str, zypp::Url(str).asString());
+    BOOST_CHECK( zypp::Url(str).isValid());
+}
+
+BOOST_AUTO_TEST_CASE(test_url2)
+{
+  zypp::Url url("http://user:pass@localhost:/path/to;version=1.1?arg=val#frag");
+
+  BOOST_CHECK_EQUAL( url.asString(),
+  "http://user@localhost/path/to?arg=val#frag" );
+
+  BOOST_CHECK_EQUAL( url.asString(zypp::url::ViewOptions() +
+                     zypp::url::ViewOptions::WITH_PASSWORD),
+  "http://user:pass@localhost/path/to?arg=val#frag");
+
+  BOOST_CHECK_EQUAL( url.asString(zypp::url::ViewOptions() +
+                     zypp::url::ViewOptions::WITH_PATH_PARAMS),
+  "http://user@localhost/path/to;version=1.1?arg=val#frag");
+
+  BOOST_CHECK_EQUAL( url.asCompleteString(),
+  "http://user:pass@localhost/path/to;version=1.1?arg=val#frag");
+}
+
+BOOST_AUTO_TEST_CASE(test_url3)
+{
+  zypp::Url   url("http://localhost/path/to#frag");
+  std::string key;
+  std::string val;
+
+  // will be encoded as "hoho=ha%20ha"
+  key = "hoho";
+  val = "ha ha";
+  url.setQueryParam(key, val);
+  BOOST_CHECK_EQUAL( url.asString(),
+  "http://localhost/path/to?hoho=ha%20ha#frag");
+
+  // will be encoded as "foo%3Dbar%26key=foo%26bar%3Dvalue"
+  key = "foo=bar&key";
+  val = "foo&bar=value";
+  url.setQueryParam(key, val);
+  BOOST_CHECK_EQUAL( url.asString(),
+  "http://localhost/path/to?foo%3Dbar%26key=foo%26bar%3Dvalue&hoho=ha%20ha#frag");
+
+  // will be encoded as "foo%25bar=is%25de%25ad"
+  key = "foo%bar";
+  val = "is%de%ad";
+  url.setQueryParam(key, val);
+  BOOST_CHECK_EQUAL( url.asString(),
+  "http://localhost/path/to?foo%25bar=is%25de%25ad&foo%3Dbar%26key=foo%26bar%3Dvalue&hoho=ha%20ha#frag");
+
+  // get encoded query parameters and compare with results:
+  zypp::url::ParamVec params( url.getQueryStringVec());
+  const char * const  result[] = {
+    "foo%25bar=is%25de%25ad",
+    "foo%3Dbar%26key=foo%26bar%3Dvalue",
+    "hoho=ha%20ha"
+  };
+  BOOST_CHECK( params.size() == (sizeof(result)/sizeof(result[0])));
+  for( size_t i=0; i<params.size(); i++)
+  {
+      BOOST_CHECK_EQUAL( params[i], result[i]);
+  }
+}
+
+BOOST_AUTO_TEST_CASE( test_url4)
+{
+  try
+  {
+    zypp::Url url("ldap://example.net/dc=example,dc=net?cn,sn?sub?(cn=*)");
+
+    // fetch query params as vector
+    zypp::url::ParamVec pvec( url.getQueryStringVec());
+    BOOST_CHECK( pvec.size() == 3);
+    BOOST_CHECK_EQUAL( pvec[0], "cn,sn");
+    BOOST_CHECK_EQUAL( pvec[1], "sub");
+    BOOST_CHECK_EQUAL( pvec[2], "(cn=*)");
+
+    // fetch the query params map
+    // with its special ldap names/keys
+    zypp::url::ParamMap pmap( url.getQueryStringMap());
+    zypp::url::ParamMap::const_iterator m;
+    for(m=pmap.begin(); m!=pmap.end(); ++m)
+    {
+      if("attrs"  == m->first)
+      {
+        BOOST_CHECK_EQUAL( m->second, "cn,sn");
+      }
+      else
+      if("filter" == m->first)
+      {
+        BOOST_CHECK_EQUAL( m->second, "(cn=*)");
+      }
+      else
+      if("scope"  == m->first)
+      {
+        BOOST_CHECK_EQUAL( m->second, "sub");
+      }
+      else
+      {
+        BOOST_FAIL("Unexpected LDAP query parameter name in the map!");
+      }
+    }
+
+    url.setQueryParam("attrs", "cn,sn,uid");
+    url.setQueryParam("filter", "(|(sn=foo)(cn=bar))");
+
+    BOOST_CHECK_EQUAL(url.getQueryParam("attrs"),  "cn,sn,uid");
+    BOOST_CHECK_EQUAL(url.getQueryParam("filter"), "(|(sn=foo)(cn=bar))");
+
+  }
+  catch(const zypp::url::UrlException &e)
+  {
+    ZYPP_CAUGHT(e);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(plugin_querystring_args)
+{
+  // url querysting options without value must be possible
+  // e.g. for plugin schema
+  Url u( "plugin:script?loptv=lvalue&v=optv&lopt=&o" );
+  url::ParamMap pm( u.getQueryStringMap() );
+  BOOST_CHECK_EQUAL( pm.size(), 4 );
+  BOOST_CHECK_EQUAL( pm["loptv"], "lvalue" );
+  BOOST_CHECK_EQUAL( pm["v"], "optv" );
+  BOOST_CHECK_EQUAL( pm["lopt"], "" );
+  BOOST_CHECK_EQUAL( pm["o"], "" );
+}
+
+// vim: set ts=2 sts=2 sw=2 ai et:
diff --git a/tests/zypp/Vendor2_test.cc b/tests/zypp/Vendor2_test.cc
new file mode 100644 (file)
index 0000000..8d62368
--- /dev/null
@@ -0,0 +1,44 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ZYpp.h"
+#include "zypp/VendorAttr.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+
+namespace zypp
+{
+  void reconfigureZConfig( const Pathname & );
+}
+
+#define DATADIR (Pathname(TESTS_BUILD_DIR) + "/zypp/data/Vendor")
+
+
+BOOST_AUTO_TEST_CASE(vendor2_test)
+{
+  reconfigureZConfig( DATADIR / "zypp2.conf" );
+
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("suse", "suse") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("equal", "equal") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("suse", "SuSE") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("opensuse", "SuSE") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("open", "SuSE") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("nothing", "SuSE") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("nvidia", "SuSE") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("nvidia_new_new", "SuSE") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("nvidia", "opensuse") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("ati", "SuSE") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("ati", "nvidia") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("ati_new", "ati") );
+}
+
diff --git a/tests/zypp/Vendor_test.cc b/tests/zypp/Vendor_test.cc
new file mode 100644 (file)
index 0000000..4aad252
--- /dev/null
@@ -0,0 +1,43 @@
+
+#include <iostream>
+#include <list>
+#include <string>
+
+// Boost.Test
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ZYpp.h"
+#include "zypp/VendorAttr.h"
+
+using boost::unit_test::test_case;
+using namespace std;
+using namespace zypp;
+
+namespace zypp
+{
+  void reconfigureZConfig( const Pathname & );
+}
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/zypp/data/Vendor")
+
+BOOST_AUTO_TEST_CASE(vendor_test1)
+{
+  reconfigureZConfig( DATADIR / "zypp1.conf" );
+  // No vendor definition files has been read. So only suse,opensuse vendors are
+  // equivalent
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("suse", "suse") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("equal", "equal") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("suse", "SuSE") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("opensuse", "SuSE") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("open", "SuSE") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("nothing", "SuSE") );
+
+  // but "opensuse build service" gets its own class:
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("opensuse build service", "suse") );
+  BOOST_REQUIRE( !VendorAttr::instance().equivalent("opensuse build service", "opensuse") );
+  BOOST_REQUIRE( VendorAttr::instance().equivalent("opensuse build service", "opensuse build service 2") );
+}
+
diff --git a/tests/zypp/base/CMakeLists.txt b/tests/zypp/base/CMakeLists.txt
new file mode 100644 (file)
index 0000000..10aa5e0
--- /dev/null
@@ -0,0 +1,4 @@
+ADD_TESTS(Glob )
+ADD_TESTS(Sysconfig )
+ADD_TESTS(String )
+ADD_TESTS( InterProcessMutex InterProcessMutex2 )
diff --git a/tests/zypp/base/Glob_test.cc b/tests/zypp/base/Glob_test.cc
new file mode 100644 (file)
index 0000000..f0d338d
--- /dev/null
@@ -0,0 +1,59 @@
+#include "TestSetup.h"
+#include "zypp/Pathname.h"
+#include "zypp/Glob.h"
+
+#define BOOST_TEST_MODULE Glob
+
+static Pathname TEST_ROOT( TESTS_SRC_DIR"/zypp/base/Glob_test.dat" );
+
+using filesystem::Glob;
+
+BOOST_AUTO_TEST_CASE(Glob_default)
+{
+  // enable loging for the scope of this block:
+  // base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "-" ) );
+  Glob q;
+  BOOST_CHECK( q.empty() );
+  BOOST_CHECK( q.size() == 0 );
+  BOOST_CHECK_EQUAL( q.begin(), q.end() );
+  BOOST_CHECK( q.defaultFlags() == Glob::Flags() );
+
+  q.add( TEST_ROOT/"file" );
+  BOOST_CHECK( ! q.empty() );
+  BOOST_CHECK( q.size() == 1 );
+  BOOST_CHECK_NE( q.begin(), q.end() );
+  BOOST_CHECK_EQUAL( *q.begin(), TEST_ROOT/"file" );
+
+  q.reset( Glob::_BRACE );
+  BOOST_CHECK( q.empty() );
+  BOOST_CHECK( q.size() == 0 );
+  BOOST_CHECK_EQUAL( q.begin(), q.end() );
+  BOOST_CHECK( q.defaultFlags() == Glob::_BRACE );
+
+  q.add( TEST_ROOT/"file*" );
+  BOOST_CHECK( q.size() == 3 );
+
+  q.add( TEST_ROOT/"*{.xml,.xml.gz}" );
+  BOOST_CHECK( q.size() == 5 );
+
+  q.clear(); // no flags reset: Glob::_BRACE active
+  BOOST_CHECK( q.size() == 0 );
+
+  q.add( TEST_ROOT/"*{.xml,.xml.gz}" );
+  BOOST_CHECK( q.size() == 2 );
+
+  q.reset(); // flags reset: Glob::_BRACE off
+  BOOST_CHECK( q.size() == 0 );
+
+  q.add( TEST_ROOT/"*{.xml,.xml.gz}" );
+  BOOST_CHECK( q.size() == 0 );
+}
+
+BOOST_AUTO_TEST_CASE(Glob_static)
+{
+  std::set<Pathname> q;
+  Glob::collect( TEST_ROOT/"*{.xml,.xml.gz}", Glob::_BRACE, std::inserter( q, q.begin() ) );
+  BOOST_REQUIRE( q.size() == 2 );
+  BOOST_CHECK_EQUAL( *q.begin(), TEST_ROOT/"file.xml" );
+  BOOST_CHECK_EQUAL( *++q.begin(), TEST_ROOT/"file.xml.gz" );
+}
diff --git a/tests/zypp/base/Glob_test.dat/file b/tests/zypp/base/Glob_test.dat/file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/zypp/base/Glob_test.dat/file.xml b/tests/zypp/base/Glob_test.dat/file.xml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/zypp/base/Glob_test.dat/file.xml.gz b/tests/zypp/base/Glob_test.dat/file.xml.gz
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/zypp/base/InterProcessMutex2_test.cc b/tests/zypp/base/InterProcessMutex2_test.cc
new file mode 100644 (file)
index 0000000..32dc7ca
--- /dev/null
@@ -0,0 +1,68 @@
+
+#include <sys/wait.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <string>
+#include <cstdio>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/InterProcessMutex.h"
+
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace boost::unit_test;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::base;
+
+
+BOOST_AUTO_TEST_CASE(Abort)
+{
+    int r = 0;
+    int status = 0;
+    { 
+        MIL << "ready to fork" << endl;
+    
+        r = fork();
+  
+        if ( r < 0 )
+        {
+            BOOST_ERROR("Can't fork process");
+            return;
+        }
+        else if ( r == 0 )
+        {
+            MIL << "child, PID: " << getpid() << endl;
+            // child
+            sleep(3);
+            BOOST_REQUIRE_THROW( InterProcessMutex( InterProcessMutex::Options(InterProcessMutex::Reader,"testcase", 0)), ZYppLockedException);
+            //InterProcessMutex mutex2("testcase");
+        }
+        else
+        {
+            MIL << "parent: " << getpid() << endl;
+            InterProcessMutex mutex( InterProcessMutex::Options(InterProcessMutex::Writer, "testcase"));
+            // parent
+            sleep(6);
+            wait(NULL);
+            MIL << "first lock will go out of scope" << endl;
+            
+        }        
+    }
+    //if ( r > 0 )
+        
+}
+
+
+
diff --git a/tests/zypp/base/InterProcessMutex_test.cc b/tests/zypp/base/InterProcessMutex_test.cc
new file mode 100644 (file)
index 0000000..9d249ce
--- /dev/null
@@ -0,0 +1,63 @@
+
+#include <sys/wait.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <string>
+#include <cstdio>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/InterProcessMutex.h"
+
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace boost::unit_test;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::base;
+
+BOOST_AUTO_TEST_CASE(WaitForTheOther)
+{
+    int r = 0;
+    int status = 0;
+    { 
+        MIL << "ready to fork" << endl;
+    
+        r = fork();
+  
+        if ( r < 0 )
+        {
+            BOOST_ERROR("Can't fork process");
+            return;
+        }
+        else if ( r == 0 )
+        {
+            MIL << "child, PID: " << getpid() << endl;
+            sleep(3);
+            InterProcessMutex mutex2(InterProcessMutex::Options(InterProcessMutex::Reader,"testcase"));
+        }
+        else
+        {
+            MIL << "parent: " << getpid() << endl;
+            InterProcessMutex mutex(InterProcessMutex::Options(InterProcessMutex::Writer,"testcase"));
+            // parent
+            sleep(6);
+        }        
+    }
+    //if ( r > 0 )
+    //    waitpid(r, &status, 0);
+}
+
+
+
+
diff --git a/tests/zypp/base/String_test.cc b/tests/zypp/base/String_test.cc
new file mode 100644 (file)
index 0000000..19d75b7
--- /dev/null
@@ -0,0 +1,276 @@
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace boost::unit_test;
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::str;
+
+BOOST_AUTO_TEST_CASE(gsubTest)
+{
+  string olds = "olds";
+  string news = "new string";
+
+  BOOST_CHECK_EQUAL(gsub("test olds string",olds,news), "test new string string");
+  BOOST_CHECK_EQUAL(gsub("no string",olds,news),"no string");
+  BOOST_CHECK_EQUAL(gsub("oldsolds",olds,news),"new stringnew string");
+}
+
+BOOST_AUTO_TEST_CASE(replaceAllTest)
+{
+  string olds = "olds";
+  string news = "new string";
+  string tests;
+
+  tests = "test olds string";
+  replaceAll(tests,olds,news);
+  BOOST_CHECK_EQUAL(tests, "test new string string");
+
+  tests = "no string";
+  replaceAll(tests,olds,news);
+  BOOST_CHECK_EQUAL(tests, "no string");
+
+  tests = "oldsolds";
+  replaceAll(tests,olds,news);
+  BOOST_CHECK_EQUAL(tests, "new stringnew string");
+}
+
+BOOST_AUTO_TEST_CASE(testsplitEscaped)
+{
+  string s( "simple non-escaped string" );
+  vector<string> v;
+
+  insert_iterator<vector<string> > ii (v,v.end());
+  splitEscaped( s, ii );
+  BOOST_CHECK_EQUAL( v.size(), 3 );
+
+  v.clear();
+  s = string( "\"escaped sentence \"" );
+  ii = insert_iterator<vector<string> >( v, v.end() );
+  splitEscaped( s, ii );
+  BOOST_CHECK_EQUAL( v.size(), 1 );
+  BOOST_CHECK_EQUAL( v.front(), string( "escaped sentence " ) );
+
+  v.clear();
+  s = string( "\"escaped \\\\sent\\\"ence \\\\\"" );
+  ii = insert_iterator<vector<string> >( v, v.end() );
+  splitEscaped( s, ii );
+  BOOST_CHECK_EQUAL( v.size(), 1 );
+  BOOST_CHECK_EQUAL( v.front(), string( "escaped \\sent\"ence \\" ) );
+
+
+  v.clear();
+  s = string( "escaped sentence\\ with\\ space" );
+  ii = insert_iterator<vector<string> >( v, v.end() );
+  splitEscaped( s, ii );
+  BOOST_CHECK_EQUAL( v.size(), 2 );
+  BOOST_CHECK_EQUAL( v[1], string( "sentence with space" ) );
+
+  // split - join
+  v.clear();
+  s = "some line \"\" foo\\ a foo\\\\ b";
+  str::splitEscaped( s, std::back_inserter(v) );
+  BOOST_CHECK_EQUAL( s, str::joinEscaped( v.begin(), v.end() ) );
+
+  // split - join using alternate sepchar
+  s = str::joinEscaped( v.begin(), v.end(), 'o' );
+  v.clear();
+  str::splitEscaped( s, std::back_inserter(v), "o" );
+  BOOST_CHECK_EQUAL( s, str::joinEscaped( v.begin(), v.end(), 'o' ) );
+}
+
+BOOST_AUTO_TEST_CASE(testsplitEscapedWithEmpty)
+{
+  string s( "simple:non-escaped:string" );
+  vector<string> v;
+
+  BOOST_CHECK_EQUAL(splitFieldsEscaped(s, std::back_inserter(v)), 3);
+  BOOST_CHECK_EQUAL(v.size(), 3);
+
+  v.clear();
+  s = "non-escaped:with::spaces:";
+  BOOST_CHECK_EQUAL(splitFieldsEscaped(s, std::back_inserter(v)), 5);
+  BOOST_CHECK_EQUAL(v.size(), 5);
+
+  v.clear();
+  s = "::";
+  BOOST_CHECK_EQUAL(splitFieldsEscaped(s, std::back_inserter(v)), 3);
+  BOOST_CHECK_EQUAL(v.size(), 3);
+
+  v.clear();
+  s = ":escaped::with\\:spaces";
+  BOOST_CHECK_EQUAL(splitFieldsEscaped(s, std::back_inserter(v)), 4);
+  BOOST_CHECK_EQUAL(v.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(test_escape)
+{
+  string badass = "bad|ass\\|worse";
+  string escaped = str::escape(badass, '|');
+
+  BOOST_CHECK_EQUAL( escaped, "bad\\|ass\\\\\\|worse" );
+}
+
+BOOST_AUTO_TEST_CASE(conversions)
+{
+    BOOST_CHECK_EQUAL(str::numstring(42),     "42");
+    BOOST_CHECK_EQUAL(str::numstring(42, 6),  "    42");
+    BOOST_CHECK_EQUAL(str::numstring(42, -6), "42    ");
+
+    BOOST_CHECK_EQUAL(str::hexstring(42),     "0x0000002a");
+    BOOST_CHECK_EQUAL(str::hexstring(42, 6),  "0x002a");
+    BOOST_CHECK_EQUAL(str::hexstring(42, -6), "0x2a  ");
+
+    BOOST_CHECK_EQUAL(str::octstring(42),     "00052");
+    BOOST_CHECK_EQUAL(str::octstring(42, 6),  "000052");
+    BOOST_CHECK_EQUAL(str::octstring(42, -6), "052   ");
+
+    BOOST_CHECK_EQUAL(str::strtonum<int>("42"), 42);
+
+    BOOST_CHECK_EQUAL(str::toLower("This IS A TeST"), "this is a test");
+    BOOST_CHECK_EQUAL(str::toUpper("This IS A TeST"), "THIS IS A TEST");
+    BOOST_CHECK_EQUAL(str::compareCI("TeST", "test"), 0);
+
+    BOOST_CHECK_EQUAL(str::compareCI("TeST", "test"), 0);
+    BOOST_CHECK_EQUAL(str::compareCI("TeST", "test"), 0);
+}
+
+BOOST_AUTO_TEST_CASE(conversions_to_bool)
+{
+  // true iff true-string {1,on,yes,true}
+  BOOST_CHECK_EQUAL( str::strToTrue("1"),     true );
+  BOOST_CHECK_EQUAL( str::strToTrue("42"),    true );
+  BOOST_CHECK_EQUAL( str::strToTrue("ON"),    true );
+  BOOST_CHECK_EQUAL( str::strToTrue("YES"),   true );
+  BOOST_CHECK_EQUAL( str::strToTrue("TRUE"),  true );
+  BOOST_CHECK_EQUAL( str::strToTrue("0"),     false );
+  BOOST_CHECK_EQUAL( str::strToTrue("OFF"),   false );
+  BOOST_CHECK_EQUAL( str::strToTrue("NO"),    false );
+  BOOST_CHECK_EQUAL( str::strToTrue("FALSE"), false );
+  BOOST_CHECK_EQUAL( str::strToTrue(""),      false );
+  BOOST_CHECK_EQUAL( str::strToTrue("foo"),   false );
+
+  // false iff false-string {0,off,no,false}
+  BOOST_CHECK_EQUAL( str::strToFalse("1"),     true );
+  BOOST_CHECK_EQUAL( str::strToFalse("42"),    true );
+  BOOST_CHECK_EQUAL( str::strToFalse("ON"),    true );
+  BOOST_CHECK_EQUAL( str::strToFalse("YES"),   true );
+  BOOST_CHECK_EQUAL( str::strToFalse("TRUE"),  true );
+  BOOST_CHECK_EQUAL( str::strToFalse("0"),     false );
+  BOOST_CHECK_EQUAL( str::strToFalse("OFF"),   false );
+  BOOST_CHECK_EQUAL( str::strToFalse("NO"),    false );
+  BOOST_CHECK_EQUAL( str::strToFalse("FALSE"), false );
+  BOOST_CHECK_EQUAL( str::strToFalse(""),      true );
+  BOOST_CHECK_EQUAL( str::strToFalse("foo"),   true );
+
+  // true iff true-string
+  BOOST_CHECK_EQUAL( str::strToBool("TRUE",  false), true );
+  BOOST_CHECK_EQUAL( str::strToBool("FALSE", false), false );
+  BOOST_CHECK_EQUAL( str::strToBool("",      false), false );
+  BOOST_CHECK_EQUAL( str::strToBool("foo",   false), false );
+
+  // false iff false-string
+  BOOST_CHECK_EQUAL( str::strToBool("TRUE",  true),  true );
+  BOOST_CHECK_EQUAL( str::strToBool("FALSE", true),  false );
+  BOOST_CHECK_EQUAL( str::strToBool("",      true),  true );
+  BOOST_CHECK_EQUAL( str::strToBool("foo",   true),  true );
+
+  // true/false iff true/false-string, else unchanged
+  bool ret;
+  ret = true; BOOST_CHECK_EQUAL( str::strToBoolNodefault("TRUE",  ret),  true );
+  ret = true; BOOST_CHECK_EQUAL( str::strToBoolNodefault("FALSE", ret),  false );
+  ret = true; BOOST_CHECK_EQUAL( str::strToBoolNodefault("",      ret),  true );
+  ret = true; BOOST_CHECK_EQUAL( str::strToBoolNodefault("foo",   ret),  true );
+
+  ret = false; BOOST_CHECK_EQUAL( str::strToBoolNodefault("TRUE",  ret),  true );
+  ret = false; BOOST_CHECK_EQUAL( str::strToBoolNodefault("FALSE", ret),  false );
+  ret = false; BOOST_CHECK_EQUAL( str::strToBoolNodefault("",      ret),  false );
+  ret = false; BOOST_CHECK_EQUAL( str::strToBoolNodefault("foo",   ret),  false );
+}
+
+BOOST_AUTO_TEST_CASE(operations)
+{
+    BOOST_CHECK_EQUAL(str::ltrim(" \t f \t ffo \t "), "f \t ffo \t ");
+    BOOST_CHECK_EQUAL(str::rtrim(" \t f \t ffo \t "), " \t f \t ffo");
+    BOOST_CHECK_EQUAL(str::trim(" \t f \t ffo \t "),  "f \t ffo");
+
+    // strip first
+    {
+        string tostrip(" Oh! la la ");
+        string word( str::stripFirstWord(tostrip, true) ); // ltrim first
+        BOOST_CHECK_EQUAL(word, "Oh!");
+        BOOST_CHECK_EQUAL(tostrip, "la la ");
+    }
+    {
+        string tostrip(" Oh! la la ");
+        string word( str::stripFirstWord(tostrip, false) ); // no ltrim first
+        BOOST_CHECK_EQUAL(word, "");
+        BOOST_CHECK_EQUAL(tostrip, "Oh! la la ");
+    }
+
+    // strip last
+    {
+        string tostrip(" Oh! la la ");
+        string word( str::stripLastWord(tostrip, true) ); // rtrim first
+        BOOST_CHECK_EQUAL(word, "la");
+        BOOST_CHECK_EQUAL(tostrip, " Oh! la");
+    }
+    {
+        string tostrip(" Oh! la la ");
+        string word( str::stripLastWord(tostrip, false) ); // no rtrim first
+        BOOST_CHECK_EQUAL(word, "");
+        BOOST_CHECK_EQUAL(tostrip, " Oh! la la");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(prefix_suffix)
+{
+  BOOST_CHECK( str::hasPrefix("abcXabcYabc", "abcX") );
+  BOOST_CHECK( str::hasSuffix("abcXabcYabc", "Yabc") );
+
+  BOOST_CHECK_EQUAL( str::stripPrefix("abcXabcYabc", "abcX"),  "abcYabc" );
+  BOOST_CHECK_EQUAL( str::stripSuffix("abcXabcYabc", "Yabc"),  "abcXabc" );
+
+  BOOST_CHECK( ! str::hasPrefix("abcXabcYabc", "ac") );
+  BOOST_CHECK( ! str::hasSuffix("abcXabcYabc", "ac") );
+
+  BOOST_CHECK_EQUAL( str::stripPrefix("abcXabcYabc", "ac"),  "abcXabcYabc" );
+  BOOST_CHECK_EQUAL( str::stripSuffix("abcXabcYabc", "ac"),  "abcXabcYabc" );
+
+  BOOST_CHECK( str::startsWith("abcXabcYabc", "abc") );
+  BOOST_CHECK( str::endsWith("abcXabcYabc", "abc") );
+
+  BOOST_CHECK( str::contains("abcXabcYabc", "XabcY") );
+  BOOST_CHECK( ! str::contains("abcXabcYabc", "xabcy") );
+  BOOST_CHECK( str::containsCI("abcXabcYabc", "xabcy") );
+}
+
+BOOST_AUTO_TEST_CASE(hexencode_hexdecode)
+{
+  std::string o;
+  o.reserve( 256 );
+  for ( unsigned i = 1; i < 256; ++i )
+    o += i;
+
+  std::string e( str::hexencode( o ) );
+  // encoded contains nothing but [%a-zA-Z0-9]
+  for ( unsigned i = 0; i < 255; ++i )
+  {
+    char ch = e[i];
+    BOOST_CHECK( ch == '%'
+                 || ( 'a' <= ch && ch <= 'z' )
+                 || ( 'A' <= ch && ch <= 'Z' )
+                 || ( '0' <= ch && ch <= '9' ) );
+  }
+
+  std::string d( str::hexdecode( e ) );
+  BOOST_CHECK( o == d );
+//   for ( unsigned i = 0; i < 255; ++i )
+//     if ( o[i] != d[i] )
+//       WAR << i << " " << unsigned(o[i]) << " != " << unsigned(d[i]) << endl;
+}
diff --git a/tests/zypp/base/Sysconfig_test.cc b/tests/zypp/base/Sysconfig_test.cc
new file mode 100644 (file)
index 0000000..610555a
--- /dev/null
@@ -0,0 +1,34 @@
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <string>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/base/Sysconfig.h"
+
+using boost::unit_test::test_suite;
+using boost::unit_test::test_case;
+using namespace boost::unit_test;
+
+using namespace std;
+using namespace zypp;
+
+#define DATADIR (Pathname(TESTS_SRC_DIR) + "/zypp/base/data/Sysconfig")
+
+BOOST_AUTO_TEST_CASE(Sysconfig)
+{
+  Pathname file = DATADIR + "proxy";
+  map<string,string> values = zypp::base::sysconfig::read(file);
+  BOOST_CHECK_EQUAL( values.size(), 6 );
+  BOOST_CHECK_EQUAL( values["PROXY_ENABLED"], "no");
+  BOOST_CHECK_EQUAL( values["GOPHER_PROXY"], "");
+  BOOST_CHECK_EQUAL( values["NO_PROXY"], "localhost, 127.0.0.1");
+}
+
diff --git a/tests/zypp/base/data/Sysconfig/proxy b/tests/zypp/base/data/Sysconfig/proxy
new file mode 100644 (file)
index 0000000..0924e45
--- /dev/null
@@ -0,0 +1,51 @@
+## Path:       Network/Proxy
+## Description:        
+## Type:       yesno
+## Default:    no
+## Config:      kde,profiles
+#
+# Enable a generation of the proxy settings to the profile.
+# This setting allows to turn the proxy on and off while
+# preserving the particular proxy setup.
+# 
+PROXY_ENABLED="no"
+
+## Type:       string
+## Default:    ""
+#
+# Some programs (e.g. lynx, arena and wget) support proxies, if set in
+# the environment.  SuSEconfig can add these environment variables to
+# /etc/SuSEconfig/* (sourced by /etc/profile etc.) -
+# See http://portal.suse.com/sdb/en/1998/01/lynx_proxy.html for more details.
+# Example: HTTP_PROXY="http://proxy.provider.de:3128/"
+HTTP_PROXY=""
+
+## Type:       string
+## Default:    ""
+#
+# Some programs (e.g. lynx, arena and wget) support proxies, if set in
+# the environment.  SuSEconfig can add these environment variables to
+# /etc/SuSEconfig/* (sourced by /etc/profile etc.) -
+# this setting is for https connections
+HTTPS_PROXY=""
+
+## Type:       string
+## Default:    ""
+#
+# Example: FTP_PROXY="http://proxy.provider.de:3128/"
+#
+FTP_PROXY=""
+
+## Type:       string
+## Default:    ""
+#
+# Example: GOPHER_PROXY="http://proxy.provider.de:3128/"
+#
+GOPHER_PROXY=""
+
+## Type:       string(localhost)
+## Default:    localhost
+#
+# Example: NO_PROXY="www.me.de, do.main, localhost"
+#
+NO_PROXY="localhost, 127.0.0.1"
diff --git a/tests/zypp/data/Delta/repodata/deltainfo.xml b/tests/zypp/data/Delta/repodata/deltainfo.xml
new file mode 100644 (file)
index 0000000..2909d1b
--- /dev/null
@@ -0,0 +1,17 @@
+ <deltainfo>
+  <newpackage name="libzypp" epoch="0" version="4.21.3" release="2" arch="i386">
+    <delta oldepoch="0" oldversion="4.21.3" oldrelease="1">
+      <filename>DRPMS/libzypp-4.21.3-1_4.21.3-2.i386.drpm</filename>
+      <sequence>libzypp-4.21.3-1-d3571f98b048b1a870e40241bb46c67ab4</sequence>
+      <size>22452</size>
+      <checksum type="sha">8f05394695dee9399c204614e21e5f6848990ab7</checksum>
+    </delta>
+    <delta oldepoch="0" oldversion="4.21.2" oldrelease="3">
+      <filename>DRPMS/libzypp-4.21.2-3_4.21.3-2.i386.drpm</filename>
+       <sequence>libzypp-4.21.2-3-e82691677eee1e83b4812572c5c9ce8eb</sequence>
+       <size>110362</size>
+       <checksum type="sha">326658fee45c0baec1e70231046dbaf560f941ce</checksum>
+     </delta>
+   </newpackage>
+ </deltainfo>
+
diff --git a/tests/zypp/data/Delta/repodata/primary.xml.gz b/tests/zypp/data/Delta/repodata/primary.xml.gz
new file mode 100644 (file)
index 0000000..b41606a
Binary files /dev/null and b/tests/zypp/data/Delta/repodata/primary.xml.gz differ
diff --git a/tests/zypp/data/Delta/repodata/repomd.xml b/tests/zypp/data/Delta/repodata/repomd.xml
new file mode 100644 (file)
index 0000000..264628d
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">d41033826a12ce44a1b33eff2e7905785e0999da</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">319b2951aad2417c8961442ba692d4700962043b</open-checksum>
+  </data>
+  <data type="updateinfo">
+    <location href="repodata/updateinfo.xml.gz"/>
+    <checksum type="sha">4f8f83525b232db93a761ed1be79515956b574cf</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">70eb95f379e0db1c9815f0a1cb2269d93e408015</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">28a6aae0cd873e1df286d4a07fc7e54263fec79d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">5ad445e403218ef4a6585dbfc37ccf31d5a10096</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">553f609c610b0cf51b54efc4c5c618537707ac8d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">8c840e0b03ad8c2ed0d4ddf57f9a6b5cea3ac412</open-checksum>
+  </data>
+  <data type="deltainfo">
+    <location href="repodata/deltainfo.xml"/>
+    <checksum type="sha">596018d6e767808ac6bf4e89cc0ea1ea7a1916a8</checksum>
+    <timestamp>1211279868</timestamp>
+    <open-checksum type="sha">596018d6e767808ac6bf4e89cc0ea1ea7a1916a8</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/zypp/data/Delta/repodata/repomd.xml.asc b/tests/zypp/data/Delta/repodata/repomd.xml.asc
new file mode 100644 (file)
index 0000000..4533fce
--- /dev/null
@@ -0,0 +1,43 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">d41033826a12ce44a1b33eff2e7905785e0999da</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">319b2951aad2417c8961442ba692d4700962043b</open-checksum>
+  </data>
+  <data type="updateinfo">
+    <location href="repodata/updateinfo.xml.gz"/>
+    <checksum type="sha">4f8f83525b232db93a761ed1be79515956b574cf</checksum>
+    <timestamp>1211014822</timestamp>
+    <open-checksum type="sha">70eb95f379e0db1c9815f0a1cb2269d93e408015</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">28a6aae0cd873e1df286d4a07fc7e54263fec79d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">5ad445e403218ef4a6585dbfc37ccf31d5a10096</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">553f609c610b0cf51b54efc4c5c618537707ac8d</checksum>
+    <timestamp>1211014821</timestamp>
+    <open-checksum type="sha">8c840e0b03ad8c2ed0d4ddf57f9a6b5cea3ac412</open-checksum>
+  </data>
+  <data type="deltainfo">
+    <location href="repodata/deltainfo.xml"/>
+    <checksum type="sha">596018d6e767808ac6bf4e89cc0ea1ea7a1916a8</checksum>
+    <timestamp>1211279868</timestamp>
+    <open-checksum type="sha">596018d6e767808ac6bf4e89cc0ea1ea7a1916a8</open-checksum>
+  </data>
+</repomd>
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEARECAAYFAkgy4a8ACgkQ1iFKyE0tq/K1UgCgkm7T8QgC27qE9labaumrHo8o
+o/gAoMX7sCbj6CkSTL8a+9eQf+BD0eDV
+=mZtk
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Delta/repodata/repomd.xml.key b/tests/zypp/data/Delta/repodata/repomd.xml.key
new file mode 100644 (file)
index 0000000..66bd5ed
--- /dev/null
@@ -0,0 +1,43 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEfNOC4RBADigjtTYiTyHJ1Ld9Eiul005PyBoIk0oG30mbsrlx+ETir90aB+
+9FwqfiYBbug0eAuIvF8Cpg2scEqtL8yLQHaeNzVz89iAQvPa1UdCUkn6MLMGLm4F
+R9aaOsVA4yrtUDyoouz8eu/WzZfiwi23auEuBcebb19HPZ38YLLNQP39RwCgzWRr
+pfJLYd/W2TYoAg/zpIh6VsMEAMzVoHQWd/BULoqmXORVvssB4u3apXiL4pThCvGk
+7PlicRTIptJnYUUnXbOBm8kcRmW10Bh/Iahxr7GAjxwADwdXAV/1W9xnj/2uZY09
+EwIrTrVGkEnBM+qciroAwwrR3232f+W/2nKoC+VHfoTdWoYfhh365IpWRlgRhWDc
+fJbNBACuHoGS8yve/hOjyOR6oeqt/vYdTPZZWhCaLME6g41yG19+hosw3a2jjgtK
+eP3pNXUQeSJ4fMzanZcgH2XNNPCljphIogjETLpR11jjoCtQy+N0jNZM27EMCd2g
+LMyW0UFOCqh7hqS0wHbbIkeja2VETsNJhcZEUE/MISdB534LQLQkSm9zZWYgUmVp
+ZGluZ2VyIDxqcmVpZGluZ2VyQHN1c2UuY3o+iGYEExECACYFAkfNOC4CGyMFCQlm
+AYAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDWIUrITS2r8kSdAJ9aAv5D1Vk1
+aAXQtjG8Kbt5OBZcMQCfRvyOql8UUoV30nx3tmfXMWyyYSK5Ag0ER804gBAIAJMI
+aFDKVAnCbm0hm+MjolK3gobEFFr2QZNCu4DvYZikUz3P1Uqc4EjhTgPWCgd2mN4w
+Odzi1n2mz+PF80i+xl92gN6e2Br5mufsXfDf58smrQZ71E6IuG/XJC9Y+obfgIFp
+ngTjYomal7wywFqBBZq3mIN8xM+9VkUIsUhvDWvbj++/JO9++uIl67gjx8zhGJhd
+0rWEKsNcbi2cVLizs6Lf+GatTr61pS1AzRU+sxsEr9pgIAN0cvxqRTH54i08Ulf3
+Z+m1+jTJ6vTB206QChyybAL9hp9tckhkWAHcS4paK8lEyz37UExTIVkQNaDxdDad
+zYpE3gVRkniLuKhCk28AAwUH+wdPJIMxIIlPMUtATLgHqeLnCA57tDra6Canp466
+KpJ/aW2GfNYUs0OgivX41oPBBCGd6oDuZwREtj0cq9LlG5YCEbgbsC1DEOncja9/
+XFoTdbp6Slvlc+aw54OUzj6rNSaOSJpigN6YVUnZCoS00vTH/NubOpC+bpl1f5lL
+8YlR2NfS2XL4CFlLBuUQiHv0zzKOj1180oyfzrzjon/pxC3jakj3+t9bJaXbiD09
+zKJRFzFv+9mLqma5zFkWUZdWRR+N7CtYl7LIT3TdbIvlJu3N5cyHgGBkiOykXu7W
+RFdFqdZ7+6aykKUUMbwp+mAVfjJVVm9U473+5X7492SlxfKITwQYEQIADwUCR804
+gAIbDAUJCWYBgAAKCRDWIUrITS2r8mVYAKC0gRLVYbuz9NTtT7u5kaXcFXyXCQCg
+lOfxwAGsuxmSvTOl+hKc69QIf1eZAQ0ER/INNgEIAPFj1RzNpcDF0w8Ryc/+KIx/
+/xqWqE6Kv6DoUu9g3VGmj4lLrwEwbInbJcffOrlZkrZazB24gfORRyyllj0UfW09
+U7qtOzdewmTRDPuYyXcvtL6ATIpjeEapRdDyL2DKLmnpJ7DxFm07ttzgDJp+cmqw
+1XlFB2gt4Sirb3NRSubtX7ZZmaffiIHh9yr31lkpOxIKXMrW5MLhezQ57K779LfY
+58Iq6EUjeGU5XQPLe7lsoBanq1MojdFsRmBx8TT2wjzpMLa1lDMvQgGCW4fajY58
+J03coxYWXu6dp6N5x+15IIETcWtcIRIhy5Y7qV/AGOX5fFG4YEDnmUIiQ5CpIPEA
+EQEAAbQkSm9zZWYgUmVpZGluZ2VyIDxqcmVpZGluZ2VyQHN1c2UuY3o+iQE2BBMB
+AgAgBQJH8g02AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQrMbwWCOvAEFw
+dAgA1BvA6/q6rsk0uVsue3sgJunCdQnF9zY2i8FRuOfLwEoOuX3P82TUCrKYT7jq
+NfiWYbtl9+sp4+5TBxY9M5nBgqckMJoze0vrufq8uBLzrsabIYRPjFYpznxjhIeo
+FDA8PcScOYR51K7YhqAk1rU0M9SmeNCh8O6mgI546QZLVHC7w+2SDtsWWoRhn3/k
+WlHBmaU0XYLtyuRIG3uuUjigUU5F9Gz1sj8X/r4WAO4Id9FGruORlGDlW4yK5vVt
+OHE1/vEnpPodJEI4kWisPoJh+/1q4u/zGBtGL724dPmBuqQ3mLwbXiahCaZiGv+p
+SRTL0aukStJZdNaArDg/PTNxFg==
+=ahjB
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Delta/repodata/updateinfo.xml.gz b/tests/zypp/data/Delta/repodata/updateinfo.xml.gz
new file mode 100644 (file)
index 0000000..28061e0
Binary files /dev/null and b/tests/zypp/data/Delta/repodata/updateinfo.xml.gz differ
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/directory.yast b/tests/zypp/data/Fetcher/remote-site/baseindex/directory.yast
new file mode 100644 (file)
index 0000000..9be1c91
--- /dev/null
@@ -0,0 +1,2 @@
+subdir1/
+subdir2/
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS
new file mode 100644 (file)
index 0000000..e93f782
--- /dev/null
@@ -0,0 +1,2 @@
+f1d2d2f924e986ac86fdf7b36c94bcdf32beec15  subdir1-file1.txt
+e242ed3bffccdf271b7fbaf34ed72d089537b42f  subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..ac94d40
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaaxUACgkQm+zCtd2wN1ZLgACfcqpLHAavAr8cIjenaR3pJHpo
+WoMAmwdbZQX2b9Yw6mY1aGbarwsEon6I
+=/0VZ
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/directory.yast b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/directory.yast
new file mode 100644 (file)
index 0000000..36efc80
--- /dev/null
@@ -0,0 +1,6 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir1-file1.txt
+subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file1.txt b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file1.txt
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file2.txt b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir1/subdir1-file2.txt
new file mode 100644 (file)
index 0000000..5716ca5
--- /dev/null
@@ -0,0 +1 @@
+bar
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS
new file mode 100644 (file)
index 0000000..c688c0b
--- /dev/null
@@ -0,0 +1 @@
+f572d396fae9206628714fb2ce00f72e94f2258f  subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..42445ee
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaayAACgkQm+zCtd2wN1Z3GwCfUjf9mRdFrB5UvOHAW/DoEzWL
+mpEAnRmkzRLTY3hXiZ+95XCYG1csiMmF
+=X6am
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/directory.yast b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/directory.yast
new file mode 100644 (file)
index 0000000..6c2ce07
--- /dev/null
@@ -0,0 +1,5 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/subdir2-file1.txt b/tests/zypp/data/Fetcher/remote-site/baseindex/subdir2/subdir2-file1.txt
new file mode 100644 (file)
index 0000000..ce01362
--- /dev/null
@@ -0,0 +1 @@
+hello
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/directory.yast
new file mode 100644 (file)
index 0000000..8c4b36d
--- /dev/null
@@ -0,0 +1,3 @@
+directory.yast
+subdir1/
+subdir2/
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS
new file mode 100644 (file)
index 0000000..e93f782
--- /dev/null
@@ -0,0 +1,2 @@
+f1d2d2f924e986ac86fdf7b36c94bcdf32beec15  subdir1-file1.txt
+e242ed3bffccdf271b7fbaf34ed72d089537b42f  subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..ac94d40
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaaxUACgkQm+zCtd2wN1ZLgACfcqpLHAavAr8cIjenaR3pJHpo
+WoMAmwdbZQX2b9Yw6mY1aGbarwsEon6I
+=/0VZ
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/directory.yast
new file mode 100644 (file)
index 0000000..36efc80
--- /dev/null
@@ -0,0 +1,6 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir1-file1.txt
+subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file1.txt b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file1.txt
new file mode 100644 (file)
index 0000000..f2fe044
--- /dev/null
@@ -0,0 +1 @@
+fooa
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file2.txt b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir1/subdir1-file2.txt
new file mode 100644 (file)
index 0000000..5716ca5
--- /dev/null
@@ -0,0 +1 @@
+bar
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS
new file mode 100644 (file)
index 0000000..c688c0b
--- /dev/null
@@ -0,0 +1 @@
+f572d396fae9206628714fb2ce00f72e94f2258f  subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..42445ee
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaayAACgkQm+zCtd2wN1Z3GwCfUjf9mRdFrB5UvOHAW/DoEzWL
+mpEAnRmkzRLTY3hXiZ+95XCYG1csiMmF
+=X6am
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/directory.yast
new file mode 100644 (file)
index 0000000..6c2ce07
--- /dev/null
@@ -0,0 +1,5 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/subdir2-file1.txt b/tests/zypp/data/Fetcher/remote-site/complexdir-broken/subdir2/subdir2-file1.txt
new file mode 100644 (file)
index 0000000..ce01362
--- /dev/null
@@ -0,0 +1 @@
+hello
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir/directory.yast
new file mode 100644 (file)
index 0000000..9be1c91
--- /dev/null
@@ -0,0 +1,2 @@
+subdir1/
+subdir2/
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS
new file mode 100644 (file)
index 0000000..e93f782
--- /dev/null
@@ -0,0 +1,2 @@
+f1d2d2f924e986ac86fdf7b36c94bcdf32beec15  subdir1-file1.txt
+e242ed3bffccdf271b7fbaf34ed72d089537b42f  subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..ac94d40
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaaxUACgkQm+zCtd2wN1ZLgACfcqpLHAavAr8cIjenaR3pJHpo
+WoMAmwdbZQX2b9Yw6mY1aGbarwsEon6I
+=/0VZ
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/directory.yast
new file mode 100644 (file)
index 0000000..36efc80
--- /dev/null
@@ -0,0 +1,6 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir1-file1.txt
+subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file1.txt b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file1.txt
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file2.txt b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir1/subdir1-file2.txt
new file mode 100644 (file)
index 0000000..5716ca5
--- /dev/null
@@ -0,0 +1 @@
+bar
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS
new file mode 100644 (file)
index 0000000..c688c0b
--- /dev/null
@@ -0,0 +1 @@
+f572d396fae9206628714fb2ce00f72e94f2258f  subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.asc b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.asc
new file mode 100644 (file)
index 0000000..42445ee
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkjaayAACgkQm+zCtd2wN1Z3GwCfUjf9mRdFrB5UvOHAW/DoEzWL
+mpEAnRmkzRLTY3hXiZ+95XCYG1csiMmF
+=X6am
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.key b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/SHA1SUMS.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/directory.yast b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/directory.yast
new file mode 100644 (file)
index 0000000..6c2ce07
--- /dev/null
@@ -0,0 +1,5 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/subdir2-file1.txt b/tests/zypp/data/Fetcher/remote-site/complexdir/subdir2/subdir2-file1.txt
new file mode 100644 (file)
index 0000000..ce01362
--- /dev/null
@@ -0,0 +1 @@
+hello
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content
new file mode 100644 (file)
index 0000000..7a674fa
--- /dev/null
@@ -0,0 +1,3 @@
+HASH SHA1 f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 subdir1/subdir1-file1.txt
+HASH SHA1 e242ed3bffccdf271b7fbaf34ed72d089537b42f subdir1/subdir1-file2.txt
+HASH SHA1 f572d396fae9206628714fb2ce00f72e94f2258f subdir2/subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.asc b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.asc
new file mode 100644 (file)
index 0000000..30732f1
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkkHLgwACgkQm+zCtd2wN1ZD7QCghDVP3V6Pi/gBMoA6Flc77/4u
+f30Anjf9MqPHx/YZXSFHrYtcst0welTn
+=g7it
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.key b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/content.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/directory.yast
new file mode 100644 (file)
index 0000000..8c4b36d
--- /dev/null
@@ -0,0 +1,3 @@
+directory.yast
+subdir1/
+subdir2/
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/directory.yast
new file mode 100644 (file)
index 0000000..36efc80
--- /dev/null
@@ -0,0 +1,6 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir1-file1.txt
+subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file1.txt b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file1.txt
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file2.txt b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir1/subdir1-file2.txt
new file mode 100644 (file)
index 0000000..fb081f3
--- /dev/null
@@ -0,0 +1 @@
+bar OWNED
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/directory.yast
new file mode 100644 (file)
index 0000000..6c2ce07
--- /dev/null
@@ -0,0 +1,5 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/subdir2-file1.txt b/tests/zypp/data/Fetcher/remote-site/contentindex-broken-digest/subdir2/subdir2-file1.txt
new file mode 100644 (file)
index 0000000..ce01362
--- /dev/null
@@ -0,0 +1 @@
+hello
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/content b/tests/zypp/data/Fetcher/remote-site/contentindex/content
new file mode 100644 (file)
index 0000000..7a674fa
--- /dev/null
@@ -0,0 +1,3 @@
+HASH SHA1 f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 subdir1/subdir1-file1.txt
+HASH SHA1 e242ed3bffccdf271b7fbaf34ed72d089537b42f subdir1/subdir1-file2.txt
+HASH SHA1 f572d396fae9206628714fb2ce00f72e94f2258f subdir2/subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/content.asc b/tests/zypp/data/Fetcher/remote-site/contentindex/content.asc
new file mode 100644 (file)
index 0000000..30732f1
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkkHLgwACgkQm+zCtd2wN1ZD7QCghDVP3V6Pi/gBMoA6Flc77/4u
+f30Anjf9MqPHx/YZXSFHrYtcst0welTn
+=g7it
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/content.key b/tests/zypp/data/Fetcher/remote-site/contentindex/content.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex/directory.yast
new file mode 100644 (file)
index 0000000..9be1c91
--- /dev/null
@@ -0,0 +1,2 @@
+subdir1/
+subdir2/
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/directory.yast
new file mode 100644 (file)
index 0000000..36efc80
--- /dev/null
@@ -0,0 +1,6 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir1-file1.txt
+subdir1-file2.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file1.txt b/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file1.txt
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file2.txt b/tests/zypp/data/Fetcher/remote-site/contentindex/subdir1/subdir1-file2.txt
new file mode 100644 (file)
index 0000000..5716ca5
--- /dev/null
@@ -0,0 +1 @@
+bar
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/directory.yast b/tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/directory.yast
new file mode 100644 (file)
index 0000000..6c2ce07
--- /dev/null
@@ -0,0 +1,5 @@
+directory.yast
+SHA1SUMS
+SHA1SUMS.asc
+SHA1SUMS.key
+subdir2-file1.txt
diff --git a/tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/subdir2-file1.txt b/tests/zypp/data/Fetcher/remote-site/contentindex/subdir2/subdir2-file1.txt
new file mode 100644 (file)
index 0000000..ce01362
--- /dev/null
@@ -0,0 +1 @@
+hello
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff b/tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff
new file mode 100644 (file)
index 0000000..fb939c8
--- /dev/null
@@ -0,0 +1,7 @@
+20a21,26
+> [edit] Distribution
+> 
+> In the past SUSE first released the Personal and Professional versions in boxed sets which included extensive printed documentation, then waited a few months before it released versions on its FTP servers. Under Novell and with advent of openSUSE this has been reversed: SUSE Linux 10.0 was available for download well before the retail release of SUSE Linux 10.0. In addition, Novell has discontinued the Personal version, renamed the Professional version to simply "SUSE Linux", and repriced "SUSE Linux" to about the same as the obsolete Personal version. Now Novell has also renamed SUSE Linux to openSUSE with version 10.2 of the distro.
+> 
+> Starting with version 9.2, an unsupported 1 DVD ISO image of SUSE Professional was made available for download as well as a bootable LiveDVD evaluation. The FTP server continues to operate and has the advantage of "streamlined" installs: Only downloading packages the user feels they need. The ISO has the advantages of an easy install package, the ability to operate even if the user's network card does not work 'out of the box', and less experience needed (i.e., an inexperienced Linux user may not know whether or not to install a certain package, and the ISO offers several preselected sets of packages). The retail box DVD supports x86, and x86_64 installs, but the included CD-ROMs do not include x86_64 support.
+> 
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff.gz b/tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff.gz
new file mode 100644 (file)
index 0000000..82941ab
Binary files /dev/null and b/tests/zypp/data/Fetcher/remote-site/diffs/file-1-2.diff.gz differ
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff b/tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff
new file mode 100644 (file)
index 0000000..275e632
--- /dev/null
@@ -0,0 +1,36 @@
+14a15,49
+> [edit] Versions
+> Major Versions
+> 1.0  - April 1994
+> 1.0.9        - July 1994
+> 11/94        - November 1994
+> 4/95         - April 1995
+> 8/95         - August 1995
+> 11/95        - November 1995
+> 4.2  - May 1996
+> 4.3  - September 1996
+> 4.4  - May 1997
+> 4.4.1        - February 1997
+> 5.0  - June 1997
+> 5.1  - November 1997
+> 5.2  - 23 March 1998
+> 6.0  - 21 December 1998
+> 6.1  - 7 April 1999
+> 6.2  - 12 August 1999
+> 6.3  - 25 November 1999
+> 6.4  - 27 March 2000
+> 7.0  - 27 September 2000
+> 7.1  - 24 January 2001
+> 7.2  - 15 June 2001
+> 7.3  - 13 October 2001
+> 8.0  - 22 April 2002
+> 8.1  - 30 September 2002
+> 8.2  - 7 April 2003
+> 9.0  - 15 October 2003
+> 9.1  - 23 April 2004
+> 9.2  - 25 October 2004
+> 9.3  - 16 April 2005
+> 10.0         - 6 October 2005
+> 10.1         - 11 May 2006
+> 10.2         - 7 December 2006
+> 
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff.gz b/tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff.gz
new file mode 100644 (file)
index 0000000..b2db7df
Binary files /dev/null and b/tests/zypp/data/Fetcher/remote-site/diffs/file-2-3.diff.gz differ
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff b/tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff
new file mode 100644 (file)
index 0000000..070e5cf
--- /dev/null
@@ -0,0 +1,5 @@
+14a15,18
+> SUSE has support for resizing NTFS partitions during installation which allows it to co-exist with existing Windows 2000 or XP installations. SUSE has the ability to detect and install drivers for many common winmodems shipped with OEM desktop and laptop systems (such modems are designed to use Windows-specific software to operate).
+> 
+> Several desktop environments such as KDE and GNOME and window managers like Window Maker and Blackbox are included, with the YaST2 installer allowing the user to choose a preselection of GNOME, KDE, or no desktop at all. SUSE ships with multimedia software such as K3B (CD/DVD burning), Amarok (audio playback), and Kaffeine (movie playback). It contains OpenOffice.org, and software for reading and/or creating other common document formats such as PDF. Due to patent problems, the distribution lacks codecs for proprietary formats like avi, but these can be installed with packages available on the internet. MP3s are handled in the fully capable graphical media studio Amarok with the Helix engine (part of RealNetworks' RealPlayer), when RealPlayer is installed. This is due to an agreement between Novell and RealNetworks to ship RealPlayer with SUSE as a solution to MP3 patent problems.[citation needed]
+> 
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff.gz b/tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff.gz
new file mode 100644 (file)
index 0000000..f0c5cea
Binary files /dev/null and b/tests/zypp/data/Fetcher/remote-site/diffs/file-3-4.diff.gz differ
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff b/tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff
new file mode 100644 (file)
index 0000000..3b7fad1
--- /dev/null
@@ -0,0 +1,5 @@
+6a7,10
+> On November 4, 2003, Novell announced it would acquire SuSE.[2] The acquisition was finalized in January 2004.[3] J. Philips (Novell's corporate technology strategist for the Asia Pacific region) stated that Novell would not "in the medium term" alter the way in which SUSE continues to be developed.[4] At Novell's annual BrainShare gathering in 2004, all computers ran SUSE Linux for the first time. At this gathering it was also announced that the proprietary SUSE administration program YaST2 would be released into the public under the GPL license.
+> 
+> On August 4, 2005, Novell spokesman and director of public relations Bruce Lowry announced that the development of the SUSE Professional series will become more open and within the community project openSUSE try to reach a wider audience of users and developers. The software, by definition of open source, already had their coding "open," but now the development process will be more "open" than before, allowing developers and users to test the product and help develop it. Previously all development work was done in-house by SUSE, and version 10.0 was the first version that had public beta testing. As part of the change, YaST Online Update server access will be complimentary for SUSE Linux users, and along the lines of most open source distributions, there will both be a free download available on the web and a boxed edition. This change in philosophy led to the release of the SUSE Linux 10.0 release on October 6, 2005 in "OSS" (completely open source), "eval" (has both open source and proprietary applications and is actually a fully featured version) and retail boxed-set editions.
+> 
diff --git a/tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff.gz b/tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff.gz
new file mode 100644 (file)
index 0000000..d54de69
Binary files /dev/null and b/tests/zypp/data/Fetcher/remote-site/diffs/file-4-current.diff.gz differ
diff --git a/tests/zypp/data/Fetcher/remote-site/directory.yast b/tests/zypp/data/Fetcher/remote-site/directory.yast
new file mode 100644 (file)
index 0000000..951afd1
--- /dev/null
@@ -0,0 +1,10 @@
+complexdir/
+complexdir-broken/
+diffs/
+file-1.txt
+file-2.txt
+file-3.txt
+file-4.txt
+file-current.txt
+file-current.txt.asc
+file-current.txt.key
diff --git a/tests/zypp/data/Fetcher/remote-site/file-1.txt b/tests/zypp/data/Fetcher/remote-site/file-1.txt
new file mode 100644 (file)
index 0000000..a3670c4
--- /dev/null
@@ -0,0 +1,64 @@
+History
+
+The SUSE Linux distribution was originally a German translation of Slackware Linux. In mid-1992, Softlanding Linux System (SLS) was founded by Peter MacDonald, and was the first comprehensive distribution to contain elements such as X and TCP/IP. The Slackware distribution (maintained by Patrick Volkerding) was initially based largely on SLS.
+
+S.u.S.E was founded in late 1992 as a UNIX consulting group, which among other things regularly released software packages that included SLS and Slackware, and printed UNIX/Linux manuals. S.u.S.E is an acronym for the German phrase "Software- und System-Entwicklung" ("Software and system development"). There is a rumour that the name is a tribute to the German computer pioneer Konrad Zuse. They released the first CD version of SLS/Slackware in 1994, under the name S.u.S.E Linux 1.0. It later integrated with the Jurix distribution by Florian La Roche, to release the first really unique S.u.S.E Linux 4.2 in 1996. Over time, SuSE Linux incorporated many aspects of Red Hat Linux (e.g., using RPMs and /etc/sysconfig). In a move to more effectively reach its business audience, SuSE introduced the SuSE Linux Enterpriser Server in 2001, and consecutively changed the company name to SUSE Linux in September 2003 as a part of its overall new branding strategy, as announced by SUSE's marketing VP Uwe Schmid.[1]
+
+The current mascot of SuSE is Geeko, also known as the "SuSE Lizard"
+
+[edit] Features
+
+SUSE includes an installation and administration program called YaST2 which handles hard disk partitioning, system setup, RPM package management, online updates, network and firewall configuration, user administration and more in an integrated interface.
+
+Starting with the 10.1 release, SuSE includes a secondary installation program known as Zen-Updater, which can be used as a secondary means of installing software and replaces Suse-updater providing notification of software updates on the desktop.
+
+The latest release, openSUSE 10.2 is available as a retail package and as a no-cost open source package. In terms of software, there are major differences between the two packages (see Reference below), including the fact that the retail edition contains a number of proprietary components, such as Macromedia Flash. In addition, the retail package, available for 59.95 USD, includes a printed manual and limited technical support. openSUSE is available to download freely from their website. The retail and eval versions contain one DVD and six CDs, while openSUSE uses five CDs. It is the first SUSE release to be called openSUSE, previous versions were called SUSE Linux.
+
+Other flavors include dedicated server editions and groupware servers geared towards corporate networks and enterprises, along with a stripped-down business desktop which runs some software designed for Microsoft Windows out of the box by virtue of WINE.
+
+SUSE Linux Enterprise Server (SLES) and SUSE Linux Enterprise Desktop (SLED) are Novell's branded version of SUSE targeted at corporate environments. SUSE Linux Enterprise product line (SLES and SLED) include some proprietary software as well as technical support. For instance, SuSE Linux Enterprise Server 9 (SLES 9) has fewer packages (around 1,000 packages) than the SuSE Linux Professional (consumer) distribution which has around 3,500 packages. Most of the packages that have been removed are desktop applications which are more suited to consumers than to a business environment. SLES has a guaranteed life cycle of 5 years and only the SLES products are certified by independent hardware and software vendors.
+
+[edit] See also
+Portal:Free software
+       Free software Portal
+
+    * Novell
+    * OpenSUSE
+    * Linux on zSeries
+    * List of Linux distributions
+    * Comparison of Linux distributions
+    * Commercial and community Linux distributions by the same vendor
+    * Sax2
+
+[edit] References
+
+   1. ^ Proffitt, B. (2003). SuSE Rebrands Ahead of 9.0 Launch.
+   2. ^ Shankland, S. (2003). Novell to acquire SuSE Linux. Retrieved December 20, 2003.
+   3. ^ Kennedy, D. (2003). Novell's Linux buy opens road to top. Retrieved December 20, 2003.
+   4. ^ Ramesh, R. (2004). Novell: SuSE stays the same, for now. Retrieved January 14, 2004.
+
+    * SuSE Roadmap
+    * Differences between boxed and retail version
+
+[edit] External links
+
+    * openSUSE
+    * Novell SUSE Linux Enterprise 10
+    * suse at DistroWatch
+    * Hacking SUSE Linux
+    * Fultus Technical Documentation eLibrary - SUSE
+    * 10.1 Review (tuxmachines.org)
+    * The Unofficial SUSE FAQ
+    * SUSE Linux Support Forums
+    * SUSE Linux Community Forums
+    * SuseBR Brazilian SUSE Linux Community Forums -
+    * SUSE Wiki
+    * SUSEroot
+    * The Linux Master Forums
+    * 10.0 Review
+    * SUSE Support Knowledgebase
+    * Links about SUSE Linux
+    * SuSE Linux OS Turkiye
+    * Planet SuSE - Blogs of SUSE employees and SUSE community members
+    * Linux Desktop Multiplier - Turn one SLED or openSUSE computer into 10 independent, full-client desktops
+    * Hong Kong & Macau Novell User Group - An unofficial web site provides news, articles and technical tips.
diff --git a/tests/zypp/data/Fetcher/remote-site/file-2.txt b/tests/zypp/data/Fetcher/remote-site/file-2.txt
new file mode 100644 (file)
index 0000000..1272359
--- /dev/null
@@ -0,0 +1,70 @@
+History
+
+The SUSE Linux distribution was originally a German translation of Slackware Linux. In mid-1992, Softlanding Linux System (SLS) was founded by Peter MacDonald, and was the first comprehensive distribution to contain elements such as X and TCP/IP. The Slackware distribution (maintained by Patrick Volkerding) was initially based largely on SLS.
+
+S.u.S.E was founded in late 1992 as a UNIX consulting group, which among other things regularly released software packages that included SLS and Slackware, and printed UNIX/Linux manuals. S.u.S.E is an acronym for the German phrase "Software- und System-Entwicklung" ("Software and system development"). There is a rumour that the name is a tribute to the German computer pioneer Konrad Zuse. They released the first CD version of SLS/Slackware in 1994, under the name S.u.S.E Linux 1.0. It later integrated with the Jurix distribution by Florian La Roche, to release the first really unique S.u.S.E Linux 4.2 in 1996. Over time, SuSE Linux incorporated many aspects of Red Hat Linux (e.g., using RPMs and /etc/sysconfig). In a move to more effectively reach its business audience, SuSE introduced the SuSE Linux Enterpriser Server in 2001, and consecutively changed the company name to SUSE Linux in September 2003 as a part of its overall new branding strategy, as announced by SUSE's marketing VP Uwe Schmid.[1]
+
+The current mascot of SuSE is Geeko, also known as the "SuSE Lizard"
+
+[edit] Features
+
+SUSE includes an installation and administration program called YaST2 which handles hard disk partitioning, system setup, RPM package management, online updates, network and firewall configuration, user administration and more in an integrated interface.
+
+Starting with the 10.1 release, SuSE includes a secondary installation program known as Zen-Updater, which can be used as a secondary means of installing software and replaces Suse-updater providing notification of software updates on the desktop.
+
+The latest release, openSUSE 10.2 is available as a retail package and as a no-cost open source package. In terms of software, there are major differences between the two packages (see Reference below), including the fact that the retail edition contains a number of proprietary components, such as Macromedia Flash. In addition, the retail package, available for 59.95 USD, includes a printed manual and limited technical support. openSUSE is available to download freely from their website. The retail and eval versions contain one DVD and six CDs, while openSUSE uses five CDs. It is the first SUSE release to be called openSUSE, previous versions were called SUSE Linux.
+
+Other flavors include dedicated server editions and groupware servers geared towards corporate networks and enterprises, along with a stripped-down business desktop which runs some software designed for Microsoft Windows out of the box by virtue of WINE.
+
+SUSE Linux Enterprise Server (SLES) and SUSE Linux Enterprise Desktop (SLED) are Novell's branded version of SUSE targeted at corporate environments. SUSE Linux Enterprise product line (SLES and SLED) include some proprietary software as well as technical support. For instance, SuSE Linux Enterprise Server 9 (SLES 9) has fewer packages (around 1,000 packages) than the SuSE Linux Professional (consumer) distribution which has around 3,500 packages. Most of the packages that have been removed are desktop applications which are more suited to consumers than to a business environment. SLES has a guaranteed life cycle of 5 years and only the SLES products are certified by independent hardware and software vendors.
+
+[edit] Distribution
+
+In the past SUSE first released the Personal and Professional versions in boxed sets which included extensive printed documentation, then waited a few months before it released versions on its FTP servers. Under Novell and with advent of openSUSE this has been reversed: SUSE Linux 10.0 was available for download well before the retail release of SUSE Linux 10.0. In addition, Novell has discontinued the Personal version, renamed the Professional version to simply "SUSE Linux", and repriced "SUSE Linux" to about the same as the obsolete Personal version. Now Novell has also renamed SUSE Linux to openSUSE with version 10.2 of the distro.
+
+Starting with version 9.2, an unsupported 1 DVD ISO image of SUSE Professional was made available for download as well as a bootable LiveDVD evaluation. The FTP server continues to operate and has the advantage of "streamlined" installs: Only downloading packages the user feels they need. The ISO has the advantages of an easy install package, the ability to operate even if the user's network card does not work 'out of the box', and less experience needed (i.e., an inexperienced Linux user may not know whether or not to install a certain package, and the ISO offers several preselected sets of packages). The retail box DVD supports x86, and x86_64 installs, but the included CD-ROMs do not include x86_64 support.
+
+[edit] See also
+Portal:Free software
+       Free software Portal
+
+    * Novell
+    * OpenSUSE
+    * Linux on zSeries
+    * List of Linux distributions
+    * Comparison of Linux distributions
+    * Commercial and community Linux distributions by the same vendor
+    * Sax2
+
+[edit] References
+
+   1. ^ Proffitt, B. (2003). SuSE Rebrands Ahead of 9.0 Launch.
+   2. ^ Shankland, S. (2003). Novell to acquire SuSE Linux. Retrieved December 20, 2003.
+   3. ^ Kennedy, D. (2003). Novell's Linux buy opens road to top. Retrieved December 20, 2003.
+   4. ^ Ramesh, R. (2004). Novell: SuSE stays the same, for now. Retrieved January 14, 2004.
+
+    * SuSE Roadmap
+    * Differences between boxed and retail version
+
+[edit] External links
+
+    * openSUSE
+    * Novell SUSE Linux Enterprise 10
+    * suse at DistroWatch
+    * Hacking SUSE Linux
+    * Fultus Technical Documentation eLibrary - SUSE
+    * 10.1 Review (tuxmachines.org)
+    * The Unofficial SUSE FAQ
+    * SUSE Linux Support Forums
+    * SUSE Linux Community Forums
+    * SuseBR Brazilian SUSE Linux Community Forums -
+    * SUSE Wiki
+    * SUSEroot
+    * The Linux Master Forums
+    * 10.0 Review
+    * SUSE Support Knowledgebase
+    * Links about SUSE Linux
+    * SuSE Linux OS Turkiye
+    * Planet SuSE - Blogs of SUSE employees and SUSE community members
+    * Linux Desktop Multiplier - Turn one SLED or openSUSE computer into 10 independent, full-client desktops
+    * Hong Kong & Macau Novell User Group - An unofficial web site provides news, articles and technical tips.
diff --git a/tests/zypp/data/Fetcher/remote-site/file-3.txt b/tests/zypp/data/Fetcher/remote-site/file-3.txt
new file mode 100644 (file)
index 0000000..3ce92b3
--- /dev/null
@@ -0,0 +1,105 @@
+History
+
+The SUSE Linux distribution was originally a German translation of Slackware Linux. In mid-1992, Softlanding Linux System (SLS) was founded by Peter MacDonald, and was the first comprehensive distribution to contain elements such as X and TCP/IP. The Slackware distribution (maintained by Patrick Volkerding) was initially based largely on SLS.
+
+S.u.S.E was founded in late 1992 as a UNIX consulting group, which among other things regularly released software packages that included SLS and Slackware, and printed UNIX/Linux manuals. S.u.S.E is an acronym for the German phrase "Software- und System-Entwicklung" ("Software and system development"). There is a rumour that the name is a tribute to the German computer pioneer Konrad Zuse. They released the first CD version of SLS/Slackware in 1994, under the name S.u.S.E Linux 1.0. It later integrated with the Jurix distribution by Florian La Roche, to release the first really unique S.u.S.E Linux 4.2 in 1996. Over time, SuSE Linux incorporated many aspects of Red Hat Linux (e.g., using RPMs and /etc/sysconfig). In a move to more effectively reach its business audience, SuSE introduced the SuSE Linux Enterpriser Server in 2001, and consecutively changed the company name to SUSE Linux in September 2003 as a part of its overall new branding strategy, as announced by SUSE's marketing VP Uwe Schmid.[1]
+
+The current mascot of SuSE is Geeko, also known as the "SuSE Lizard"
+
+[edit] Features
+
+SUSE includes an installation and administration program called YaST2 which handles hard disk partitioning, system setup, RPM package management, online updates, network and firewall configuration, user administration and more in an integrated interface.
+
+Starting with the 10.1 release, SuSE includes a secondary installation program known as Zen-Updater, which can be used as a secondary means of installing software and replaces Suse-updater providing notification of software updates on the desktop.
+
+[edit] Versions
+Major Versions
+1.0    - April 1994
+1.0.9  - July 1994
+11/94  - November 1994
+4/95   - April 1995
+8/95   - August 1995
+11/95  - November 1995
+4.2    - May 1996
+4.3    - September 1996
+4.4    - May 1997
+4.4.1  - February 1997
+5.0    - June 1997
+5.1    - November 1997
+5.2    - 23 March 1998
+6.0    - 21 December 1998
+6.1    - 7 April 1999
+6.2    - 12 August 1999
+6.3    - 25 November 1999
+6.4    - 27 March 2000
+7.0    - 27 September 2000
+7.1    - 24 January 2001
+7.2    - 15 June 2001
+7.3    - 13 October 2001
+8.0    - 22 April 2002
+8.1    - 30 September 2002
+8.2    - 7 April 2003
+9.0    - 15 October 2003
+9.1    - 23 April 2004
+9.2    - 25 October 2004
+9.3    - 16 April 2005
+10.0   - 6 October 2005
+10.1   - 11 May 2006
+10.2   - 7 December 2006
+
+The latest release, openSUSE 10.2 is available as a retail package and as a no-cost open source package. In terms of software, there are major differences between the two packages (see Reference below), including the fact that the retail edition contains a number of proprietary components, such as Macromedia Flash. In addition, the retail package, available for 59.95 USD, includes a printed manual and limited technical support. openSUSE is available to download freely from their website. The retail and eval versions contain one DVD and six CDs, while openSUSE uses five CDs. It is the first SUSE release to be called openSUSE, previous versions were called SUSE Linux.
+
+Other flavors include dedicated server editions and groupware servers geared towards corporate networks and enterprises, along with a stripped-down business desktop which runs some software designed for Microsoft Windows out of the box by virtue of WINE.
+
+SUSE Linux Enterprise Server (SLES) and SUSE Linux Enterprise Desktop (SLED) are Novell's branded version of SUSE targeted at corporate environments. SUSE Linux Enterprise product line (SLES and SLED) include some proprietary software as well as technical support. For instance, SuSE Linux Enterprise Server 9 (SLES 9) has fewer packages (around 1,000 packages) than the SuSE Linux Professional (consumer) distribution which has around 3,500 packages. Most of the packages that have been removed are desktop applications which are more suited to consumers than to a business environment. SLES has a guaranteed life cycle of 5 years and only the SLES products are certified by independent hardware and software vendors.
+
+[edit] Distribution
+
+In the past SUSE first released the Personal and Professional versions in boxed sets which included extensive printed documentation, then waited a few months before it released versions on its FTP servers. Under Novell and with advent of openSUSE this has been reversed: SUSE Linux 10.0 was available for download well before the retail release of SUSE Linux 10.0. In addition, Novell has discontinued the Personal version, renamed the Professional version to simply "SUSE Linux", and repriced "SUSE Linux" to about the same as the obsolete Personal version. Now Novell has also renamed SUSE Linux to openSUSE with version 10.2 of the distro.
+
+Starting with version 9.2, an unsupported 1 DVD ISO image of SUSE Professional was made available for download as well as a bootable LiveDVD evaluation. The FTP server continues to operate and has the advantage of "streamlined" installs: Only downloading packages the user feels they need. The ISO has the advantages of an easy install package, the ability to operate even if the user's network card does not work 'out of the box', and less experience needed (i.e., an inexperienced Linux user may not know whether or not to install a certain package, and the ISO offers several preselected sets of packages). The retail box DVD supports x86, and x86_64 installs, but the included CD-ROMs do not include x86_64 support.
+
+[edit] See also
+Portal:Free software
+       Free software Portal
+
+    * Novell
+    * OpenSUSE
+    * Linux on zSeries
+    * List of Linux distributions
+    * Comparison of Linux distributions
+    * Commercial and community Linux distributions by the same vendor
+    * Sax2
+
+[edit] References
+
+   1. ^ Proffitt, B. (2003). SuSE Rebrands Ahead of 9.0 Launch.
+   2. ^ Shankland, S. (2003). Novell to acquire SuSE Linux. Retrieved December 20, 2003.
+   3. ^ Kennedy, D. (2003). Novell's Linux buy opens road to top. Retrieved December 20, 2003.
+   4. ^ Ramesh, R. (2004). Novell: SuSE stays the same, for now. Retrieved January 14, 2004.
+
+    * SuSE Roadmap
+    * Differences between boxed and retail version
+
+[edit] External links
+
+    * openSUSE
+    * Novell SUSE Linux Enterprise 10
+    * suse at DistroWatch
+    * Hacking SUSE Linux
+    * Fultus Technical Documentation eLibrary - SUSE
+    * 10.1 Review (tuxmachines.org)
+    * The Unofficial SUSE FAQ
+    * SUSE Linux Support Forums
+    * SUSE Linux Community Forums
+    * SuseBR Brazilian SUSE Linux Community Forums -
+    * SUSE Wiki
+    * SUSEroot
+    * The Linux Master Forums
+    * 10.0 Review
+    * SUSE Support Knowledgebase
+    * Links about SUSE Linux
+    * SuSE Linux OS Turkiye
+    * Planet SuSE - Blogs of SUSE employees and SUSE community members
+    * Linux Desktop Multiplier - Turn one SLED or openSUSE computer into 10 independent, full-client desktops
+    * Hong Kong & Macau Novell User Group - An unofficial web site provides news, articles and technical tips.
diff --git a/tests/zypp/data/Fetcher/remote-site/file-4.txt b/tests/zypp/data/Fetcher/remote-site/file-4.txt
new file mode 100644 (file)
index 0000000..e031d57
--- /dev/null
@@ -0,0 +1,109 @@
+History
+
+The SUSE Linux distribution was originally a German translation of Slackware Linux. In mid-1992, Softlanding Linux System (SLS) was founded by Peter MacDonald, and was the first comprehensive distribution to contain elements such as X and TCP/IP. The Slackware distribution (maintained by Patrick Volkerding) was initially based largely on SLS.
+
+S.u.S.E was founded in late 1992 as a UNIX consulting group, which among other things regularly released software packages that included SLS and Slackware, and printed UNIX/Linux manuals. S.u.S.E is an acronym for the German phrase "Software- und System-Entwicklung" ("Software and system development"). There is a rumour that the name is a tribute to the German computer pioneer Konrad Zuse. They released the first CD version of SLS/Slackware in 1994, under the name S.u.S.E Linux 1.0. It later integrated with the Jurix distribution by Florian La Roche, to release the first really unique S.u.S.E Linux 4.2 in 1996. Over time, SuSE Linux incorporated many aspects of Red Hat Linux (e.g., using RPMs and /etc/sysconfig). In a move to more effectively reach its business audience, SuSE introduced the SuSE Linux Enterpriser Server in 2001, and consecutively changed the company name to SUSE Linux in September 2003 as a part of its overall new branding strategy, as announced by SUSE's marketing VP Uwe Schmid.[1]
+
+The current mascot of SuSE is Geeko, also known as the "SuSE Lizard"
+
+[edit] Features
+
+SUSE includes an installation and administration program called YaST2 which handles hard disk partitioning, system setup, RPM package management, online updates, network and firewall configuration, user administration and more in an integrated interface.
+
+Starting with the 10.1 release, SuSE includes a secondary installation program known as Zen-Updater, which can be used as a secondary means of installing software and replaces Suse-updater providing notification of software updates on the desktop.
+
+SUSE has support for resizing NTFS partitions during installation which allows it to co-exist with existing Windows 2000 or XP installations. SUSE has the ability to detect and install drivers for many common winmodems shipped with OEM desktop and laptop systems (such modems are designed to use Windows-specific software to operate).
+
+Several desktop environments such as KDE and GNOME and window managers like Window Maker and Blackbox are included, with the YaST2 installer allowing the user to choose a preselection of GNOME, KDE, or no desktop at all. SUSE ships with multimedia software such as K3B (CD/DVD burning), Amarok (audio playback), and Kaffeine (movie playback). It contains OpenOffice.org, and software for reading and/or creating other common document formats such as PDF. Due to patent problems, the distribution lacks codecs for proprietary formats like avi, but these can be installed with packages available on the internet. MP3s are handled in the fully capable graphical media studio Amarok with the Helix engine (part of RealNetworks' RealPlayer), when RealPlayer is installed. This is due to an agreement between Novell and RealNetworks to ship RealPlayer with SUSE as a solution to MP3 patent problems.[citation needed]
+
+[edit] Versions
+Major Versions
+1.0    - April 1994
+1.0.9  - July 1994
+11/94  - November 1994
+4/95   - April 1995
+8/95   - August 1995
+11/95  - November 1995
+4.2    - May 1996
+4.3    - September 1996
+4.4    - May 1997
+4.4.1  - February 1997
+5.0    - June 1997
+5.1    - November 1997
+5.2    - 23 March 1998
+6.0    - 21 December 1998
+6.1    - 7 April 1999
+6.2    - 12 August 1999
+6.3    - 25 November 1999
+6.4    - 27 March 2000
+7.0    - 27 September 2000
+7.1    - 24 January 2001
+7.2    - 15 June 2001
+7.3    - 13 October 2001
+8.0    - 22 April 2002
+8.1    - 30 September 2002
+8.2    - 7 April 2003
+9.0    - 15 October 2003
+9.1    - 23 April 2004
+9.2    - 25 October 2004
+9.3    - 16 April 2005
+10.0   - 6 October 2005
+10.1   - 11 May 2006
+10.2   - 7 December 2006
+
+The latest release, openSUSE 10.2 is available as a retail package and as a no-cost open source package. In terms of software, there are major differences between the two packages (see Reference below), including the fact that the retail edition contains a number of proprietary components, such as Macromedia Flash. In addition, the retail package, available for 59.95 USD, includes a printed manual and limited technical support. openSUSE is available to download freely from their website. The retail and eval versions contain one DVD and six CDs, while openSUSE uses five CDs. It is the first SUSE release to be called openSUSE, previous versions were called SUSE Linux.
+
+Other flavors include dedicated server editions and groupware servers geared towards corporate networks and enterprises, along with a stripped-down business desktop which runs some software designed for Microsoft Windows out of the box by virtue of WINE.
+
+SUSE Linux Enterprise Server (SLES) and SUSE Linux Enterprise Desktop (SLED) are Novell's branded version of SUSE targeted at corporate environments. SUSE Linux Enterprise product line (SLES and SLED) include some proprietary software as well as technical support. For instance, SuSE Linux Enterprise Server 9 (SLES 9) has fewer packages (around 1,000 packages) than the SuSE Linux Professional (consumer) distribution which has around 3,500 packages. Most of the packages that have been removed are desktop applications which are more suited to consumers than to a business environment. SLES has a guaranteed life cycle of 5 years and only the SLES products are certified by independent hardware and software vendors.
+
+[edit] Distribution
+
+In the past SUSE first released the Personal and Professional versions in boxed sets which included extensive printed documentation, then waited a few months before it released versions on its FTP servers. Under Novell and with advent of openSUSE this has been reversed: SUSE Linux 10.0 was available for download well before the retail release of SUSE Linux 10.0. In addition, Novell has discontinued the Personal version, renamed the Professional version to simply "SUSE Linux", and repriced "SUSE Linux" to about the same as the obsolete Personal version. Now Novell has also renamed SUSE Linux to openSUSE with version 10.2 of the distro.
+
+Starting with version 9.2, an unsupported 1 DVD ISO image of SUSE Professional was made available for download as well as a bootable LiveDVD evaluation. The FTP server continues to operate and has the advantage of "streamlined" installs: Only downloading packages the user feels they need. The ISO has the advantages of an easy install package, the ability to operate even if the user's network card does not work 'out of the box', and less experience needed (i.e., an inexperienced Linux user may not know whether or not to install a certain package, and the ISO offers several preselected sets of packages). The retail box DVD supports x86, and x86_64 installs, but the included CD-ROMs do not include x86_64 support.
+
+[edit] See also
+Portal:Free software
+       Free software Portal
+
+    * Novell
+    * OpenSUSE
+    * Linux on zSeries
+    * List of Linux distributions
+    * Comparison of Linux distributions
+    * Commercial and community Linux distributions by the same vendor
+    * Sax2
+
+[edit] References
+
+   1. ^ Proffitt, B. (2003). SuSE Rebrands Ahead of 9.0 Launch.
+   2. ^ Shankland, S. (2003). Novell to acquire SuSE Linux. Retrieved December 20, 2003.
+   3. ^ Kennedy, D. (2003). Novell's Linux buy opens road to top. Retrieved December 20, 2003.
+   4. ^ Ramesh, R. (2004). Novell: SuSE stays the same, for now. Retrieved January 14, 2004.
+
+    * SuSE Roadmap
+    * Differences between boxed and retail version
+
+[edit] External links
+
+    * openSUSE
+    * Novell SUSE Linux Enterprise 10
+    * suse at DistroWatch
+    * Hacking SUSE Linux
+    * Fultus Technical Documentation eLibrary - SUSE
+    * 10.1 Review (tuxmachines.org)
+    * The Unofficial SUSE FAQ
+    * SUSE Linux Support Forums
+    * SUSE Linux Community Forums
+    * SuseBR Brazilian SUSE Linux Community Forums -
+    * SUSE Wiki
+    * SUSEroot
+    * The Linux Master Forums
+    * 10.0 Review
+    * SUSE Support Knowledgebase
+    * Links about SUSE Linux
+    * SuSE Linux OS Turkiye
+    * Planet SuSE - Blogs of SUSE employees and SUSE community members
+    * Linux Desktop Multiplier - Turn one SLED or openSUSE computer into 10 independent, full-client desktops
+    * Hong Kong & Macau Novell User Group - An unofficial web site provides news, articles and technical tips.
diff --git a/tests/zypp/data/Fetcher/remote-site/file-current.txt b/tests/zypp/data/Fetcher/remote-site/file-current.txt
new file mode 100644 (file)
index 0000000..0358edd
--- /dev/null
@@ -0,0 +1,113 @@
+History
+
+The SUSE Linux distribution was originally a German translation of Slackware Linux. In mid-1992, Softlanding Linux System (SLS) was founded by Peter MacDonald, and was the first comprehensive distribution to contain elements such as X and TCP/IP. The Slackware distribution (maintained by Patrick Volkerding) was initially based largely on SLS.
+
+S.u.S.E was founded in late 1992 as a UNIX consulting group, which among other things regularly released software packages that included SLS and Slackware, and printed UNIX/Linux manuals. S.u.S.E is an acronym for the German phrase "Software- und System-Entwicklung" ("Software and system development"). There is a rumour that the name is a tribute to the German computer pioneer Konrad Zuse. They released the first CD version of SLS/Slackware in 1994, under the name S.u.S.E Linux 1.0. It later integrated with the Jurix distribution by Florian La Roche, to release the first really unique S.u.S.E Linux 4.2 in 1996. Over time, SuSE Linux incorporated many aspects of Red Hat Linux (e.g., using RPMs and /etc/sysconfig). In a move to more effectively reach its business audience, SuSE introduced the SuSE Linux Enterpriser Server in 2001, and consecutively changed the company name to SUSE Linux in September 2003 as a part of its overall new branding strategy, as announced by SUSE's marketing VP Uwe Schmid.[1]
+
+On November 4, 2003, Novell announced it would acquire SuSE.[2] The acquisition was finalized in January 2004.[3] J. Philips (Novell's corporate technology strategist for the Asia Pacific region) stated that Novell would not "in the medium term" alter the way in which SUSE continues to be developed.[4] At Novell's annual BrainShare gathering in 2004, all computers ran SUSE Linux for the first time. At this gathering it was also announced that the proprietary SUSE administration program YaST2 would be released into the public under the GPL license.
+
+On August 4, 2005, Novell spokesman and director of public relations Bruce Lowry announced that the development of the SUSE Professional series will become more open and within the community project openSUSE try to reach a wider audience of users and developers. The software, by definition of open source, already had their coding "open," but now the development process will be more "open" than before, allowing developers and users to test the product and help develop it. Previously all development work was done in-house by SUSE, and version 10.0 was the first version that had public beta testing. As part of the change, YaST Online Update server access will be complimentary for SUSE Linux users, and along the lines of most open source distributions, there will both be a free download available on the web and a boxed edition. This change in philosophy led to the release of the SUSE Linux 10.0 release on October 6, 2005 in "OSS" (completely open source), "eval" (has both open source and proprietary applications and is actually a fully featured version) and retail boxed-set editions.
+
+The current mascot of SuSE is Geeko, also known as the "SuSE Lizard"
+
+[edit] Features
+
+SUSE includes an installation and administration program called YaST2 which handles hard disk partitioning, system setup, RPM package management, online updates, network and firewall configuration, user administration and more in an integrated interface.
+
+Starting with the 10.1 release, SuSE includes a secondary installation program known as Zen-Updater, which can be used as a secondary means of installing software and replaces Suse-updater providing notification of software updates on the desktop.
+
+SUSE has support for resizing NTFS partitions during installation which allows it to co-exist with existing Windows 2000 or XP installations. SUSE has the ability to detect and install drivers for many common winmodems shipped with OEM desktop and laptop systems (such modems are designed to use Windows-specific software to operate).
+
+Several desktop environments such as KDE and GNOME and window managers like Window Maker and Blackbox are included, with the YaST2 installer allowing the user to choose a preselection of GNOME, KDE, or no desktop at all. SUSE ships with multimedia software such as K3B (CD/DVD burning), Amarok (audio playback), and Kaffeine (movie playback). It contains OpenOffice.org, and software for reading and/or creating other common document formats such as PDF. Due to patent problems, the distribution lacks codecs for proprietary formats like avi, but these can be installed with packages available on the internet. MP3s are handled in the fully capable graphical media studio Amarok with the Helix engine (part of RealNetworks' RealPlayer), when RealPlayer is installed. This is due to an agreement between Novell and RealNetworks to ship RealPlayer with SUSE as a solution to MP3 patent problems.[citation needed]
+
+[edit] Versions
+Major Versions
+1.0    - April 1994
+1.0.9  - July 1994
+11/94  - November 1994
+4/95   - April 1995
+8/95   - August 1995
+11/95  - November 1995
+4.2    - May 1996
+4.3    - September 1996
+4.4    - May 1997
+4.4.1  - February 1997
+5.0    - June 1997
+5.1    - November 1997
+5.2    - 23 March 1998
+6.0    - 21 December 1998
+6.1    - 7 April 1999
+6.2    - 12 August 1999
+6.3    - 25 November 1999
+6.4    - 27 March 2000
+7.0    - 27 September 2000
+7.1    - 24 January 2001
+7.2    - 15 June 2001
+7.3    - 13 October 2001
+8.0    - 22 April 2002
+8.1    - 30 September 2002
+8.2    - 7 April 2003
+9.0    - 15 October 2003
+9.1    - 23 April 2004
+9.2    - 25 October 2004
+9.3    - 16 April 2005
+10.0   - 6 October 2005
+10.1   - 11 May 2006
+10.2   - 7 December 2006
+
+The latest release, openSUSE 10.2 is available as a retail package and as a no-cost open source package. In terms of software, there are major differences between the two packages (see Reference below), including the fact that the retail edition contains a number of proprietary components, such as Macromedia Flash. In addition, the retail package, available for 59.95 USD, includes a printed manual and limited technical support. openSUSE is available to download freely from their website. The retail and eval versions contain one DVD and six CDs, while openSUSE uses five CDs. It is the first SUSE release to be called openSUSE, previous versions were called SUSE Linux.
+
+Other flavors include dedicated server editions and groupware servers geared towards corporate networks and enterprises, along with a stripped-down business desktop which runs some software designed for Microsoft Windows out of the box by virtue of WINE.
+
+SUSE Linux Enterprise Server (SLES) and SUSE Linux Enterprise Desktop (SLED) are Novell's branded version of SUSE targeted at corporate environments. SUSE Linux Enterprise product line (SLES and SLED) include some proprietary software as well as technical support. For instance, SuSE Linux Enterprise Server 9 (SLES 9) has fewer packages (around 1,000 packages) than the SuSE Linux Professional (consumer) distribution which has around 3,500 packages. Most of the packages that have been removed are desktop applications which are more suited to consumers than to a business environment. SLES has a guaranteed life cycle of 5 years and only the SLES products are certified by independent hardware and software vendors.
+
+[edit] Distribution
+
+In the past SUSE first released the Personal and Professional versions in boxed sets which included extensive printed documentation, then waited a few months before it released versions on its FTP servers. Under Novell and with advent of openSUSE this has been reversed: SUSE Linux 10.0 was available for download well before the retail release of SUSE Linux 10.0. In addition, Novell has discontinued the Personal version, renamed the Professional version to simply "SUSE Linux", and repriced "SUSE Linux" to about the same as the obsolete Personal version. Now Novell has also renamed SUSE Linux to openSUSE with version 10.2 of the distro.
+
+Starting with version 9.2, an unsupported 1 DVD ISO image of SUSE Professional was made available for download as well as a bootable LiveDVD evaluation. The FTP server continues to operate and has the advantage of "streamlined" installs: Only downloading packages the user feels they need. The ISO has the advantages of an easy install package, the ability to operate even if the user's network card does not work 'out of the box', and less experience needed (i.e., an inexperienced Linux user may not know whether or not to install a certain package, and the ISO offers several preselected sets of packages). The retail box DVD supports x86, and x86_64 installs, but the included CD-ROMs do not include x86_64 support.
+
+[edit] See also
+Portal:Free software
+       Free software Portal
+
+    * Novell
+    * OpenSUSE
+    * Linux on zSeries
+    * List of Linux distributions
+    * Comparison of Linux distributions
+    * Commercial and community Linux distributions by the same vendor
+    * Sax2
+
+[edit] References
+
+   1. ^ Proffitt, B. (2003). SuSE Rebrands Ahead of 9.0 Launch.
+   2. ^ Shankland, S. (2003). Novell to acquire SuSE Linux. Retrieved December 20, 2003.
+   3. ^ Kennedy, D. (2003). Novell's Linux buy opens road to top. Retrieved December 20, 2003.
+   4. ^ Ramesh, R. (2004). Novell: SuSE stays the same, for now. Retrieved January 14, 2004.
+
+    * SuSE Roadmap
+    * Differences between boxed and retail version
+
+[edit] External links
+
+    * openSUSE
+    * Novell SUSE Linux Enterprise 10
+    * suse at DistroWatch
+    * Hacking SUSE Linux
+    * Fultus Technical Documentation eLibrary - SUSE
+    * 10.1 Review (tuxmachines.org)
+    * The Unofficial SUSE FAQ
+    * SUSE Linux Support Forums
+    * SUSE Linux Community Forums
+    * SuseBR Brazilian SUSE Linux Community Forums -
+    * SUSE Wiki
+    * SUSEroot
+    * The Linux Master Forums
+    * 10.0 Review
+    * SUSE Support Knowledgebase
+    * Links about SUSE Linux
+    * SuSE Linux OS Turkiye
+    * Planet SuSE - Blogs of SUSE employees and SUSE community members
+    * Linux Desktop Multiplier - Turn one SLED or openSUSE computer into 10 independent, full-client desktops
+    * Hong Kong & Macau Novell User Group - An unofficial web site provides news, articles and technical tips.
diff --git a/tests/zypp/data/Fetcher/remote-site/file-current.txt.asc b/tests/zypp/data/Fetcher/remote-site/file-current.txt.asc
new file mode 100644 (file)
index 0000000..1550f57
--- /dev/null
@@ -0,0 +1,98 @@
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+owGFWbtvJMl5X0vw4wYQYKW2cCgw0JLGTJMzfOzxEmO5Q94+yCXNWd6etFgbNd01
+MyX2Y1TVzeFsIMCZAxtw4MiAIQdOFChxIBhOHStQYDixAwX+G5z693316B7unn3B
+Lae6+ns/ft/Xf/u97z76zvd//fmvfjH72clvfueXj/95+ocznatB2hijyjqp7+uz
+z3/+2+fa1pVZ93pvFkpMbian4lyXzb3IcG70tKl1VYqVtKIyeq5LmedrIcVXyhSy
+FLWRpc0l36lmYpLL9HYljXI0EvGiFIXOBsPj41FfTKpZncsy0+Xc85isba0KsT05
+n+wwj1nVlJnKxHQtrlStjLiQ6bgC06wv8CbfqSHnTBtbi7QqlkYtVGn1ndoUuK7w
+tKylLoXKVQF9rbBNuhAg8A2TevPsavfFVSJY7Sj3BpHtAu8TDS+RxKP0Vnxd5bfK
+kBpOaF3qWrNdptLiai7NXOEXKECxpNebJE0ySU43NIRgsJsSZBoSSoqb1y++IaFt
+k9dkormpmmVfrBaaxC4qHFXQ3cAAeGyFUfMGrMDIQEXmbGFhVmMJfeRckbFkDV5p
+3hBTiMO6R32dVZcGauIxSbDrPAPnNjK3iQiya4hYCpmaqlwX0MKwH3wYLBcG/MXW
+xPMfiIa4sHcHp2W9gtnyppxvie14hzlbFwCZulN5tSQ3be2wS/CcOArTFFVjnBrE
+sJSFf+LcpMjTHUkoJBoKnCUcqPDvq6o0MhM/bqxiwh1rtYH0bCzulLEhis8nu21E
+wFHw0UGfVFKmFSIYxtlrmOwh2mv2qRFkzrmRZNOVrhf80svG6AdZhZg6y5FWkPtc
+iusqXcAh0MdL2BHQKI6vptQ/bR6yPkhGXsijRFzekYy6AKFJE3MZAVCZZeUkgpmQ
+wXapUuQE1L3G2XOY113dVsk8gbKWQvD66sKyn3ZVne7CWQjPmZ7vcGJLUVR3bP+i
+gp3UbAaKSEQOSImY1aA/JULKgkqTaVWmQS5YyFRZk3o3dGRFuCiDiLRQZKLMHZtT
+jPb2hi5YKUNU2nhO6UKWc0+EfE+6sXsgVqeYgcJELRFqU5ADrX2XcktpajIBSQpd
+DIwsSrUSU+PLFJwFm83Xfb5flkje1BUDIv7YwpjmVnG6fn0lblbQJF2g4iXvhu97
+vctSvAZZZooAIr59PgGblpiuxapq8gzJ9dNGG2eM5N3oPZcmPrQ6VuEZlWD9wVWQ
+l5SlZk2UD5J3++/Fy0RcLXSul1ZsO0aQMfpe1CpdlFVezddBM4RjTOanVksUuVTP
+dErVBSx3cI+DhhPQi+6kLatabEEGerNQmW4KkDfFlpB57fNkJdckpatg7A2qyXAI
+FaZKTFVIfAWDHbwXTwOLx2xsFCBxYlB+JwtKxLmk6kemdvEAi5K/QsajICKPOj4P
+arkMopxIiAOqp+3SqtmsqHVVxyex3ixNhVhUNVmZacusQL1n65FL8ByJXogfycmb
+kbcM9Io1BnHu6tOymeYwa1tEvro6FzhB81IJh8rTZt5AUBcohzFQ7LK6VZZqG0V/
+hgBJ0a8paj1J8GJZLIyFhBLn1QrCfkKXTpWl1+vQ769MNUOKauqzAmmn4Z6VBuup
+gnGVS284yUlABc27HU8LlKR6TWb4CeTiW0yzhghcyagQSLxEaocaQNxRj42rLSEG
+jHXNODSxPqVZpmbcXl1lZiEsGgLVEZmDerYWC8nprw3k4aTdomv9LdSeGlG6+kh3
+CJtSSfI6Ov34pS0yFuqyQuwwh7xaEcVWRJbYyU6OVTaGCYpZzU8XKl+GNxBeyEij
+7nTVWAJO4NgVZVWZW46/DN0KwTJY4J4K9cUVvNCZhnvJ3gMAFB6xi8kOPiSmCFgW
+DsIj6G0sdOw1Lpl9DllxWeaoz+JmmVF9sK7eynTDPpRhuSZ5KQsorTpZxrZwgsqc
+MAqxIJrcW4rK1l2vbfQ/vFZzp3eMAG6ImxQzo3CxWpV5BZXkndS5nOZKVC7qVmrq
+2OGVe8Q3ag9Ro+BBZjvtqEQsUQgrWy0Xa5FTGrg8DL21mwC+hZN94+NSXCLPqHIf
+uYQkkluXkwkwDBsE8JQwXqvbTl9sqTuZ48ICXmJ9uqo7pNWWE7mEVVOfuvSQYE1a
+Nx5hzxr6d6Zk3RgVo2CHbxpQ0LnTf2BVHWxgEwfjPcRHd0LHZse7pmsBk9Rt1Xfl
+7hbJUQofUFu+BX+QJtvq9d4RxffizLG3wLA33LUZSDIU1CVaQ+6xP/vj05UxxSXI
+7wsktwK4KMtBBWWdKpq95fhkDRCw/YAKoRgBYICQgGcJu+AfCsU+XMSh23DoIpZK
+VXM6kSxID7Vy3YEAS+Nk6nO0PhSU7nMJ0KXTK2I3+tPMZEoFelKTiIjvCOgQL8MQ
+LxHVBPtAeLDOyM8bhgpWibb/sSoHLvtMwPoplyCSNXMwpaVVKMxbjFgcUcYoXUBt
+1BLIFRJM8PrA2cYQ1zvNtRFdm9p7nNjiy96MIcmgxG1dLRPveApo2ywBIxxcQEjo
+D0Tv9ZuzSes91LHGd+iOzn6CoWJqqd/ybDZQ94Q92Jr8J732VpcZXUK+7WHeFN9c
+bVCicSRIQ0LKKaBOzY0mQzr6+uvfEJnRlDQsL2Nealckji6LKlMFNFro5TJg9MvT
+i6A1k8nlkv50sQg4xdOjf5EnRZhgXrrCQjXbyz4gYM0IKpoWF6h3wLw7ZE/FQDPy
+UuWdxlC1OaK+Gp+yEF+9vrw49W2XyPsEgFa5vg08MSTfUljj1gmNLSgLwk0ubuzr
+tzHrstBbiN4JHY4ecnKQcxZVZakQY7i2iO80BAtL0yfh+uSdsmoNVhMp7x4yq3U8
+C5plCR3K1hxRx/0Tsf1svDv+eoxWbSj1UUKfAlFXt2KbsEIlEMzrKVTacR3mlcSQ
+QUm/jdFDq/Yxz15+4LfiElX3cgYfqKQyc/dqZO/CV3I24MEufqb4zfHn5msfJ1mV
+Ntyk8UYhO865Gp8lYtywY5fwqoMU6FCF62ibQx55hDB4plIXi90eEEizN+Wd7jNo
+AQ3Y35eB4CwfpnGy/6gvcrVCEUzExdW+i1FXaHlUYNDALSWVS34NdQgtEuVZeAfV
+bHLvgBgyz1WOuVWVc7Z7ABLXGEhfu4JrH/OvK/hCmR0qYuh57Ql1naiDb9KaCgXb
+j2DtHP2eDQ3YslKqbEekbIMP3afY6hJnMR0w50pZ5XH/Ays8dE/yLtW1K0qlUkiN
+97HTfe36q+1dyJ/ASfEnBnvx2UA8hctyXgTQSXJMZy8bWNMdDXePD+gojnt8fLB7
+fLjx8mHvi3DkwD6f0duHD98+7NFcj8MLyUyO8HuffrdzrD896Nx6Qr/RlnBypqaG
+h0M+PnRqvGxKFQ6GD1nSIbMc7YOeQbDj8IvekXt1NBRjlcbLdM4UnrTqHeOMCQxH
+HQXplCUfHW6wo3OWffTEs6Oy33vi2T3ZnNjpAfMbHXTH3iGOHctDp5w/Y4bD/Qji
++PgLT3rkRcbhCIdMdn9vkx89GHX1o+m9d+wogFmHMB0Pvd3i3QMcjrzWnbt07GQ7
+au8iBPYc4aPuXT5mwsMhOxhnR3Tm5Yru4HMGfrR+4nWRRyVxHuO3dLdqcL54IBng
+FcM4Oi+rQfoQuvtLvPuhSd928UOA8lR1Cs6gTKNWGxr3bExsqifI5baIbVtA/Wvl
+L+Ie2hFKiOtcoS8BgdXtFOtF9oi3LfoQumFr0GTcKbEE1jFcobv2YwW/oD2mq3pn
+ubQLt8/KHMl+l42XtN8xHNXww+MEKXszGfe7mC/sUd3y1KEIXWjen9DmhYuth1FJ
+65sNtxCWCaMPDUI0A0BWP+Fi9rGg5wZlLyKxobkjjAg2br5ppqTuyv0PRfzZ2DLG
+zFXLHE2flkp3ip5yG9XdCZPvxH0k72w8oA8U+gQTeL5tBVhRKPh77YRFaw5usLNc
+3lXGBtMBRWQESWmB7QbQMM6w5LwHd8iBn1oxV9Iw8MJp1t1vlaFTsE3iIpHmU55N
+uVtQpzMM/AZk6XZFGcCMw6umAX9L+48IHSLkoxC40IghehRRa9XEGZsgGIZ4ILu6
+4Vnz7YvXpwFMP9xzhjXn9uT8dOIGvE/fG3sB6eJ4h3Mt7st4ZdnOiTzz8SKGPkaQ
+aWXdMVQXdCbfwi3sNHjKYtmcaMw7uI4N1E23dhqhMKAubj8V/GeVcaigXQl/m1mO
+PfPjHQb+M7WiiSZWEKAV+tgw7NPMEI533CLnwWp5Y9G1zZ9aCkCWB5+6/IhKCIop
+7/cPO5SBrrgwOj9vfmdZyDuqYSh0RtFuPAuDgsPI3Xnfz0TGb59s46pEJYJU1mtQ
+0Z4jRGjHbYlgo7CcYo52KGE4+vikZ0i9dZpz2B2KNXLF5QPm5bWzCL3ovetwYqoM
+jYVus41oVsjtjIATTejtx5rg2js8Rf4mETyNOwbs9V6U3jahfoRvGJ3vLlfQkN1A
+hDf8EosICphb8FhVB4PFL1nqvvaf/ULRDXBdxhpOC3M2q6SggaHLemH9co/m0ChQ
+ZAn306eAszdXodYk4oY3th1I6opIdufXqO3Kk5Atb35cBNDrKvvyoxUTr5s3ukks
++JwwXsBOE+rsrB4Qe9C4vJQkBGI6LNsfWNxr2wdZ+lLin37CBYy3dbFE2Gy1fLf6
+YdFgNK2Xu484XKeVG2GEpe8wflSvpoDmqEMfiZFA6FVXcF5NBdE66rop2pmaXdDZ
+i45CPnImVx+ta8JVQDKSXqC0u0IEFkNukS8ml0IXhIGCkTcsQj4rZKa+zXGdYkdr
+yarmK+eITyJO3bmRYUmpOvG1+UHEbwncFjnsOLI7ZLYXbAvaKVlQRc62wlRlv6Qt
+7joKQ1p36pKf62eAEvxzzaOPE4S0/ogR4zoYCSEXl1ctDnqwdwkiI9qRO7PIDx0p
+7ONS3vJVoEufjPjo8WarfOxCKucSd7+kLxCEBt2IJrZ1opK+W821T7PO/hmuWTNx
+2qrR/MkogzcUvG0KSkgudISMWlhXuvgnS1QEWNHz/YImbj9CCSJgGfrLBgKjbk9+
+9kFlxf0XR440/viLo4PoqTjet4Xs2XhwfXlBHwBY2tBX/YuhX8ZKOwFepgzpXeFY
+5l+e0a48FObeZxs/hbvT6wn89yc+x/yPS59K/qezJTLkw4S//8Rj1+rc443dvb/x
+jL65ols7xPF/3kNTS7Wv+O2Xo0+8Qk0o1g/XazyVibwfRVPEscGyhsNE/Dkn7UzX
+dV+cJGKbZjN4ihHAtWKEZMXThULCQlia5c5lU6aLhN4f0fsT9NzbHPeASVoCvjpR
+cet8ovWgFpQhuqJu3xnI3NdeJrxPhF+pEjm77ovxQ7LIFGeDaeM+J1hhqKTQ94pq
++f+TPyDy1zCVXfTFtaN+EKl/6URF+K1tNGqfixdypUs9DNVD9wHyIAlx48wHkQq5
+9EfjT8x2rld3vk74ohv9dXpP+ykEAMrXrQ3Uq81A9Kb+NCId7vlblhauAFyMO6q3
+sk4X/slzJCgVwJaAf3DW5DVGlDcRio67eEGoc434gPoD0ZGG5+9rTDeAD9t1c1/I
+dEFIjNaKO/4O1YGbksKOo5sZnz39s2C8VpGJ358D+jaF/fj5s5gTmzeg68m1ODHy
+A6ru5ifuh6+IQZfsW32rO78N2lJHZkfhAiANEbXBkVGK07pLLsj/CoGDwW6upsAk
+bf2gqYtb/0em7yDwy4l405hbvQ4vXiHZVO2uDMRJXs1t7MAK0KNaK2XbkaitGwWn
+gt2oX2E+uqCtM8A2FBsQPzcJ0+BCTSGiCNoMNKQ9AGQFrbvIt8/L0kEKIoB5HsIH
+bs9plnxF//shrRJkE8L2hprRVzSvgvFTwhkxLuirJY3u/lsMdUO1osEUMCXNvY7t
+oAT5ga//6k+/+7uPzj7/+W9//fmvfjH72clvvv+dR68f/cMP/uCz1Wzw++/f/sdf
+3//Xfz/5l//pf/ZHj/7x73b+6e//819/MJ3/8b/9e/JLs/WX9d/83v8C
+=Tb07
+-----END PGP MESSAGE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/file-current.txt.key b/tests/zypp/data/Fetcher/remote-site/file-current.txt.key
new file mode 100644 (file)
index 0000000..3b2cffa
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQGiBDslQEkRBADgDXtg08dhjHQpv9SisvzvROcsr6QRFOLWf95GWh7c6SI7Pj5m
+TClUaJXBRjLuvOR0L7w8l5nVJ/4kzSjle1ad04eDzP3sB4rNK18l7GAwc0ijV0mp
+/Kl9F4iibco3cCIMsjysWgUmlk+dYYWQubQA/EgTs7kFQo9QRxzojqnSywCg/5CZ
+5TJ19gNS8knyu819v6ZSkV8D/01Fem2WZejqsmNBNiY1JoWxnTofi3AjMv6j/+Nj
+yBuU9ozIhUicx4y+Ro05eKUZ+VAIecVbVhEqH9VUriX48TDrNxu4OGJh62q58s0u
+/tqIX5OrvrpmdOC1UTdcc2CM88vssTt8oxauZeM7YAkY9rApXUniyfnJ3fdd3FlC
+IbvaA/9XXrlBjG/3HQSPfknbqLlCvEclelRmm6LAX8fuf/JFgj0ctRMAJKB6HOE7
+/pvl3yRMMITwmd9lB/s2u+fqcwV1bDqfnyV8h3KFpTCGM1qKT+EaaT/8JhFJifYD
+jBEnUe9mYG7dUlt+Ff9Fj6ZRJQs9vszzQcsJDzNpys+j/KJutbQtRHVuY2FuIE1h
+Yy1WaWNhciBHYXLtbiA8ZHVuY2FuQG1hYy12aWNhci5jb20+iGAEEBECABgFAjsl
+QEkICwMJCAcCAQoCGQEFGwMAAAAAEgkQwSqJHVebm9QHZUdQRwABAeHRAJsGKaYQ
+p44NzYEjS9dsEAT4sYKzDgCgw4c7kDarZL/qKqFvHbvsPS6fRteIRgQQEQIABgUC
+OyVG9QAKCRDNHrapZn5C0WO7AKCBUULk55fR/yavkYgUhL9AX0rShgCfQ0jjVarV
+QNc/cj5ilTdKqSkeJ8q5Ag0EOyVAShAIAPZCV7cIfwgXcqK61qlC8wXo+VMROU+2
+8W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf3HZS
+Tz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2gpXI6
+1Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPAQ/Cl
+WxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQDGcgH
+KXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH/3NY
+WNGw9Wa8B6Ow6NyS2WSBrx33ZxInDgObZe72mO/GSyxxZqxPiqTFTpajTD+HWKQu
+vSfMGZdD1kJZTrNEI/MNLwVBhrgxLX2qrmq1aJgWa5aRcv05wHuIUrw2g220oZ09
+gUhXuF3U/oDUCAX36d16CaPCMJV1TRUoc2L1WoLP2BjzIkAgKrqUGvIFNsvtXe3c
+NU5dSQzF0spExuCz1Wy67/T4fzLSJpiWadl/eN67cp83ega5fyYy9NpISmeiFcV2
+5UJqbQM0ktilZzDhx+evmYd6PHP7i8OqgC1oHajEohfo9kmyO/AXJ/v08svnSJVO
+0G/9qQeuFp3JVeBHsN6IVAQYEQIADAUCOyVASgUbDAAAAAASCRDBKokdV5ub1Adl
+R1BHAAEBRRIAnjhaS/m/eYLw2g79GuLD/1x12Xj9AKCW04cRwUxoIirwdJxLNRT2
+GoLAdg==
+=BjTA
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file-unsigned/content b/tests/zypp/data/Fetcher/remote-site/images-file-unsigned/content
new file mode 100644 (file)
index 0000000..a85d664
--- /dev/null
@@ -0,0 +1,143 @@
+CONTENTSTYLE 11
+BASEARCHS i586
+DATADIR suse
+DESCRDIR suse/setup/descr
+DISTRIBUTION openSUSE
+FLAVOR dvd
+LABEL openSUSE 11.1
+LINGUAS cs da de en en_GB en_US es fi fr hu it ja nb nl pl pt pt_BR ru sv zh zh_CN zh_TW
+NAME openSUSE
+REFERENCES openSUSE-release = 11.1
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/11.0.42/release-notes-openSUSE.rpm
+SUMMARY openSUSE
+VERSION 11.1
+META SHA1 dd627cf38a5dee28421690d2ec6abea7af274a50  dvd-11.1-58.1.i586.pat.gz
+META SHA1 fa49e3205fea8f95d395710a4ba79f55b4626ccd  non_oss-11.1-58.1.i586.pat.gz
+META SHA1 f6c6ef681aa5fc4c5e9532f04420d54fbec033a8  packages.cs.gz
+META SHA1 cc5438a74d1b39f34bc31fbc2399ae6a95f43bc4  packages.de.gz
+META SHA1 90dfa1e81f5515769f5b16fcf31c1ab63c55ff87  packages.DU.gz
+META SHA1 b1c6b77703357297ecdd8d74b89a099ebbfe2fe6  packages.en.gz
+META SHA1 b137fc7749531183b5250ec907d4b9adf4624288  packages.es.gz
+META SHA1 3e55d6d5c21a6a1457ea6fef09c999b28883ade4  packages.fr.gz
+META SHA1 b5b6092d57a701d4052ad2bb047636c68ac31d8b  packages.gz
+META SHA1 a893937892aeff71da1c38aa06f3cce5ba02de1c  packages.hu.gz
+META SHA1 f6c6ef681aa5fc4c5e9532f04420d54fbec033a8  packages.sk.gz
+META SHA1 1d66972a977913541c7cb7130d61ec0e751148ba  patterns
+HASH SHA1 4ae30edd0588114b642bbb08fbd3fb71d7af257d  license.tar.gz
+HASH SHA1 67d82aa5cd4f08cf77cc70fe9ca1834a4b6212cc  control.xml
+HASH SHA1 e727c621ded4324cc61e94c615dbd423436f63e4  media.1/info.txt
+HASH SHA1 7442eb13ca3c184fe0d47060dbc56fd45cfcb720  boot/i386/bind
+HASH SHA1 99920bc0c322d0d0b63ac361d2f9c8f6bc9e3524  boot/i386/branding
+HASH SHA1 ca2f0bce909d73d1bfaa7cdeb98595f17e4524aa  boot/i386/common
+HASH SHA1 d107fa19616c3eae9d49fe52e54971e5b9c160fa  boot/i386/config
+HASH SHA1 4b7d8525b1d3588562bea71b26cdb5e48e29ff64  boot/i386/cracklib-dict-full.rpm
+HASH SHA1 1a8151b2bf88cf618be74eee60d65063386469e0  boot/i386/fonts-arabic.rpm
+HASH SHA1 79aec7c93b730c2355aebd085eddf4195a9c1d36  boot/i386/fonts-thai.rpm
+HASH SHA1 52e1040f8e0fae373d7426447b1f9d05d25b90f6  boot/i386/gdb
+HASH SHA1 a1ba35bcecde55eb36be2e89b7210998b846be9d  boot/i386/indic-fonts.rpm
+HASH SHA1 6394ec4eb1883520de23bd2ac043490da6ed8fde  boot/i386/initrd-xen
+HASH SHA1 6394ec4eb1883520de23bd2ac043490da6ed8fde  boot/i386/initrd-xenpae
+HASH SHA1 6cebf392eaf1b17b53f3e5e88a6b6c6d8c2b76bb  boot/i386/KhmerOS-fonts.rpm
+HASH SHA1 e9c779539f1b3f56a96337f262b2bc548e6b223e  boot/i386/LIESMICH
+HASH SHA1 abdf17a0246be709efbcc7c0e0a68f8ad797a754  boot/i386/LIESMICH.DOS
+HASH SHA1 f51e15897e5f77c7a7b4d1600dc53be6f117d9ec  boot/i386/lklug.rpm
+HASH SHA1 c4dca65aa97554bc45885b22ef4af24ad5177c75  boot/i386/mkbootdisk
+HASH SHA1 99920bc0c322d0d0b63ac361d2f9c8f6bc9e3524  boot/i386/openSUSE
+HASH SHA1 ae44557c57285f0866be41334fdaa20312ccdeab  boot/i386/README
+HASH SHA1 4a71ee6114eccfb88582dc5422020cbc5a2639dd  boot/i386/README.DOS
+HASH SHA1 e12efd095a7fb990d0c4be1e04999e72de84dd6a  boot/i386/rescue
+HASH SHA1 73e50e6321e6938471c4fc0521e0b2dce54f0346  boot/i386/root
+HASH SHA1 9b4cced8b700c6737ab02af8f1521945c925c8f9  boot/i386/rpmlist
+HASH SHA1 2181540518e50f88e7e35c619460e746934eacd4  boot/i386/sax2
+HASH SHA1 78d3ee95682c1150aa8a95132ba1c5d0d88c23bd  boot/i386/ttf-arphic-uming.rpm
+HASH SHA1 55a2d437b32d6c192c60e016756725f7a2e717e9  boot/i386/unfonts.rpm
+HASH SHA1 7c6fbdc97c11e5e03098f19c196d85cd63e5ce85  boot/i386/vmlinuz-xen
+HASH SHA1 7c6fbdc97c11e5e03098f19c196d85cd63e5ce85  boot/i386/vmlinuz-xenpae
+HASH SHA1 67d109ba80c3f396710774ff03ace955a2184dc6  boot/i386/yast2-trans-af.rpm
+HASH SHA1 3d835ea74b409914cf40ba48669a11590cffa902  boot/i386/yast2-trans-ar.rpm
+HASH SHA1 5654cabb329f0b1251d5743624fdf599b92d71e5  boot/i386/yast2-trans-bg.rpm
+HASH SHA1 1015f485542ae7c903ec9d1a9c7983d4701e2824  boot/i386/yast2-trans-bn.rpm
+HASH SHA1 454e0f1b79d2076a9ba03b23c34da3cbc3e58e25  boot/i386/yast2-trans-bs.rpm
+HASH SHA1 76bbfdb77952db51e9b21dc90ab167f2a46f4580  boot/i386/yast2-trans-ca.rpm
+HASH SHA1 59f26af0b058b47f2d821f6fa8102d58af8d0377  boot/i386/yast2-trans-cs.rpm
+HASH SHA1 db43e4829de2a26b4bb09aa1e93069af2b230377  boot/i386/yast2-trans-cy.rpm
+HASH SHA1 1aee13cb0b0a1f4e7b1bbb49e809fd13919ba7e9  boot/i386/yast2-trans-da.rpm
+HASH SHA1 36335f1ff2738071be00be65a64f7d41b7db1c33  boot/i386/yast2-trans-de.rpm
+HASH SHA1 5297e40871de03217cfee404cb88e909422ceae8  boot/i386/yast2-trans-el.rpm
+HASH SHA1 bc61efbd85302ed96ae51550a163cd0fc9ad59a3  boot/i386/yast2-trans-en_GB.rpm
+HASH SHA1 a237b73e48777fcdd42505ec1e007484d9f398da  boot/i386/yast2-trans-en_US.rpm
+HASH SHA1 f2e0dba8e86a723ab72d7f7490665f8e631f63a1  boot/i386/yast2-trans-es.rpm
+HASH SHA1 ca9bd8bf05434d42b6a7c2c7549e2ce0170c6be8  boot/i386/yast2-trans-et.rpm
+HASH SHA1 57e5cd63079ac258734f28df5385e5288cd59db0  boot/i386/yast2-trans-fa.rpm
+HASH SHA1 2983eb7ba93882e52434f396b49cd6e98d7b7399  boot/i386/yast2-trans-fi.rpm
+HASH SHA1 0dfc3b38294524871f527d919ce31d6921cf77c0  boot/i386/yast2-trans-fr.rpm
+HASH SHA1 2051d343881b288c5d22aec792e9d13ec192dfd1  boot/i386/yast2-trans-gl.rpm
+HASH SHA1 49736a9798a371c3edf02f350ebe4db1e6fff011  boot/i386/yast2-trans-gu.rpm
+HASH SHA1 d9737ea270e5166cbc409466c1d2a6f16fcc9f06  boot/i386/yast2-trans-hi.rpm
+HASH SHA1 1e222655cfabbf0212e629ed1fccf304847ac732  boot/i386/yast2-trans-hr.rpm
+HASH SHA1 1b6e646f9f8abc7c33ef2dd830b72c7a2f775620  boot/i386/yast2-trans-hu.rpm
+HASH SHA1 723a18301396eccf7c0620f1f8f016719eaf7543  boot/i386/yast2-trans-id.rpm
+HASH SHA1 70c272dbe7bfe963e44e5331edd5f440450bc827  boot/i386/yast2-trans-it.rpm
+HASH SHA1 f17bc3588c7d2878821600d42fe174156971f87e  boot/i386/yast2-trans-ja.rpm
+HASH SHA1 d46ea44b7d98ade64658d1cf388013093d1c0d39  boot/i386/yast2-trans-jv.rpm
+HASH SHA1 e4e684ebb7f93ac20461062544b49fe18c88627d  boot/i386/yast2-trans-ka.rpm
+HASH SHA1 123eb83403b88845cb5efd9046ef39351c1b68c3  boot/i386/yast2-trans-km.rpm
+HASH SHA1 85c28913c4432f43fa29d8791d97b3e10456a42a  boot/i386/yast2-trans-ko.rpm
+HASH SHA1 6692fca0c1acb85b398bcbb9150d0826ea38f2c8  boot/i386/yast2-trans-lo.rpm
+HASH SHA1 6247198d1348d31192dc158969ce8ae9252d6f0b  boot/i386/yast2-trans-lt.rpm
+HASH SHA1 a630f16e50a6227f814751931bb1a1ec15981c29  boot/i386/yast2-trans-mk.rpm
+HASH SHA1 4f0bffd5cbeb7d37a7d78f1996cade7ae2ad2420  boot/i386/yast2-trans-mr.rpm
+HASH SHA1 bbc950f9fec446e9a5a5876c532cd56e060c5eb4  boot/i386/yast2-trans-nb.rpm
+HASH SHA1 edae4d58bd99064bbddd62278875b0387a2d495d  boot/i386/yast2-trans-nl.rpm
+HASH SHA1 85f1dc1eef8862066050c09eef023705a5d61039  boot/i386/yast2-trans-pa.rpm
+HASH SHA1 e1eb02ebedff18a01facc150e00d05385affaa17  boot/i386/yast2-trans-pl.rpm
+HASH SHA1 c3d31be5bf8af62c537e1a70db9afc98842b3f82  boot/i386/yast2-trans-pt_BR.rpm
+HASH SHA1 b2bcd427139f2e13684a592ff186c3076a5b22d8  boot/i386/yast2-trans-pt.rpm
+HASH SHA1 7fdb3299ca8230ec363d3ee8eee24915c931790c  boot/i386/yast2-trans-ro.rpm
+HASH SHA1 8b9bdb13f3b7c73e8c64303e8614e44bf640cc9f  boot/i386/yast2-trans-ru.rpm
+HASH SHA1 55876ae86c10a3504bbbcd71e49564f4c0b47149  boot/i386/yast2-trans-si.rpm
+HASH SHA1 ee8ce4bc8a06b5094f8d51c79b32806531203d62  boot/i386/yast2-trans-sk.rpm
+HASH SHA1 04130a176a24ce3adb663a2bb694fd484f2a7776  boot/i386/yast2-trans-sl.rpm
+HASH SHA1 d3408d472a956ffbd1fb49354057da5c0b77879f  boot/i386/yast2-trans-sr.rpm
+HASH SHA1 fda2c586ff8a17d402108f007c2c6214bcaeca3f  boot/i386/yast2-trans-sv.rpm
+HASH SHA1 04021fd6654b2e7b6cbff65c094efa8c35424215  boot/i386/yast2-trans-ta.rpm
+HASH SHA1 6aaae1e49e3188b5e73bedb3c3cd044c4108cca2  boot/i386/yast2-trans-th.rpm
+HASH SHA1 6bb5903b7bc64185352c616099c344f8738bb768  boot/i386/yast2-trans-tr.rpm
+HASH SHA1 9f631f117433db5e44a6bbed7c8a50cbbe5e8ac0  boot/i386/yast2-trans-uk.rpm
+HASH SHA1 3ba5a8f4f3dc3f454e13223c2ecef9937824a611  boot/i386/yast2-trans-vi.rpm
+HASH SHA1 bc2775d3a4a0a3ca80e5a5caa51a6612e7158d32  boot/i386/yast2-trans-wa.rpm
+HASH SHA1 a3511637ca9bd4cbf5dc9eca58f8ccf5df49603c  boot/i386/yast2-trans-xh.rpm
+HASH SHA1 ba8875e4c0febe1d26d27c0c948e93128f14476a  boot/i386/yast2-trans-zh_CN.rpm
+HASH SHA1 d39b329ce32a0d59bebecd0d87ecfd59cb26e297  boot/i386/yast2-trans-zh_TW.rpm
+HASH SHA1 0c6f45e6b01d531ac7e821ae9e479416149e92eb  boot/i386/yast2-trans-zu.rpm
+HASH SHA1 2325eb886cf06b58ca1892578383b5fcc5b11866  boot/i386/loader/linux
+HASH SHA1 586ec9beb26fb337283f3b49149d843a3fb7c821  boot/i386/loader/initrd
+HASH SHA1 d6b4db657272efe902f8ce92b53082fda4d5f1ca  boot/i386/loader/08000600.spl
+HASH SHA1 22d4aeede74fdf44b3c4a2524319fe9cf418d4c6  boot/i386/loader/10240600.spl
+HASH SHA1 93e297fb9ffa23d6a167c50c318bbb46b0705e45  boot/i386/loader/10240768.spl
+HASH SHA1 8c135621ac91216c3388f9ea9c02127b2f6375a0  boot/i386/loader/12800800.spl
+HASH SHA1 54dd2bc17c9f73ad9d6a85489f99f518849cb84d  boot/i386/loader/12801024.spl
+HASH SHA1 ef0f517a3f3a3240349b9bdd01e794267372c4ab  boot/i386/loader/14001050.spl
+HASH SHA1 2abbc5b4c7c3dc8134ae1207f9d9e95a640046ef  boot/i386/loader/16001200.spl
+HASH SHA1 e37862355648f6330b6831c69c989dec02b80ab0  boot/i386/loader/16801050.spl
+HASH SHA1 082a7887d2f43d049fdb0fc92c5051f1a5b7e663  boot/i386/loader/19201200.spl
+HASH SHA1 ed5f4a87074367bcf8396da7a9b9b3d1be6dfcb2  images/base-i386.tar.lzma
+HASH SHA1 9d055cacc983226171cdd3a3fe1acd6bb6fa044e  images/base-meta-i386.tar.lzma
+HASH SHA1 361e07c44526cd43a2d477b7b3448e49e674f0a8  images/common-base-i386.tar.lzma
+HASH SHA1 6854608038e4d3b11fcc950e28ca49e4582fd2a4  images/common-desktop-i386.tar.lzma
+HASH SHA1 5f6a43158c017e188309c094cdaf24c346de3dfb  images/common-xorg-i386.tar.lzma
+HASH SHA1 ce6620b820e494c042398b9fd7ed108578008168  images/details-i386.xml
+HASH SHA1 22c466bf9af3003ee95fed6f7fb93b4c8c0cd993  images/gnome-i386.tar.lzma
+HASH SHA1 49374551f38fe919cf4545d5af0fea7d45361738  images/gnome-meta-i386.tar.lzma
+HASH SHA1 d1e9a38629e353d1f0136ded670cf28213d47e6f  images/images.xml
+HASH SHA1 0df7debf780a0e01b18004d18ef6ab21410b9480  images/kde-i386.tar.lzma
+HASH SHA1 c418a15669de278e523893f030c209c479f490e6  images/kde-meta-i386.tar.lzma
+HASH SHA1 6c4238796684f770a0b4ecd1ea4469e5a7a49b0d  images/x11-i386.tar.lzma
+HASH SHA1 cc50a8bc9d560c8d522660feed5feb019f9b889e  images/x11-meta-i386.tar.lzma
+KEY SHA1  17162a96933229a9771ee10c0976bdc047a2f53d  gpg-pubkey-0dfb3188-41ed929b.asc
+KEY SHA1  f6accbb18d705bfc104c893cf7dfca1247a33f3c  gpg-pubkey-307e3d54-481f30aa.asc
+KEY SHA1  47f6492d127ae9f6aac353a2dd23752fc0ed4f8d  gpg-pubkey-3d25d3d9-36e12d04.asc
+KEY SHA1  2288e5849740566e4fb65b7c9dc0c7e4f43b1039  gpg-pubkey-56b4177a-47965b33.asc
+KEY SHA1  89d4bcd20a281553fd1d4ec1708603ebf88f1a59  gpg-pubkey-7e2e3b05-4816488f.asc
+KEY SHA1  06ff5171362496c0db84beeccd29967f580350b2  gpg-pubkey-9c800aca-481f343a.asc
+KEY SHA1  04544096c5c3b0ed7b01a83d79e048307c2af919  gpg-pubkey-a1912208-446a0899.asc
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file-unsigned/images/images.xml b/tests/zypp/data/Fetcher/remote-site/images-file-unsigned/images/images.xml
new file mode 100644 (file)
index 0000000..7b252e8
--- /dev/null
@@ -0,0 +1,345 @@
+<?xml version="1.0"?>
+<!DOCTYPE images>
+<image_installation xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
+<image_sets config:type="list" >
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>i386</archs>
+    <pkg_image>kde-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>i386</archs>
+    <pkg_image>gnome-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>i386</archs>
+    <pkg_image>base-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>i386</archs>
+    <pkg_image>x11-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>kde-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>gnome-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>base-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>x11-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>ppc</archs>
+    <pkg_image>kde-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>ppc</archs>
+    <pkg_image>gnome-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>ppc</archs>
+    <pkg_image>base-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>ppc</archs>
+    <pkg_image>x11-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+</image_sets>
+</image_installation>
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file/content b/tests/zypp/data/Fetcher/remote-site/images-file/content
new file mode 100644 (file)
index 0000000..a85d664
--- /dev/null
@@ -0,0 +1,143 @@
+CONTENTSTYLE 11
+BASEARCHS i586
+DATADIR suse
+DESCRDIR suse/setup/descr
+DISTRIBUTION openSUSE
+FLAVOR dvd
+LABEL openSUSE 11.1
+LINGUAS cs da de en en_GB en_US es fi fr hu it ja nb nl pl pt pt_BR ru sv zh zh_CN zh_TW
+NAME openSUSE
+REFERENCES openSUSE-release = 11.1
+RELNOTESURL http://www.suse.com/relnotes/i386/openSUSE/11.0.42/release-notes-openSUSE.rpm
+SUMMARY openSUSE
+VERSION 11.1
+META SHA1 dd627cf38a5dee28421690d2ec6abea7af274a50  dvd-11.1-58.1.i586.pat.gz
+META SHA1 fa49e3205fea8f95d395710a4ba79f55b4626ccd  non_oss-11.1-58.1.i586.pat.gz
+META SHA1 f6c6ef681aa5fc4c5e9532f04420d54fbec033a8  packages.cs.gz
+META SHA1 cc5438a74d1b39f34bc31fbc2399ae6a95f43bc4  packages.de.gz
+META SHA1 90dfa1e81f5515769f5b16fcf31c1ab63c55ff87  packages.DU.gz
+META SHA1 b1c6b77703357297ecdd8d74b89a099ebbfe2fe6  packages.en.gz
+META SHA1 b137fc7749531183b5250ec907d4b9adf4624288  packages.es.gz
+META SHA1 3e55d6d5c21a6a1457ea6fef09c999b28883ade4  packages.fr.gz
+META SHA1 b5b6092d57a701d4052ad2bb047636c68ac31d8b  packages.gz
+META SHA1 a893937892aeff71da1c38aa06f3cce5ba02de1c  packages.hu.gz
+META SHA1 f6c6ef681aa5fc4c5e9532f04420d54fbec033a8  packages.sk.gz
+META SHA1 1d66972a977913541c7cb7130d61ec0e751148ba  patterns
+HASH SHA1 4ae30edd0588114b642bbb08fbd3fb71d7af257d  license.tar.gz
+HASH SHA1 67d82aa5cd4f08cf77cc70fe9ca1834a4b6212cc  control.xml
+HASH SHA1 e727c621ded4324cc61e94c615dbd423436f63e4  media.1/info.txt
+HASH SHA1 7442eb13ca3c184fe0d47060dbc56fd45cfcb720  boot/i386/bind
+HASH SHA1 99920bc0c322d0d0b63ac361d2f9c8f6bc9e3524  boot/i386/branding
+HASH SHA1 ca2f0bce909d73d1bfaa7cdeb98595f17e4524aa  boot/i386/common
+HASH SHA1 d107fa19616c3eae9d49fe52e54971e5b9c160fa  boot/i386/config
+HASH SHA1 4b7d8525b1d3588562bea71b26cdb5e48e29ff64  boot/i386/cracklib-dict-full.rpm
+HASH SHA1 1a8151b2bf88cf618be74eee60d65063386469e0  boot/i386/fonts-arabic.rpm
+HASH SHA1 79aec7c93b730c2355aebd085eddf4195a9c1d36  boot/i386/fonts-thai.rpm
+HASH SHA1 52e1040f8e0fae373d7426447b1f9d05d25b90f6  boot/i386/gdb
+HASH SHA1 a1ba35bcecde55eb36be2e89b7210998b846be9d  boot/i386/indic-fonts.rpm
+HASH SHA1 6394ec4eb1883520de23bd2ac043490da6ed8fde  boot/i386/initrd-xen
+HASH SHA1 6394ec4eb1883520de23bd2ac043490da6ed8fde  boot/i386/initrd-xenpae
+HASH SHA1 6cebf392eaf1b17b53f3e5e88a6b6c6d8c2b76bb  boot/i386/KhmerOS-fonts.rpm
+HASH SHA1 e9c779539f1b3f56a96337f262b2bc548e6b223e  boot/i386/LIESMICH
+HASH SHA1 abdf17a0246be709efbcc7c0e0a68f8ad797a754  boot/i386/LIESMICH.DOS
+HASH SHA1 f51e15897e5f77c7a7b4d1600dc53be6f117d9ec  boot/i386/lklug.rpm
+HASH SHA1 c4dca65aa97554bc45885b22ef4af24ad5177c75  boot/i386/mkbootdisk
+HASH SHA1 99920bc0c322d0d0b63ac361d2f9c8f6bc9e3524  boot/i386/openSUSE
+HASH SHA1 ae44557c57285f0866be41334fdaa20312ccdeab  boot/i386/README
+HASH SHA1 4a71ee6114eccfb88582dc5422020cbc5a2639dd  boot/i386/README.DOS
+HASH SHA1 e12efd095a7fb990d0c4be1e04999e72de84dd6a  boot/i386/rescue
+HASH SHA1 73e50e6321e6938471c4fc0521e0b2dce54f0346  boot/i386/root
+HASH SHA1 9b4cced8b700c6737ab02af8f1521945c925c8f9  boot/i386/rpmlist
+HASH SHA1 2181540518e50f88e7e35c619460e746934eacd4  boot/i386/sax2
+HASH SHA1 78d3ee95682c1150aa8a95132ba1c5d0d88c23bd  boot/i386/ttf-arphic-uming.rpm
+HASH SHA1 55a2d437b32d6c192c60e016756725f7a2e717e9  boot/i386/unfonts.rpm
+HASH SHA1 7c6fbdc97c11e5e03098f19c196d85cd63e5ce85  boot/i386/vmlinuz-xen
+HASH SHA1 7c6fbdc97c11e5e03098f19c196d85cd63e5ce85  boot/i386/vmlinuz-xenpae
+HASH SHA1 67d109ba80c3f396710774ff03ace955a2184dc6  boot/i386/yast2-trans-af.rpm
+HASH SHA1 3d835ea74b409914cf40ba48669a11590cffa902  boot/i386/yast2-trans-ar.rpm
+HASH SHA1 5654cabb329f0b1251d5743624fdf599b92d71e5  boot/i386/yast2-trans-bg.rpm
+HASH SHA1 1015f485542ae7c903ec9d1a9c7983d4701e2824  boot/i386/yast2-trans-bn.rpm
+HASH SHA1 454e0f1b79d2076a9ba03b23c34da3cbc3e58e25  boot/i386/yast2-trans-bs.rpm
+HASH SHA1 76bbfdb77952db51e9b21dc90ab167f2a46f4580  boot/i386/yast2-trans-ca.rpm
+HASH SHA1 59f26af0b058b47f2d821f6fa8102d58af8d0377  boot/i386/yast2-trans-cs.rpm
+HASH SHA1 db43e4829de2a26b4bb09aa1e93069af2b230377  boot/i386/yast2-trans-cy.rpm
+HASH SHA1 1aee13cb0b0a1f4e7b1bbb49e809fd13919ba7e9  boot/i386/yast2-trans-da.rpm
+HASH SHA1 36335f1ff2738071be00be65a64f7d41b7db1c33  boot/i386/yast2-trans-de.rpm
+HASH SHA1 5297e40871de03217cfee404cb88e909422ceae8  boot/i386/yast2-trans-el.rpm
+HASH SHA1 bc61efbd85302ed96ae51550a163cd0fc9ad59a3  boot/i386/yast2-trans-en_GB.rpm
+HASH SHA1 a237b73e48777fcdd42505ec1e007484d9f398da  boot/i386/yast2-trans-en_US.rpm
+HASH SHA1 f2e0dba8e86a723ab72d7f7490665f8e631f63a1  boot/i386/yast2-trans-es.rpm
+HASH SHA1 ca9bd8bf05434d42b6a7c2c7549e2ce0170c6be8  boot/i386/yast2-trans-et.rpm
+HASH SHA1 57e5cd63079ac258734f28df5385e5288cd59db0  boot/i386/yast2-trans-fa.rpm
+HASH SHA1 2983eb7ba93882e52434f396b49cd6e98d7b7399  boot/i386/yast2-trans-fi.rpm
+HASH SHA1 0dfc3b38294524871f527d919ce31d6921cf77c0  boot/i386/yast2-trans-fr.rpm
+HASH SHA1 2051d343881b288c5d22aec792e9d13ec192dfd1  boot/i386/yast2-trans-gl.rpm
+HASH SHA1 49736a9798a371c3edf02f350ebe4db1e6fff011  boot/i386/yast2-trans-gu.rpm
+HASH SHA1 d9737ea270e5166cbc409466c1d2a6f16fcc9f06  boot/i386/yast2-trans-hi.rpm
+HASH SHA1 1e222655cfabbf0212e629ed1fccf304847ac732  boot/i386/yast2-trans-hr.rpm
+HASH SHA1 1b6e646f9f8abc7c33ef2dd830b72c7a2f775620  boot/i386/yast2-trans-hu.rpm
+HASH SHA1 723a18301396eccf7c0620f1f8f016719eaf7543  boot/i386/yast2-trans-id.rpm
+HASH SHA1 70c272dbe7bfe963e44e5331edd5f440450bc827  boot/i386/yast2-trans-it.rpm
+HASH SHA1 f17bc3588c7d2878821600d42fe174156971f87e  boot/i386/yast2-trans-ja.rpm
+HASH SHA1 d46ea44b7d98ade64658d1cf388013093d1c0d39  boot/i386/yast2-trans-jv.rpm
+HASH SHA1 e4e684ebb7f93ac20461062544b49fe18c88627d  boot/i386/yast2-trans-ka.rpm
+HASH SHA1 123eb83403b88845cb5efd9046ef39351c1b68c3  boot/i386/yast2-trans-km.rpm
+HASH SHA1 85c28913c4432f43fa29d8791d97b3e10456a42a  boot/i386/yast2-trans-ko.rpm
+HASH SHA1 6692fca0c1acb85b398bcbb9150d0826ea38f2c8  boot/i386/yast2-trans-lo.rpm
+HASH SHA1 6247198d1348d31192dc158969ce8ae9252d6f0b  boot/i386/yast2-trans-lt.rpm
+HASH SHA1 a630f16e50a6227f814751931bb1a1ec15981c29  boot/i386/yast2-trans-mk.rpm
+HASH SHA1 4f0bffd5cbeb7d37a7d78f1996cade7ae2ad2420  boot/i386/yast2-trans-mr.rpm
+HASH SHA1 bbc950f9fec446e9a5a5876c532cd56e060c5eb4  boot/i386/yast2-trans-nb.rpm
+HASH SHA1 edae4d58bd99064bbddd62278875b0387a2d495d  boot/i386/yast2-trans-nl.rpm
+HASH SHA1 85f1dc1eef8862066050c09eef023705a5d61039  boot/i386/yast2-trans-pa.rpm
+HASH SHA1 e1eb02ebedff18a01facc150e00d05385affaa17  boot/i386/yast2-trans-pl.rpm
+HASH SHA1 c3d31be5bf8af62c537e1a70db9afc98842b3f82  boot/i386/yast2-trans-pt_BR.rpm
+HASH SHA1 b2bcd427139f2e13684a592ff186c3076a5b22d8  boot/i386/yast2-trans-pt.rpm
+HASH SHA1 7fdb3299ca8230ec363d3ee8eee24915c931790c  boot/i386/yast2-trans-ro.rpm
+HASH SHA1 8b9bdb13f3b7c73e8c64303e8614e44bf640cc9f  boot/i386/yast2-trans-ru.rpm
+HASH SHA1 55876ae86c10a3504bbbcd71e49564f4c0b47149  boot/i386/yast2-trans-si.rpm
+HASH SHA1 ee8ce4bc8a06b5094f8d51c79b32806531203d62  boot/i386/yast2-trans-sk.rpm
+HASH SHA1 04130a176a24ce3adb663a2bb694fd484f2a7776  boot/i386/yast2-trans-sl.rpm
+HASH SHA1 d3408d472a956ffbd1fb49354057da5c0b77879f  boot/i386/yast2-trans-sr.rpm
+HASH SHA1 fda2c586ff8a17d402108f007c2c6214bcaeca3f  boot/i386/yast2-trans-sv.rpm
+HASH SHA1 04021fd6654b2e7b6cbff65c094efa8c35424215  boot/i386/yast2-trans-ta.rpm
+HASH SHA1 6aaae1e49e3188b5e73bedb3c3cd044c4108cca2  boot/i386/yast2-trans-th.rpm
+HASH SHA1 6bb5903b7bc64185352c616099c344f8738bb768  boot/i386/yast2-trans-tr.rpm
+HASH SHA1 9f631f117433db5e44a6bbed7c8a50cbbe5e8ac0  boot/i386/yast2-trans-uk.rpm
+HASH SHA1 3ba5a8f4f3dc3f454e13223c2ecef9937824a611  boot/i386/yast2-trans-vi.rpm
+HASH SHA1 bc2775d3a4a0a3ca80e5a5caa51a6612e7158d32  boot/i386/yast2-trans-wa.rpm
+HASH SHA1 a3511637ca9bd4cbf5dc9eca58f8ccf5df49603c  boot/i386/yast2-trans-xh.rpm
+HASH SHA1 ba8875e4c0febe1d26d27c0c948e93128f14476a  boot/i386/yast2-trans-zh_CN.rpm
+HASH SHA1 d39b329ce32a0d59bebecd0d87ecfd59cb26e297  boot/i386/yast2-trans-zh_TW.rpm
+HASH SHA1 0c6f45e6b01d531ac7e821ae9e479416149e92eb  boot/i386/yast2-trans-zu.rpm
+HASH SHA1 2325eb886cf06b58ca1892578383b5fcc5b11866  boot/i386/loader/linux
+HASH SHA1 586ec9beb26fb337283f3b49149d843a3fb7c821  boot/i386/loader/initrd
+HASH SHA1 d6b4db657272efe902f8ce92b53082fda4d5f1ca  boot/i386/loader/08000600.spl
+HASH SHA1 22d4aeede74fdf44b3c4a2524319fe9cf418d4c6  boot/i386/loader/10240600.spl
+HASH SHA1 93e297fb9ffa23d6a167c50c318bbb46b0705e45  boot/i386/loader/10240768.spl
+HASH SHA1 8c135621ac91216c3388f9ea9c02127b2f6375a0  boot/i386/loader/12800800.spl
+HASH SHA1 54dd2bc17c9f73ad9d6a85489f99f518849cb84d  boot/i386/loader/12801024.spl
+HASH SHA1 ef0f517a3f3a3240349b9bdd01e794267372c4ab  boot/i386/loader/14001050.spl
+HASH SHA1 2abbc5b4c7c3dc8134ae1207f9d9e95a640046ef  boot/i386/loader/16001200.spl
+HASH SHA1 e37862355648f6330b6831c69c989dec02b80ab0  boot/i386/loader/16801050.spl
+HASH SHA1 082a7887d2f43d049fdb0fc92c5051f1a5b7e663  boot/i386/loader/19201200.spl
+HASH SHA1 ed5f4a87074367bcf8396da7a9b9b3d1be6dfcb2  images/base-i386.tar.lzma
+HASH SHA1 9d055cacc983226171cdd3a3fe1acd6bb6fa044e  images/base-meta-i386.tar.lzma
+HASH SHA1 361e07c44526cd43a2d477b7b3448e49e674f0a8  images/common-base-i386.tar.lzma
+HASH SHA1 6854608038e4d3b11fcc950e28ca49e4582fd2a4  images/common-desktop-i386.tar.lzma
+HASH SHA1 5f6a43158c017e188309c094cdaf24c346de3dfb  images/common-xorg-i386.tar.lzma
+HASH SHA1 ce6620b820e494c042398b9fd7ed108578008168  images/details-i386.xml
+HASH SHA1 22c466bf9af3003ee95fed6f7fb93b4c8c0cd993  images/gnome-i386.tar.lzma
+HASH SHA1 49374551f38fe919cf4545d5af0fea7d45361738  images/gnome-meta-i386.tar.lzma
+HASH SHA1 d1e9a38629e353d1f0136ded670cf28213d47e6f  images/images.xml
+HASH SHA1 0df7debf780a0e01b18004d18ef6ab21410b9480  images/kde-i386.tar.lzma
+HASH SHA1 c418a15669de278e523893f030c209c479f490e6  images/kde-meta-i386.tar.lzma
+HASH SHA1 6c4238796684f770a0b4ecd1ea4469e5a7a49b0d  images/x11-i386.tar.lzma
+HASH SHA1 cc50a8bc9d560c8d522660feed5feb019f9b889e  images/x11-meta-i386.tar.lzma
+KEY SHA1  17162a96933229a9771ee10c0976bdc047a2f53d  gpg-pubkey-0dfb3188-41ed929b.asc
+KEY SHA1  f6accbb18d705bfc104c893cf7dfca1247a33f3c  gpg-pubkey-307e3d54-481f30aa.asc
+KEY SHA1  47f6492d127ae9f6aac353a2dd23752fc0ed4f8d  gpg-pubkey-3d25d3d9-36e12d04.asc
+KEY SHA1  2288e5849740566e4fb65b7c9dc0c7e4f43b1039  gpg-pubkey-56b4177a-47965b33.asc
+KEY SHA1  89d4bcd20a281553fd1d4ec1708603ebf88f1a59  gpg-pubkey-7e2e3b05-4816488f.asc
+KEY SHA1  06ff5171362496c0db84beeccd29967f580350b2  gpg-pubkey-9c800aca-481f343a.asc
+KEY SHA1  04544096c5c3b0ed7b01a83d79e048307c2af919  gpg-pubkey-a1912208-446a0899.asc
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file/content.asc b/tests/zypp/data/Fetcher/remote-site/images-file/content.asc
new file mode 100644 (file)
index 0000000..1e13b6f
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+iEYEABECAAYFAkkTDKoACgkQm+zCtd2wN1bWFwCcDMWfuN+JDv8/Dew9ozDLNkNM
+JbAAn2nqGLhU1PcX6Ev4t/jlPsazliaC
+=8Ava
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file/content.key b/tests/zypp/data/Fetcher/remote-site/images-file/content.key
new file mode 100644 (file)
index 0000000..7b6734f
--- /dev/null
@@ -0,0 +1,49 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF
+110vZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6e
+DAbL05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRiu
+s2ev221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjC
+kfar/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/
+gnQt+mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUK
+fZ3gA/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzp
+fnWJ3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAy
+UYuG4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0
+IEtleSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNm
+TgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2lao
+hb2Q4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQA
+toB5TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnr
+VW9PKj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPH
+cMOc31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues
+73DPPyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsL
+HC5wHMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y
+0KVapBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2w
+N1apuACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dz
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=ZZx0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/Fetcher/remote-site/images-file/images/images.xml b/tests/zypp/data/Fetcher/remote-site/images-file/images/images.xml
new file mode 100644 (file)
index 0000000..7b252e8
--- /dev/null
@@ -0,0 +1,345 @@
+<?xml version="1.0"?>
+<!DOCTYPE images>
+<image_installation xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
+<image_sets config:type="list" >
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>i386</archs>
+    <pkg_image>kde-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>i386</archs>
+    <pkg_image>gnome-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>i386</archs>
+    <pkg_image>base-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>i386</archs>
+    <pkg_image>x11-meta-i386.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-i386.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-i386.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>kde-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>gnome-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>base-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>x86_64</archs>
+    <pkg_image>x11-meta-x86_64.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-x86_64.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-x86_64.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,games,imaging,kde4,kde4_basis,multimedia,sw_management,x11,xgl</patterns>
+    <archs>ppc</archs>
+    <pkg_image>kde-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>KDE [base]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [xorg]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [desktop]</name>
+       <type>tar</type>
+       <file>common-desktop-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE</name>
+       <type>tar</type>
+       <file>kde-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>KDE [meta]</name>
+       <type>tar</type>
+       <file>kde-meta-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,games,gnome,gnome_basis,imaging,multimedia,sw_management,x11,xgl</patterns>
+    <archs>ppc</archs>
+    <pkg_image>gnome-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>GNOME [1]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [2]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [4]</name>
+       <type>tar</type>
+       <file>common-desktop-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME</name>
+       <type>tar</type>
+       <file>gnome-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>GNOME [meta]</name>
+       <type>tar</type>
+       <file>gnome-meta-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,sw_management</patterns>
+    <archs>ppc</archs>
+    <pkg_image>base-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>BASE [1]</name>
+        <type>tar</type>
+        <file>base-meta-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [2]</name>
+        <type>tar</type>
+        <file>base-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>BASE [3]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+  <image_set>
+    <patterns>base,enhanced_base,fonts,sw_management,x11</patterns>
+    <archs>ppc</archs>
+    <pkg_image>x11-meta-ppc.tar.lzma</pkg_image>
+    <images config:type="list">
+      <image>
+        <name>X11 [1]</name>
+        <type>tar</type>
+        <file>x11-meta-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [2]</name>
+        <type>tar</type>
+        <file>x11-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [3]</name>
+        <type>tar</type>
+        <file>common-xorg-ppc.tar.lzma</file>
+      </image>
+      <image>
+        <name>X11 [4]</name>
+        <type>tar</type>
+        <file>common-base-ppc.tar.lzma</file>
+      </image>
+    </images>
+  </image_set>
+</image_sets>
+</image_installation>
diff --git a/tests/zypp/data/FileChecker/hello.txt b/tests/zypp/data/FileChecker/hello.txt
new file mode 100644 (file)
index 0000000..10377d3
--- /dev/null
@@ -0,0 +1 @@
+hello this is a test
diff --git a/tests/zypp/data/FileChecker/hello.txt.asc b/tests/zypp/data/FileChecker/hello.txt.asc
new file mode 100644 (file)
index 0000000..f71bd45
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQBGXW7Gm+zCtd2wN1YRAuAXAJ9e2obl0RWM9eBQz8dmPdQjclmamQCfRnJS
+XFIPNZAA1XeQL/Dvi51EIwc=
+=3TuF
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/FileChecker/hello.txt.key b/tests/zypp/data/FileChecker/hello.txt.key
new file mode 100644 (file)
index 0000000..1ee7f92
--- /dev/null
@@ -0,0 +1,214 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+mQGiBDsj/RYRBACQD/DCxkMgmEjBNYh53AfsV+zcMaz4nDmeEElANfHrVzVGx33N
+Siiqs33RIjV35Gd8OH1iSnbA7ef0gWELgVSToK2ydv/3X5Cbcb1MOWYQKJE1dQz7
+fw7Ic9nP7NieM18YMsOYEmCvyL4sLZviQIlb3caP+OpI/GAoNINY8m9yowCgxgx1
+L+jnJznXyKy7v5WgwMyrE2cD/38Nvp62Rq1/IqhUDc3SDUp5+xPddwOZ/E7P9F73
+0Gb2ec2fhAm9QZyVvFvLa+SJq2/LvY+vITZSRI0HTBZf4Yrzd6eHu/cDp0m0o/BS
+McuoaHmKeHYcyIa2w8LMREpchgdlY/LnHR83Yipc3iegBRUvoTtwUYMqpswwi+6i
+50nhA/9MC5cPOZbPpqbaDbSz0NtAVM2gcvgiBx4VKCh/AhkZ+abzogeHn6uT2eaP
+3Fnk4YOa0FEbO+YHg3Lu45tZV3pBQUZoY07r5niT0Sb6dAKO/j/omEt4q44OO3ba
+fanEvFurtgpkszoD20yheQLhv7CVdS8IUfQ2R+r0eQjxtAfJWLQmRHVuY2FuIE1h
+Yy1WaWNhciBQcmV0dCA8ZHVuY2FuQHB1Yy5jbD6IVwQTEQIAFwUCOyP9FgULBwoD
+BAMVAwIDFgIBAheAAAoJEM0etqlmfkLRqZUAnA8SIsD1eQkhDR7GkekdXWtlbW1W
+AJ0eAtcylAOTGf3AezgtP/vlWtLj5rkBDQQ7I/0vEAQAnZXsJoF43AMGs5ccBsfe
+nbLa1GacjBA212+wJ/toRCbs9xzs5dozh+TnY4Px4cQSafdcsmm3jwMVeCdPdRZ0
+RuJ5qEm2e29qm9nj2MTLVMKEjbTS/FbK5SkxKuLUXHsgZyFLGssFjTWDKsX/jy8c
+u0Kby++b6gPkO3Ft0BjwyRMAAwUEAJSn61N7TqyPOs5GSCxzUIAbsh4PlGeDZ3Uc
+g+CY/+WZS7CzJlUZiDWqIFADmip5FcuF/MV0mYgcd8nMBVcy17maEf1OnfPhEhOj
+spu8xBnSNBGWQHQx9h5CBy66riBSHG8czF9/IlKmWgyo+TGJXrxz1R2VIYgoato6
+BKZSduFZiEYEGBECAAYFAjsj/S8ACgkQzR62qWZ+QtFuBQCeMY2aH7t6yvVR6o1W
+YopKmcxZw0UAn3iLjS38pR3qIfRN4Qhzktzu1ofBmQGiBDslJtURBADjFHks4HZl
+ZIc3UjWQrICS2dKbbBWxbRIKmirXD0mOZsOCdmkMpIgy4BWM2HncN5BtP/1eyh+n
+IwHHnFKgzqKxAIY3AjWzEDVLKPbJZ2xdQceEbKrx3Zrywt4KdyHG9DeAMYmhib8B
+VZ9Gg0zyTD6/HqXmA0QUOfOHQKLBApVTVwCg/+F6iL4OlBX1xKlg17yHNbdAFsME
+AIOH/1rBCnc2sIjyjFdgha5OxACwOJvrodAOLjDxRL/uRWnjOrKhM36A+TySc5Lv
+PpaqnGYcPQMNm7hUThRoLU5rYf/rdUV49y/ARQZDAdZOFf4meYk0c735c7TTTcqw
+f7Ri3gVGfrhsmRFwvU9JyNhkWJ/9HpOk1EZCYLgl8f4SA/sEI9CRWiLS+8PHf02p
+PzVZkBZzTMZNIUlXKltPJ9hSsbn1P0PHAfk1hYxch0QAQWn786F5P8DBT7bY9/Fj
+2Tuu9SReDg+hK1X3K/S8QpUZ3aDuQPBTvtzTryI0k7wsRioXl+dI7yJ5TrtmCUpl
+J7QRTaEvpYRn1MuHorM8exqrxLQxRnJhbmNpc2NhIFF1aW50YW5hIE5hcmFuam8g
+PGZyYW5jaXNjYUBob3Rwb3AuY29tPohgBBARAgAYBQI7JSbVCAsDCQgHAgEKAhkB
+BRsDAAAAABIJEGcamjydIvodB2VHUEcAAQG0twCg462jbwEDWXbQkjp7BsOpn17h
+NIgAoMTjKy9QT5pSPvCR2vg4O9DFKJJciEYEEBECAAYFAjslRYcACgkQzR62qWZ+
+QtGT6ACbBW2aYqhvqyE8UmOTpJNeSNa/HrkAoJr1/YGNLR0mOJ4Km5xZ8RprGHv7
+uQINBDslJtUQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmP
+QFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24
+rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhO
+SdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18
+F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsC
+RtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICCADd3vdMio1+IrBMRxfHs5bw
+qUTxQbHuGIrX4gFqQswWQHnihWPpotDIGwf+uuBWz1X9RiHKCPIOE0rTvXk6DsH1
+s/5iE0+zCLHsoGPSY4DU7WYQgrqumSBP0CqEBFzBJSUN/iCD4PP6lfjzlrmfDznE
+Yn73KDJKXM5c0T4YJbs9NFBSmq8qzY9MAixlkty5jvz3GX8/EMTgSxG9ih68CTac
+uq+u7fzbQszjKcrAqrPRAgJMtVwTuzBrpyTp/nelvOG8STR0z30yKu0kxaSPupi/
+BDCg/pTjRwAT6TmkdNm1uhebJzK8L6bKy8msqZVMuscxnS/hO2HZ651RvCysyRFh
+iFQEGBECAAwFAjslJtUFGwwAAAAAEgkQZxqaPJ0i+h0HZUdQRwABAXlmAKD1P1CN
+skD6H649PbY7THYFIgrgswCglmKY5nhOTdRIUsTi5cN37CHIob6ZAaIEOyU2WhEE
+AMSfxMgHwElMwonNAeawUGT8NhqgfgRGCqJGHK3v6yx3/ms51sVA4PdA7jt6wG8/
+nbhszeeQCIwiqZtaxGoHZcTEu4219EdOPzMebbLcm2iCH57kXGAc/vn6VOGChNhw
+Zt+VgdpEgy/+9gEORKC0cBDT0X93oa5im1O4q5030frvAKD/3pf4Bz4qmi35U9vW
+YDan/NqIFwQAocSAWtfn2WtS6UlqHkIBdpc/Zu49DUWJ1rL4wagIhRgGlblyzqZO
+GLEhnOrBsHXj3/yxXi+sF3mdDy9Z112DK+1L1taSf8LkRFsutbxsJqumH+bTU1mw
+bjnJgd4D8/oz8ryUOc6OnwsmAqaGjWR/Mul+H8mtzKbFpVHjL8uiMs8D/R/mTe6t
+GTIQCKaninTn9ovJbRgu4V2H4wB+gvpV463W7UtnY6ypCZOxgjsw+sdRfrOKiH+T
+k5bSkRUeaJgFm3Ohfbx9g4CcnKbYLDR1M8TWAlVcb1kZqqrb4daYHz9e5jD2oeZQ
+vancOBrsTg6jj2Nrmqzns2LkV+jf/AcMZ80StClNaWNoYWVsIE1hYy1WaWNhciBQ
+cmV0dCA8bWltYWN2aWNAcHVjLmNsPohgBBARAgAYBQI7JTZaCAsDCQgHAgEKAhkB
+BRsDAAAAABIJEG1K/bpUtQOCB2VHUEcAAQGmEwCeKa3LJJqxvSg2hF18+JQS4F7i
+5mQAoMZeMPJWVSWmlU41imAGjwHoP4NyiEYEEBECAAYFAjslRx8ACgkQzR62qWZ+
+QtFkQACfbMS0sv/k91tVcXV5poK0CR+m9EsAn3ywd5cQw3eenCn1z+q3gwI3G99z
+uQINBDslNloQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmP
+QFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24
+rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhO
+SdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18
+F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsC
+RtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/46z7Jy+uxh4TiACldUV23L
+pzj0laL/HhAn+Sv02xAZW7YVgHIq21ZwyiQ9ZbEo005HqvLPch7YymIXxoYiokia
+nCgbncHtG0aeaX1T/IPPbJbwNRx2+2GCLongbNvmAO9bCWP9LVeVaT9D4LpsCium
+hAtiOw5t/Q2cAPMgN03hTLkRI2LEdZnsapLDogQo5v6oOiLEZasSrft0MMwv3l1G
+MUvxdfwrR2HM7WopJS6H4AK2W0tSBLPKFanW9KAsLVcAVFV6u3lEXjuXncOo9feF
+WUf8A4LBJr5cQBu2kKuBcy0iayCz18s46KlqxCCCWLu+gnxcTU3YNEdQF41NjdgY
+iFQEGBECAAwFAjslNloFGwwAAAAAEgkQbUr9ulS1A4IHZUdQRwABAWk6AKDmE88v
+eFoum1b8bwCADKJjNX1WzgCgqJ61xnX80EBLtAO8rQZQSBKyABSZAaIEOyVASREE
+AOANe2DTx2GMdCm/1KKy/O9E5yyvpBEU4tZ/3kZaHtzpIjs+PmZMKVRolcFGMu68
+5HQvvDyXmdUn/iTNKOV7Vp3Th4PM/ewHis0rXyXsYDBzSKNXSan8qX0XiKJtyjdw
+IgyyPKxaBSaWT51hhZC5tAD8SBOzuQVCj1BHHOiOqdLLAKD/kJnlMnX2A1LySfK7
+zX2/plKRXwP/TUV6bZZl6OqyY0E2JjUmhbGdOh+LcCMy/qP/42PIG5T2jMiFSJzH
+jL5GjTl4pRn5UAh5xVtWESof1VSuJfjxMOs3G7g4YmHrarnyzS7+2ohfk6u+umZ0
+4LVRN1xzYIzzy+yxO3yjFq5l4ztgCRj2sCldSeLJ+cnd913cWUIhu9oD/1deuUGM
+b/cdBI9+SduouUK8RyV6VGabosBfx+5/8kWCPRy1EwAkoHoc4Tv+m+XfJEwwhPCZ
+32UH+za75+pzBXVsOp+fJXyHcoWlMIYzWopP4RppP/wmEUmJ9gOMESdR72Zgbt1S
+W34V/0WPplElCz2+zPNBywkPM2nKz6P8om61tC1EdW5jYW4gTWFjLVZpY2FyIEdh
+cu1uIDxkdW5jYW5AbWFjLXZpY2FyLmNvbT6IYAQQEQIAGAUCOyVASQgLAwkIBwIB
+CgIZAQUbAwAAAAASCRDBKokdV5ub1AdlR1BHAAEB4dEAmwYpphCnjg3NgSNL12wQ
+BPixgrMOAKDDhzuQNqtkv+oqoW8du+w9Lp9G14hGBBARAgAGBQI7JUb1AAoJEM0e
+tqlmfkLRY7sAoIFRQuTnl9H/Jq+RiBSEv0BfStKGAJ9DSONVqtVA1z9yPmKVN0qp
+KR4nyrkCDQQ7JUBKEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadW
+oxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeS
+Wc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0
+iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF04
+0zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQ
+ClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/c1hY0bD1ZrwHo7Do
+3JLZZIGvHfdnEicOA5tl7vaY78ZLLHFmrE+KpMVOlqNMP4dYpC69J8wZl0PWQllO
+s0Qj8w0vBUGGuDEtfaquarVomBZrlpFy/TnAe4hSvDaDbbShnT2BSFe4XdT+gNQI
+Bffp3XoJo8IwlXVNFShzYvVags/YGPMiQCAqupQa8gU2y+1d7dw1Tl1JDMXSykTG
+4LPVbLrv9Ph/MtImmJZp2X943rtynzd6Brl/JjL02khKZ6IVxXblQmptAzSS2KVn
+MOHH56+Zh3o8c/uLw6qALWgdqMSiF+j2SbI78Bcn+/Tyy+dIlU7Qb/2pB64WnclV
+4Eew3ohUBBgRAgAMBQI7JUBKBRsMAAAAABIJEMEqiR1Xm5vUB2VHUEcAAQFFEgCe
+OFpL+b95gvDaDv0a4sP/XHXZeP0AoJbThxHBTGgiKvB0nEs1FPYagsB2mQGiBENE
+9tQRBACoPUvSF0C3Gyg93BzfjPoQzfIG7KcH+X1PMd7wsaF48B53t2V7px4MGw5W
+CwRy6S+bkz86Os3Ycxewm0a7/We/G0QmzmyOOD+f+L5s7Sr0rM1fmFVGZRKin1MX
+0s7YpX40rSHouNSYXXX2vu+o7uJvkzI8/yf16Kac4b1nP3jT5wCguV6Gl1eWkOS9
+DfwBUuIdwLObzjcD/2B9rPsuN4mhbjTAnPxz8fMKI2r+68UvEkicVIeKKauEp9JW
+defR5Yfv2koagqZC3F9550SjZw2r/Wmx0RhAA4mskuMXwIato39NK9+oXqgSVbyT
+CAb8SEgPlw60f/MctlIveRpTmSaSL37tj+UEHqOUSPoRk0VNXTML0prWpuVxA/9A
+YiJ62X/0CV3DksecKvr3hWdD3WZhoniRInYhkeokHJFP0V0WkWpHD1+brNgHE0ig
+jnbkTLWfVYDhppRccKjMiB4CxlwNoreOc2uRvO2cAzo5d0zCdJDfTNi1Ibspuf3r
+lnYFZ+Y2dhrP825FMvcS0dL2ZTVcOXPwDVVJRDB0l7QvV2lsbCdzIEdXIGNyeXB0
+byB0ZXN0IGtleSA8d3N0ZXBoZW5zb25Ac3VzZS5kZT6IYAQTEQIAIAUCQ0T21AIb
+AwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEHfQbNrv1xE4iJkAn3W4/fWpkgZf
+pit9du1vzKTaS+y6AJwLXPrzm5F9CDTX77AfCSPsvYOHYrkCDQRDRPbgEAgAveOV
+KYqnKMeC9mZEzpYPFjwxMe0qSdVAqxPvAOknzbJpDHm0O9uIf8F/IQZC+Hq/COQk
+9zowMkrHIBs93a0SSX4pV174XjshxDoYxDiUf/XTTDec43a420M3K3ji3ySj/MyI
+gOr1zu1zCWN90nFIyhBbAUqWwiEO4tHtI96pMko6RvmMZb0gsC5ERMTd5iXfvEPC
+437tNyuVokeZmZAPZ2QLJoms3rt1sRujF4vZeDORZXwrhUXGtscopNgg3C39Asw2
+eFS7waeblUdQbtH6iz3ofD+qz2PNc2wcnY5q6aeZTyB0USShG7PsQATLDhFwkeDP
+qp8g5oI+JmvPgqyAYwADBQgApLDng/rWmEKEqaUEG5sgG2fqSe7Q8tSXD/OyjXGv
+zjkurCe+gOUiWqUfPEtYRGBXGvE8VqYGveQZVjqiNxJ6l6tK8x4GKxMxC4hMFFTo
+QWoBhEw1Jy5wvOY8KoqSLnDERjXFqqTQsDEnyFjMGBl4K2XWjOYMBmSqkaDJydtv
+unqgH0yULu3ouxY7wk3ZKovDGmtt4i4MrthmYXq9W2tM1LutXh7PChJf4DVIo/0x
+SghvMQvJIELAPoIIp8h4V07sjPdvGg+n4nSAhw67KLHCR/TEqgTULH5w83Aopwbr
+yDnBKBXFh6zx3ArZu09YM19xW5J1ju4ureQG6jbly5Vmn4hIBBgRAgAJBQJDRPbg
+AhsMAAoJEHfQbNrv1xE4qNsAn2soSMFX3ZErJ4Uwwk2pg7dvYlZ0AJUYWEKOTfAc
+5kHox/B8rV0xDu90mQGiBDnu9IERBACT8Y35+2vv4MGVKiLEMOl9GdST6MCkYS3y
+EKeueNWc+z/0Kvff4JctBsgs47tjmiI9sl0eHjm3gTR8rItXMN6sJEUHWzDP+Y0P
+FPboMvKx0FXl/A0dM+HFrruCgBlWt6FA+okRySQiliuI5phwqkXefl9AhkwR8xoc
+QSVCFxcwvwCglVcOQliHu8jwRQHxlRE0tkwQQI0D+wfQwKdvhDplxHJ5nf7U8c/y
+E/vdvpN6lF0tmFrKXBUX+K7u4ifrZlQvj/81M4INjtXreqDiJtr99Rs6xa0ScZqI
+TuZC4CWxJa9GynBED3+D2t1V/f8l0smsuYoFOF7Ib49IkTdbtwAThlZp8bEhELBe
+GaPdNCcmfZ66rKUdG5sRA/9ovnc1krSQF2+sqB9/o7w5/q2qiyzwOSTnkjtBUVKn
+4zLUOf6aeBAoV6NMCC3Kj9aZHfA+ND0ehPaVGJgjaVNFhPi4x0e7BULdvgOoAqaj
+LfvkURHAeSsxXIoEmyW/xC1sBbDkDUIBSx5oej73XCZgnj/inphRqGpsb+1nKFvF
++rQoU3VTRSBQYWNrYWdlIFNpZ25pbmcgS2V5IDxidWlsZEBzdXNlLmRlPohiBBMR
+AgAiBQJA2AY+AhsDBQkObd+9BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCoTtronIAK
+ypCfAJ9RuZ6ZSV7QW4pTgTIxQ+ABPp0sIwCffG9bCNnrETPlgOn+dGEkAWegKL+I
+RgQQEQIABgUCOnBeUgAKCRCeQOMQAAqrpNzOAKCL512FZvv4VZx94TpbA9lxyoAe
+jACeOO1HIbActAevk5MUBhNeLZa/qM2JARUDBRA6cGBvd7LmAD0l09kBATWnB/9A
+n5vfiUUE1VQnt+T/EYklES3tXXaJJp9pHMa4fzFa8jPVtv5UBHGee3XoUNDVwM2O
+gSEISZxbzdXGnqIlcT08TzBUD9i579uifklLsnr35SJDZ6ram51/CWOnnaVhUzne
+OA9gTPSr+/fT3WeVnwJiQCQ30kNLWVXWATMnsnT486eAOlT6UNBPYQLpUprF5Yry
+k23pQUPAgJENDEqeU6iIO9Ot1ZPtB0lniw+/xCi13D360o1tZDYOp0hHHJN3D3EN
+8C1yPqZd5CvvznYvB6bWBIpWcRgdn2DUVMmpU661jwqGlRz1F84JG/xe4jGuzgpJ
+t9IXSzyohEJB6XG5+D0BuQINBDnu9JIQCACEkdBN6Mxf5WvqDWkcMRy6wnrd9DYJ
+8UUTmIT2iQf07tRUKJJ9v0JXfx2Z4d08IQSMNRaq4VgSe+PdYgIy0fbj23Via5/g
+O7fJEpD2hd2f+pMnOWvH2rOOIbeYfuhzAc6BQjAKtmgR0ERUTafTM9Wb6F13CNZZ
+NZfDqnFDP6L12w3z3F7FFXkz07Rs3AIto1ZfYZd4sCSpMr/0S5nLrHbIvGLp271h
+hQBeRmmoGEKO2JRelGgUJ2CUzOdtwDIKT0LbCpvaP8PVnYF5IFoYJIWRHqlEt5uc
+TXstZy7vYjL6vTP4l5xs+LIOkNmPhqmfsgLzVo0UaLt80hOwc4NvDCOLAAMGB/9g
++9V3ORzw4LvO1pwRYJqfDKUq/EJ0rNMMD4N8RLpZRhKHKJUm9nNHLbksnlZwrbST
+M5LpC/U6sheLP+l0bLVoq0lmsCcUSyh+mY6PxWirLIWCn/IAZAGnXb6Zd6TtIJlG
+G6pqUN8QxGJYQnonl0uTJKHJENbI9sWHQdcTtBMc34gorHFCo1Bcvpnc1LFLrWn7
+mfoGx6INQjf3HGQpMXAWuSBQhzkazY6vaWFpa8bBJ+gKbBuySWzNm3rFtT5HRKMW
+pO+M9bHp4d+puY0L1YwN1OMatcMMpcWnZpiWiR83oi32+xtWUY2U7Ae38mMag8zF
+bpeqPQUsDv9V7CAJ1dbriEwEGBECAAwFAkDYBnoFCQ5t3+gACgkQqE7a6JyACspn
+pgCfRbYwxT3iq+9l/PgNTUNTZOlof2oAn25y0eGi0371jap9kOV6uq71sUuOmQGi
+BEYjZk4RBACjIOtNaPzvKlC32b8R5TDRB0/FQ0tsMtt5dLwuq2ZYlEbT1YLF110v
+ZEl5IQAq5ldvD7MdR/6fqdXTdxBeYzZjeIEYbHzg3rN/N/+MkcG4W8IK1H6eDAbL
+05HlQ1ueTp0mjgoGLYKt1igQe8h5uA6gEE7dv0tG0NJx2w5Gs2GpmwCgiRius2ev
+221Pa65IpR1gsYuXLOEEAKJ1Bvjm+BfHJirqoH7iPq5HlABwn+s9sUmf6bjCkfar
+/ySAsL0VUhHNCIoHUEZd2imA2ZA0kTBxB+BIX/HMRZzxPZEwYI8Q0UYsTVb/gnQt
++mWaZs1/2teWR0wnUp+eO5MpOAO9QjFJTdIz0GegsfSOPCo55CUtktr3tJUKfZ3g
+A/9mZe+b1Evi1/Us+klnERRKR2jjWXxwuPN6UivJbfXIZjuVUNclAhEqstzpfnWJ
+3LhPxj0zJvhp/MnqSTaI6DQbr0f+JvwP+5k/4gbnqm+xxOocyhiVT45zOPAyUYuG
+4t0m+9G7Vx6LC9tMukbdfHaRym42yC2s04GW2isKfta1ZbQsWllwcCBUZXN0IEtl
+eSBQYWlyIDx6eXBwLWRldmVsQG9wZW5zdXNlLm9yZz6IYAQTEQIAIAUCRiNmTgIb
+IwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEJvswrXdsDdWSVAAnjkR2laohb2Q
+4WnxamdHYWSf8ULKAJ4jjfZsFq0vmgPsO/YHaKTJN5sAL7kBDQRGI2ZREAQAtoB5
+TGT9K7NCv5D5dQw7jVHngnxp3NGTtAhwirYphBWaF2be3UJVTLbUFW14eMnrVW9P
+Kj/HNVLhQu0C6CaXtXy5LahIls+mFlSKwbiP74cFlNYcj69tzCnaFKgElQPHcMOc
+31EgjySYcUIys421MxI++sugW+yHr5ByIsL6vfcAAwUEAILSwmLtD+Pwkues73DP
+PyWIM3MA0exO7QmZeFwnbpiZYuZQ3GiPGrbeZVqHWB72dhW8+5ugR9CVQSsLHC5w
+HMIQFU8RsiL06gZdIaJNgAr7ajhtUybP0WPVpXkzm5+VB8Che9m0Z0t2tK8Y0KVa
+pBcr3YDgx89F9VA0yny6q3WiiEkEGBECAAkFAkYjZlECGwwACgkQm+zCtd2wN1ap
+uACfUR+Daoo3N1fxxDa3A3t4OkAfpQgAn1UEvpQp+/4DnzSbEvwzLeoek3dzmQGi
+BEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7BwW4E
+qmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuciR3y
+gsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY3q7e
+R8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD++naz
+SIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbnZ0yo
+OaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9uAhgD
+A/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3sYaE
+tZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin1/4D
+ABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0Y2Fz
+ZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wCAhsD
+BgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmOlWDx
+WV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4T11K
+PE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8WxezAsj
+yTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUkyHYl
+t8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5Ly4I
+jfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUluHqD
+aCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabMjQgG
+hV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv8ZBk
+ApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAREDWb
+AzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/KiZ/C
+6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9DDtpa
+UeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/tDCe
+J1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/vAwC
+GwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6IaVSt
+jbDRYEnTHQOGsVy9mQGiBER0ULcRBADZvvVUsUUSjMyQ2fUt8Uu4eGJuPbMhEtR/
+vF7pzF/8u9Br7kh6WASBZ02dMeNoqFzPTU7zv6PiP93iOLwy2MQNOSBLkS+MscR6
+fzS6ZL81mNr+DwgOF/rLZ4tucAsBY9Z3lgRx0mWhl+XunVPXHhkR9H9Rig4wOrrz
+wBsYjjfXvwCguQ1PQ6+UQgL8STVqNaNnlVYzzUUD/336IftwogH/tKp6dV/FYP/V
+pLHXtwFee0vcCh5FwcKP0vYo9NIfB6CR2g0pyYsHBWbheMWXRVTlCaUVOhaycd+D
+XoyKl3FcxL61OD0F/feg2UEqH7n1csGV2MDGlqXsF5urKdPYcBRG56ynlWXs3W7D
+zu9JN8skhWSnXdtBFa3BBACo+CAXSzLJnFrG7kg8AYT+0k+kTS/UydwAr57QSLUc
+/blXR6seNd+TM4Kwj4Ij2pNL/LpZyzVrDS9BWNZHq3bQnERPrfXpK5XWX0quZn8z
+g1ImFwxmJduHKmtyyxNjjPR1SUFzD1EXpPfCYgRL6kU4OflbgGoL2YpmgHO6LQ/O
+GrQyb3BlblNVU0UgQnVpbGQgU2VydmljZSA8YnVpbGRzZXJ2aWNlQG9wZW5zdXNl
+Lm9yZz6IZAQTEQIAJAUCRHRQtwIbAwUJA8JnAAYLCQgHAwIDFQIDAxYCAQIeAQIX
+gAAKCRA7MBG3a51lI/ewAJkB4psDm44RckrzyMyjXEKBYXYKXQCcCLBW95t7ooAI
+yqfsg94RICpbr50=
+=NAat
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/FileChecker/hello2.txt b/tests/zypp/data/FileChecker/hello2.txt
new file mode 100644 (file)
index 0000000..43e4a03
--- /dev/null
@@ -0,0 +1,2 @@
+this is another text
+
diff --git a/tests/zypp/data/KeyRing/private.asc b/tests/zypp/data/KeyRing/private.asc
new file mode 100644 (file)
index 0000000..803f0d7
--- /dev/null
@@ -0,0 +1,33 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+lQHhBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/v4DAwKtVz8ehZCg
+rWAxhnYmmTtu8nnzDEF6BWQfS7gSUCu66LgtoWSjLfBuiZk0bSNDIrPPy6ZKvOOq
+MrHTBrQrWllwcCB0ZXN0Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3Jn
+PohgBBMRAgAgBQJGP7wCAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHY
+m9mIIb7TJQCfTe4MwrmOlWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kx
+uGn8nQJjBEY/vAwQCAD4T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVB
+pYORxyWjS8QMc9ge8WxezAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7b
+Ai4xx+NLOu9DuuU+jqUkyHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPx
+vYrFAWnesgUJMqZo7Td5Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/
+m+MTV05nT6+1KCBqCRUluHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraa
+tXyHnAyhwiwTZLq2jabMjQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSj
+ZKlHAUdnGul0M2KuyJhv8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt6
+7abbezp/Dgs8+691OtAREDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2
+HGTzVy1k33vnuGCVwC/KiZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp
+8dMJ+FFsTmzONzZpFC9DDtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+
+cFkE7hW8tyOwcx4sk1J/tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqC
+GWJV/gMDAq1XPx6FkKCtYI80yXvLiqb3UuzJbGJv5vVmdtsUcwiHhGnyWRd6AbKy
+Z0sflw+3PmpqEuAZ8LsdfJv8uIhkWh2W+eDYBrZBJ4IqQogALCf+AMCISQQYEQIA
+CQUCRj+8DAIbDAAKCRC9Ydib2Yghvk73AJ9cke9SDyJr+M+nusuWZqzdsJYr0wCc
+DHnEr0GsuEal8fCTCJuMQScd4zo=
+=7nLA
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/tests/zypp/data/KeyRing/public.asc b/tests/zypp/data/KeyRing/public.asc
new file mode 100644 (file)
index 0000000..ca2aa69
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQGiBEY/vAIRBAD2cxLY83P2G1h5TkkKYQYTLopgWQh7/7H5UK0cf62gLH7R6F7B
+wW4EqmLsm8eGE8kIOob5wCQU6pxpBMv+1UYoO1bohtx4L2JUY5ycJiq4u1CNyRuc
+iR3ygsueMRkelkQ2hpNuKvmficOcoazvU3tZM6ITJjV/tQvYTQRGqwEfwwCgs0OY
+3q7eR8NwWekaj23t8TV7hjMEANS6QMgjsp5CdLglX02oeiCG82oEKLDOWoZ2ajD+
++nazSIflJE0DaZ0W26QXewh7IRzTomV98fJV6inQNanlk5/TNuAb1elXdaYFuNbn
+Z0yoOaTJx/mb88vm63Ur8FTyKdcN+dau8yzuNlJggj5yBcNg+/8ZOAm1ZkDMlg9u
+AhgDA/0RSjXu/YNmflePFxIKBCAJFJenz4dQUZeb5cuJv20eCqnKn5CFYFU6YYg3
+sYaEtZeultDXweRveGwe28E/vpLUa7p+aZq+XwtjI6U6W5VqvkCKIUsQqwVWRHin
+1/4DABJ5rnU+yPeLXNH6jrMQ+jDG8RieI91/4n+gCX1nbwZQ/rQrWllwcCB0ZXN0
+Y2FzZSBrZXkgPHp5cHAtZGV2ZWxAb3BlbnN1c2Uub3JnPohgBBMRAgAgBQJGP7wC
+AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQvWHYm9mIIb7TJQCfTe4MwrmO
+lWDxWV3yZ6E4B9xQq0YAoIWvs4oYVzbaQzclStHai5kxuGn8uQINBEY/vAwQCAD4
+T11KPE7CzkqgGMaNP+yNQzfUDbd/SaEQ5Wce5q3VvmVBpYORxyWjS8QMc9ge8Wxe
+zAsjyTKsXl+u7e/QmMKspPzPhkVKyB6s5D8FhR1Pdo7bAi4xx+NLOu9DuuU+jqUk
+yHYlt8QF2zX98OOcCIuQc2hjk12dvfHKmUiDoUnfuQPxvYrFAWnesgUJMqZo7Td5
+Ly4IjfMJQlQ7A186BGU8bPWoV1QqUInVkNGNXLmglel/m+MTV05nT6+1KCBqCRUl
+uHqDaCiFHOUOFVWvtirmPJZ/67J78NJpF7huzXvkQraatXyHnAyhwiwTZLq2jabM
+jQgGhV8QyKd4qniSBL+jAAMGCACfH4FGqrs9pGBURmSjZKlHAUdnGul0M2KuyJhv
+8ZBkApUtPcMhZJco50pFpkqjfH7f3xXMRVDP5FpjaRt67abbezp/Dgs8+691OtAR
+EDWbAzarNNR3FbB9fUebh1J2i4W7tfBcoKwKFWJCvqX2HGTzVy1k33vnuGCVwC/K
+iZ/C6pc4DqUwCWNoZNd8hmFadJgx3CMlxSTllsaIyOXp8dMJ+FFsTmzONzZpFC9D
+DtpaUeEChCptjWwy5WkQFPe+FOLUH1BnGScQYwGlE8l+cFkE7hW8tyOwcx4sk1J/
+tDCeJ1wE8we1LXMdIiD5ugf3Jej1/98o+hQTHjfkwGqCGWJViEkEGBECAAkFAkY/
+vAwCGwwACgkQvWHYm9mIIb5O9wCfeI0Ro1UCK/CVT1/BH9NNB0TyYzAAmwdQFE6I
+aVStjbDRYEnTHQOGsVy9
+=LiUY
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/zypp/data/KeyRing/readme.txt b/tests/zypp/data/KeyRing/readme.txt
new file mode 100644 (file)
index 0000000..cfab478
--- /dev/null
@@ -0,0 +1,2 @@
+passphrase for the key pair is zypp-devel
+
diff --git a/tests/zypp/data/KeyRing/repomd.xml b/tests/zypp/data/KeyRing/repomd.xml
new file mode 100644 (file)
index 0000000..f68f8dc
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">49589f0e6569914ada9293e8c3895cb899b58a58</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">67b155adc1e7622f7962849ee43965253a797765</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">4174aed6d4ffb8cfee41b64ae357ce1db3fe904a</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">1981b7db9252974869ce1d71443bde69fa9423ca</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">2a72bebe987fb613673d9db73120e95a999f143d</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">6a328b1ec1fab195fb69035c13fe4340ee2b9cbd</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/zypp/data/KeyRing/repomd.xml.asc b/tests/zypp/data/KeyRing/repomd.xml.asc
new file mode 100644 (file)
index 0000000..9b68fbb
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQBGQFwzvWHYm9mIIb4RAnbuAJsHJkj5X8M5aOxeZ2KUsHjhQ1vKOACdFrQ0
+Vxp3Zznn3w4j5SaGAqpyGWA=
+=iu9H
+-----END PGP SIGNATURE-----
diff --git a/tests/zypp/data/KeyRing/repomd.xml.corrupted b/tests/zypp/data/KeyRing/repomd.xml.corrupted
new file mode 100644 (file)
index 0000000..b655532
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repomd xmlns="http://linux.duke.edu/metadata/repo">
+  <data type="other">
+    <location href="repodata/other.xml.gz"/>
+    <checksum type="sha">49589f0e6569914ada9293e8c3895cb899b58a58</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">67b855adc1e7622f7962849ee43965253a797765</open-checksum>
+  </data>
+  <data type="filelists">
+    <location href="repodata/filelists.xml.gz"/>
+    <checksum type="sha">4174aed6d4ffb8cfee41b64ae357ce1db3fe904a</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">1981b7db9252974869ce1d71443bde69fa9423ca</open-checksum>
+  </data>
+  <data type="primary">
+    <location href="repodata/primary.xml.gz"/>
+    <checksum type="sha">2a72bebe987fb613673d9db73120e95a999f143d</checksum>
+    <timestamp>1177395604</timestamp>
+    <open-checksum type="sha">6a328b1ec1fab195fb69035c13fe4340ee2b9cbd</open-checksum>
+  </data>
+</repomd>
diff --git a/tests/zypp/data/Locks/locks b/tests/zypp/data/Locks/locks
new file mode 100644 (file)
index 0000000..464cd99
--- /dev/null
@@ -0,0 +1 @@
+query_string: zypper
diff --git a/tests/zypp/data/PoolQuery/savedqueries b/tests/zypp/data/PoolQuery/savedqueries
new file mode 100644 (file)
index 0000000..79c689b
--- /dev/null
@@ -0,0 +1,12 @@
+solvable_name: tool
+repo: vbox
+repo: zyppsvn
+
+query_string: ma*
+repo: opensuse
+type: patch
+match_type: regex
+require_all: on
+case_sensitive: on
+install_status: not-installed
+version: != 0.8.3
diff --git a/tests/zypp/data/RepoManager/plugin-service-lib-1/services/service b/tests/zypp/data/RepoManager/plugin-service-lib-1/services/service
new file mode 100755 (executable)
index 0000000..a5bb647
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+echo "
+[repo1]
+name=Repository1
+baseurl=http://somehost.com/repo1
+type=rpmmd
+
+[repo12]
+name=Repository2
+baseurl=http://somehost.com/repo2
+type=rpmmd
+"
+
diff --git a/tests/zypp/data/RepoManager/plugin-service-lib-2/services/service b/tests/zypp/data/RepoManager/plugin-service-lib-2/services/service
new file mode 100755 (executable)
index 0000000..97db399
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+echo "
+[repo3]
+name=Repository3
+baseurl=http://somehost.com/repo3
+type=rpmmd
+"
+
diff --git a/tests/zypp/data/RepoManager/proprietary.repo b/tests/zypp/data/RepoManager/proprietary.repo
new file mode 100644 (file)
index 0000000..0392ffe
--- /dev/null
@@ -0,0 +1,14 @@
+[macromedia]
+name=Macromedia for i386 Linux
+baseurl=http://macromedia.rediris.es/rpm/
+enabled=1
+gpgcheck=1
+gpgkey=http://macromedia.mplug.org/FEDORA-GPG-KEY
+
+[office]
+name=Microsoft Office for Linux
+baseurl=http://www.microsoft.com/linux/office
+enabled=1
+gpgcheck=1
+gpgkey=http://www.microsoft.com/~sballmer/gpgkey.txt
+
diff --git a/tests/zypp/data/RepoManager/repo/repoindex.xml b/tests/zypp/data/RepoManager/repo/repoindex.xml
new file mode 100644 (file)
index 0000000..06632c8
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repoindex>
+  <repo name="SLES10-SP2-Online" 
+        alias="SLES10-SP2-Online" 
+        description="SLES10-SP2-Online for sles-10-i586" 
+        distro_target="sles-10-i586" path="$RCE/SLES10-SP2-Online/sles-10-i586" 
+        priority="0" 
+        pub="0" />
+  <repo name="SLE10-SP2-Debuginfo-Updates" 
+        alias="SLE10-SP2-Debuginfo-Updates" 
+        description="SLE10-SP2-Debuginfo-Updates for sles-10-i586" 
+        distro_target="sles-10-i586"
+        path="$RCE/SLE10-SP2-Debuginfo-Updates/sles-10-i586" 
+        priority="0" 
+        pub="0" />
+  <repo name="SLES10-SP2-Updates" 
+        alias="SLES10-SP2-Updates" 
+        description="SLES10-SP2-Updates for sles-10-i586" 
+        distro_target="sles-10-i586" 
+        path="$RCE/SLES10-SP2-Updates/sles-10-i586" 
+        priority="0" 
+        pub="0" />
+</repoindex>
+
diff --git a/tests/zypp/data/RepoManager/repos.d/filesharing.repo b/tests/zypp/data/RepoManager/repos.d/filesharing.repo
new file mode 100644 (file)
index 0000000..7571d48
--- /dev/null
@@ -0,0 +1,7 @@
+[filesharing]
+name=Filesharing applications (SUSE_Factory)
+type=rpm-md
+baseurl=http://software.opensuse.org/download/filesharing/SUSE_Factory/
+gpgcheck=1
+gpgkey=http://software.opensuse.org/openSUSE-Build-Service.asc
+enabled=1
diff --git a/tests/zypp/data/RepoManager/repos.d/home:dmacvicar.repo b/tests/zypp/data/RepoManager/repos.d/home:dmacvicar.repo
new file mode 100644 (file)
index 0000000..3a79b29
--- /dev/null
@@ -0,0 +1,7 @@
+[home:dmacvicar]
+name=Duncan Mac-Vicar SUSE rpms (openSUSE_Factory)
+type=rpm-md
+baseurl=http://software.opensuse.org/download/home:/dmacvicar/openSUSE_Factory/
+gpgcheck=1
+gpgkey=http://software.opensuse.org/openSUSE-Build-Service.asc
+enabled=1
diff --git a/tests/zypp/data/RepoManager/repos.d/proprietary.repo b/tests/zypp/data/RepoManager/repos.d/proprietary.repo
new file mode 100644 (file)
index 0000000..e78c242
--- /dev/null
@@ -0,0 +1,5 @@
+
+[adobe]
+name=acrobat reader
+baseurl=http://www.adobe.com/reader/linux
+
diff --git a/tests/zypp/data/RepoManager/repos.d/ruby.repo b/tests/zypp/data/RepoManager/repos.d/ruby.repo
new file mode 100644 (file)
index 0000000..435ea1a
--- /dev/null
@@ -0,0 +1,7 @@
+[ruby]
+name=Ruby is an Interpreted Object-Oriented Scripting Language (openSUSE_Factory)
+type=rpm-md
+baseurl=http://software.opensuse.org/download/ruby/openSUSE_Factory/
+gpgcheck=1
+gpgkey=http://software.opensuse.org/openSUSE-Build-Service.asc
+enabled=1
diff --git a/tests/zypp/data/RepoManager/second/repo/repoindex.xml b/tests/zypp/data/RepoManager/second/repo/repoindex.xml
new file mode 100644 (file)
index 0000000..3e87256
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<repoindex>
+  <repo name="SLES10-SP2-Online" 
+        alias="SLES10-SP2-Online" 
+        description="SLES10-SP2-Online for sles-10-i586" 
+        distro_target="sles-10-i586" path="$RCE/SLES10-SP2-Online/sles-10-i586" 
+        priority="0" 
+        pub="0" />
+  <repo name="SLES10-SP2-Updates" 
+        alias="SLES10-SP2-Updates" 
+        description="SLES10-SP2-Updates for sles-10-i586" 
+        distro_target="sles-10-i586" 
+        path="$RCE/SLES10-SP2-Updates/sles-10-i586" 
+        priority="0" 
+        pub="0" />
+</repoindex>
+
diff --git a/tests/zypp/data/Target/product.prod b/tests/zypp/data/Target/product.prod
new file mode 100644 (file)
index 0000000..2e66b62
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<product schemeversion="0.0">
+    <vendor>Novell</vendor>
+    <name>SUSE_SLES</name>
+    <version>10</version>
+    <release>0</release>
+    <arch>i586</arch>
+    <productline>sles</productline>
+    <register>
+        <target>sle-10-i586</target>
+        <!-- release is optional and normally empty -->
+        <release>special_edition</release>
+    </register>
+    <updaterepokey>A43242OOO</updaterepokey>
+    <summary>A cool distribution</summary>
+    <summary lang="de">Einfach genial</summary>
+    <description>This is the coolest distribution on the world.
+        Try it out and find it useful.</description>
+    <description lang="de">Was soll man da sagen?
+        Einfach toll!</description>
+    <linguas>
+      <lang>da</lang>
+      <lang>de</lang>
+    </linguas>
+    <urls>
+      <url name="releasenotes">http://www.suse.com/relnotes/i386/SLE/SERVER/11/release-notes-sles.rpm</url>
+      <url name="extraurls">http://foo</url>
+    </urls>
+    <buildconfig producttheme="SuSE" betaversion="-Alpha1plus">
+      <linguas>
+        <lang>af</lang>
+      </linguas>
+    </buildconfig>
+    <installconfig defaultlang="en_US" />
+</product>
+
diff --git a/tests/zypp/data/Vendor/vendors.d/ati b/tests/zypp/data/Vendor/vendors.d/ati
new file mode 100644 (file)
index 0000000..57f8a7b
--- /dev/null
@@ -0,0 +1,3 @@
+[main]
+
+vendors = ati,ati_new,ati_old
\ No newline at end of file
diff --git a/tests/zypp/data/Vendor/vendors.d/nvidia b/tests/zypp/data/Vendor/vendors.d/nvidia
new file mode 100644 (file)
index 0000000..772ef16
--- /dev/null
@@ -0,0 +1,3 @@
+[main]
+
+vendors = nvidia,suse,old_suse
\ No newline at end of file
diff --git a/tests/zypp/data/Vendor/zypp1.conf b/tests/zypp/data/Vendor/zypp1.conf
new file mode 100644 (file)
index 0000000..5c017e0
--- /dev/null
@@ -0,0 +1,126 @@
+## Configuration file for software management
+## /etc/zypp/zypp.conf
+##
+## Boolean values are 0 1 yes no on off true false
+
+
+[main]
+
+
+##
+## Override the detected architecture
+##
+## Valid values:  i586, i686, x86_64, ppc, ppc64, ia64, s390, s390x, ..
+## Default value: Autodetected
+##
+## ** CAUTION: Only set if you know what you're doing !
+## ** Changing this needs a full refresh (incl. download)
+## ** of all repository data.
+##
+# arch = s390
+
+
+##
+## Path where the repo metadata is downloaded and kept.
+##
+## Valid values: A directory
+## Default value: /var/cache/zypp/raw
+##
+## Changing this needs a full refresh (incl. download) of all repository data
+##
+# metadatadir = /var/cache/zypp/raw
+
+
+##
+## Path where the known repositories .repo files are kept
+##
+## Valid values: A directory
+## Default value: /etc/zypp/repos.d
+##
+## Changing this invalidates all known repositories
+##
+# reposdir = /etc/zypp/repos.d
+
+
+##
+## Path where the processed cache is kept (this is where zypp.db is located)
+##
+## Valid values: A directory
+## Default value: /var/cache/zypp
+##
+## Changing this needs a full refresh (except download) of all repository data
+##
+# cachedir = /var/cache/zypp
+
+
+##
+## Whether repository urls should be probed when added
+##
+## Valid values: boolean
+## Default value: false
+##
+## If true, accessability of repositories is checked immediately (when added)
+##   (e.g. 'zypper ar' will check immediately)
+## If false, accessability of repositories is checked when refreshed
+##   (e.g. 'zypper ar' will delay the check until the next refresh)
+##
+# repo.add.probe = false
+
+
+##
+## Amount of time in minutes that must pass before another refresh.
+##
+## Valid values: Integer
+## Default value: 10
+##
+## If you have autorefresh enabled for a repository, it is checked for
+## up-to-date metadata not more often than every <repo.refresh.delay>
+## minutes. If an automatic request for refresh comes before <repo.refresh.delay>
+## minutes passed since the last check, the request is ignored.
+##
+## A value of 0 means the repository will always be checked. To get the oposite
+## effect, disable autorefresh for your repositories.
+##
+## This option has no effect for repositories with autorefresh disabled, nor for
+## user-requested refresh.
+##
+# repo.refresh.delay = 10
+
+
+##
+## Whether to consider using a .patch.rpm when downloading a package
+##
+## Valid values: boolean
+## Default value: true
+##
+## Using a patch rpm will decrease the download size for package updates
+## since it does not contain all files of the package but only the changed
+## ones. The .patch.rpm is ready to be installed immediately after download.
+## There is no further processing needed, as it is for a .delta.rpm.
+##
+# download.use_patchrpm = true
+
+
+##
+## Whether to consider using a .delta.rpm when downloading a package
+##
+## Valid values: boolean
+## Default value: true
+##
+## Using a delta rpm will decrease the download size for package updates
+## since it does not contain all files of the package but only the binary
+## diff of changed ones. Recreating the rpm package on the local machine
+## is an expensive operation (memory,CPU). If your network connection is
+## not too slow, you benefit from disabling .delta.rpm.
+##
+# download.use_deltarpm = true
+
+
+## 
+## Defining directory for equivalent vendors
+##
+## Valid values: A directory
+## Default value: /etc/zypp/vondors.d
+##
+vendordir = data/Vendor/notExist
+
diff --git a/tests/zypp/data/Vendor/zypp2.conf.cmake b/tests/zypp/data/Vendor/zypp2.conf.cmake
new file mode 100644 (file)
index 0000000..564a841
--- /dev/null
@@ -0,0 +1,127 @@
+## Configuration file for software management
+## /etc/zypp/zypp.conf
+##
+## Boolean values are 0 1 yes no on off true false
+
+
+[main]
+
+
+##
+## Override the detected architecture
+##
+## Valid values:  i586, i686, x86_64, ppc, ppc64, ia64, s390, s390x, ..
+## Default value: Autodetected
+##
+## ** CAUTION: Only set if you know what you're doing !
+## ** Changing this needs a full refresh (incl. download)
+## ** of all repository data.
+##
+# arch = s390
+
+
+##
+## Path where the repo metadata is downloaded and kept.
+##
+## Valid values: A directory
+## Default value: /var/cache/zypp/raw
+##
+## Changing this needs a full refresh (incl. download) of all repository data
+##
+# metadatadir = /var/cache/zypp/raw
+
+
+##
+## Path where the known repositories .repo files are kept
+##
+## Valid values: A directory
+## Default value: /etc/zypp/repos.d
+##
+## Changing this invalidates all known repositories
+##
+# reposdir = /etc/zypp/repos.d
+
+
+##
+## Path where the processed cache is kept (this is where zypp.db is located)
+##
+## Valid values: A directory
+## Default value: /var/cache/zypp
+##
+## Changing this needs a full refresh (except download) of all repository data
+##
+# cachedir = /var/cache/zypp
+
+
+##
+## Whether repository urls should be probed when added
+##
+## Valid values: boolean
+## Default value: false
+##
+## If true, accessability of repositories is checked immediately (when added)
+##   (e.g. 'zypper ar' will check immediately)
+## If false, accessability of repositories is checked when refreshed
+##   (e.g. 'zypper ar' will delay the check until the next refresh)
+##
+# repo.add.probe = false
+
+
+##
+## Amount of time in minutes that must pass before another refresh.
+##
+## Valid values: Integer
+## Default value: 10
+##
+## If you have autorefresh enabled for a repository, it is checked for
+## up-to-date metadata not more often than every <repo.refresh.delay>
+## minutes. If an automatic request for refresh comes before <repo.refresh.delay>
+## minutes passed since the last check, the request is ignored.
+##
+## A value of 0 means the repository will always be checked. To get the oposite
+## effect, disable autorefresh for your repositories.
+##
+## This option has no effect for repositories with autorefresh disabled, nor for
+## user-requested refresh.
+##
+# repo.refresh.delay = 10
+
+
+##
+## Whether to consider using a .patch.rpm when downloading a package
+##
+## Valid values: boolean
+## Default value: true
+##
+## Using a patch rpm will decrease the download size for package updates
+## since it does not contain all files of the package but only the changed
+## ones. The .patch.rpm is ready to be installed immediately after download.
+## There is no further processing needed, as it is for a .delta.rpm.
+##
+# download.use_patchrpm = true
+
+
+##
+## Whether to consider using a .delta.rpm when downloading a package
+##
+## Valid values: boolean
+## Default value: true
+##
+## Using a delta rpm will decrease the download size for package updates
+## since it does not contain all files of the package but only the binary
+## diff of changed ones. Recreating the rpm package on the local machine
+## is an expensive operation (memory,CPU). If your network connection is
+## not too slow, you benefit from disabling .delta.rpm.
+##
+# download.use_deltarpm = true
+
+
+## 
+## Defining directory for equivalent vendors
+##
+## Valid values: A directory
+## Default value: /etc/zypp/vondors.d
+##
+#vendordir = ./../../tests/zypp/data/Vendor/vendors.d
+vendordir = @VENDOR_D@
+
diff --git a/tests/zypp/data/mediasetaccess/src1/cd1/dir/file1 b/tests/zypp/data/mediasetaccess/src1/cd1/dir/file1
new file mode 100644 (file)
index 0000000..55efc3e
--- /dev/null
@@ -0,0 +1 @@
+this is a file from dir
diff --git a/tests/zypp/data/mediasetaccess/src1/cd1/dir/file2 b/tests/zypp/data/mediasetaccess/src1/cd1/dir/file2
new file mode 100644 (file)
index 0000000..2ea4f1e
--- /dev/null
@@ -0,0 +1 @@
+Occifer! I'm not as think as you stoned I am!
diff --git a/tests/zypp/data/mediasetaccess/src1/cd1/dir/subdir/file b/tests/zypp/data/mediasetaccess/src1/cd1/dir/subdir/file
new file mode 100644 (file)
index 0000000..edb5a3b
--- /dev/null
@@ -0,0 +1 @@
+this is a file from subdir
diff --git a/tests/zypp/data/mediasetaccess/src1/cd1/test.txt b/tests/zypp/data/mediasetaccess/src1/cd1/test.txt
new file mode 100644 (file)
index 0000000..cf9a2f2
--- /dev/null
@@ -0,0 +1 @@
+this is cd1
\ No newline at end of file
diff --git a/tests/zypp/data/mediasetaccess/src1/cd1/x.media1 b/tests/zypp/data/mediasetaccess/src1/cd1/x.media1
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/zypp/data/mediasetaccess/src1/cd2/test.txt b/tests/zypp/data/mediasetaccess/src1/cd2/test.txt
new file mode 100644 (file)
index 0000000..5687ff9
--- /dev/null
@@ -0,0 +1 @@
+this is cd2
\ No newline at end of file
diff --git a/tests/zypp/data/mediasetaccess/src1/cd2/x.mediabad b/tests/zypp/data/mediasetaccess/src1/cd2/x.mediabad
new file mode 100644 (file)
index 0000000..deec169
--- /dev/null
@@ -0,0 +1 @@
+.media2 would identify the cd2 correctly
\ No newline at end of file
diff --git a/tests/zypp/data/mediasetaccess/src1/cd3/test.txt b/tests/zypp/data/mediasetaccess/src1/cd3/test.txt
new file mode 100644 (file)
index 0000000..6e7f9b2
--- /dev/null
@@ -0,0 +1 @@
+this is cd3
\ No newline at end of file
diff --git a/tests/zypp/data/mediasetaccess/src1/cd3/x.media3 b/tests/zypp/data/mediasetaccess/src1/cd3/x.media3
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/zypp/data/mediasetaccess/src2/test.txt b/tests/zypp/data/mediasetaccess/src2/test.txt
new file mode 100644 (file)
index 0000000..bd99b59
--- /dev/null
@@ -0,0 +1 @@
+this is a single media
\ No newline at end of file
diff --git a/tests/zypp/data/mediasetaccess/src2/x.media b/tests/zypp/data/mediasetaccess/src2/x.media
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2c2a3da
--- /dev/null
@@ -0,0 +1,22 @@
+
+ADD_SUBDIRECTORY( package-manager )
+
+INSTALL( FILES notify-message DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/zypp" )
+
+## ############################################################
+
+FILE( GLOB ALLCC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" )
+STRING( REPLACE ".cc" ";" APLLPROG ${ALLCC} )
+FOREACH( loop_var ${APLLPROG} )
+  ADD_EXECUTABLE( ${loop_var}
+    ${loop_var}.cc
+  )
+  TARGET_LINK_LIBRARIES( ${loop_var}
+    zypp
+    ${Boost_PROGRAM_OPTIONS_LIBRARY}
+  )
+ENDFOREACH( loop_var )
+
+## ############################################################
+
+INSTALL(TARGETS zypp-CheckAccessDeleted DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
diff --git a/tools/DumpSelectable.cc b/tools/DumpSelectable.cc
new file mode 100644 (file)
index 0000000..60b2402
--- /dev/null
@@ -0,0 +1,138 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+#include <algorithm>
+#include <zypp/PoolQuery.h>
+
+static std::string appname( "DumpSelectable" );
+
+#define message cout
+using std::flush;
+
+int errexit( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  return exit_r;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  cerr << "Usage: " << appname << " [--root ROOTDIR] [OPTIONS] NAME..." << endl;
+  cerr << "  Load all enabled repositories (no refresh) and search for" << endl;
+  cerr << "  Selectables names NAME" << endl;
+  cerr << "  --root   Load repos from the system located below ROOTDIR. If ROOTDIR" << endl;
+  cerr << "           denotes a sover testcase, the testcase is loaded." << endl;
+  cerr << "" << endl;
+  return exit_r;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc,++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ZConfig::instance();
+  Pathname sysRoot("/");
+  sat::Pool satpool( sat::Pool::instance() );
+
+  if ( (*argv) == std::string("--root") )
+  {
+    --argc,++argv;
+    if ( ! argc )
+      return errexit("--root requires an argument.");
+
+    if ( ! PathInfo( *argv ).isDir() )
+      return errexit("--root requires a directory.");
+
+    sysRoot = *argv;
+    --argc,++argv;
+  }
+
+  if ( TestSetup::isTestcase( sysRoot ) )
+  {
+    message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test;
+    test.loadTestcaseRepos( sysRoot );
+  }
+  else if ( TestSetup::isTestSetup( sysRoot ) )
+  {
+    message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test( sysRoot, Arch_x86_64 );
+    test.loadRepos();
+  }
+  else
+  {
+    // a system
+    message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
+    if ( true )
+    {
+      message << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
+      getZYpp()->initializeTarget( sysRoot );
+      getZYpp()->target()->load();
+      message << satpool.systemRepo() << endl;
+    }
+
+    if ( true )
+    {
+      RepoManager repoManager( sysRoot );
+      RepoInfoList repos = repoManager.knownRepositories();
+      for_( it, repos.begin(), repos.end() )
+      {
+        RepoInfo & nrepo( *it );
+
+        if ( ! nrepo.enabled() )
+          continue;
+
+        if ( ! repoManager.isCached( nrepo ) )
+        {
+          message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+
+        message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
+        try
+        {
+          repoManager.loadFromCache( nrepo );
+          message << satpool.reposFind( nrepo.alias() ) << endl;
+        }
+        catch ( const Exception & exp )
+        {
+          message << exp.asString() + "\n" + exp.historyAsString() << endl;
+          message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+      }
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  for ( ; argc; --argc,++argv )
+  {
+    message << dump( ui::Selectable::get( IdString( *argv ) ) ) << endl;
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
diff --git a/tools/NameReqPrv.cc b/tools/NameReqPrv.cc
new file mode 100644 (file)
index 0000000..36a818f
--- /dev/null
@@ -0,0 +1,263 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+#include <algorithm>
+#include <zypp/PoolQuery.h>
+
+static std::string appname( "NameReqPrv" );
+
+#define message cout
+using std::flush;
+
+int errexit( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  return exit_r;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  cerr << "Usage: " << appname << " [--root ROOTDIR] [OPTIONS] NAME... [[OPTIONS] NAME...]..." << endl;
+  cerr << "  Load all enabled repositories (no refresh) and search for" << endl;
+  cerr << "  occurrences of NAME (regex) in package names, provides or" << endl;
+  cerr << "  requires." << endl;
+  cerr << "  --root   Load repos from the system located below ROOTDIR. If ROOTDIR" << endl;
+  cerr << "           denotes a sover testcase, the testcase is loaded." << endl;
+  cerr << "  --installed Process installed packages only." << endl;
+  cerr << "  -i/-I    turn on/off case insensitive search (default on)" << endl;
+  cerr << "  -n/-N    turn on/off looking for names       (default on)" << endl;
+  cerr << "  -p/-P    turn on/off looking for provides    (default off)" << endl;
+  cerr << "  -r/-R    turn on/off looking for requires    (default off)" << endl;
+  cerr << "  -c/-C    turn on/off looking for conflicts   (default off)" << endl;
+  cerr << "  -o/-O    turn on/off looking for obsoletes   (default off)" << endl;
+  cerr << "  -m/-M    turn on/off looking for recommends  (default off)" << endl;
+  cerr << "  -s/-S    turn on/off looking for supplements (default off)" << endl;
+  cerr << "  -a       short for -n -p -r" << endl;
+  cerr << "  -A       short for -n -P -R" << endl;
+  cerr << "" << endl;
+  return exit_r;
+}
+
+void tableOut( const std::string & s1 = std::string(),
+               const std::string & s2 = std::string(),
+               const std::string & s3 = std::string(),
+               const std::string & s4 = std::string(),
+               const std::string & s5 = std::string() )
+{
+  message << "  ";
+#define TABEL(N) static unsigned w##N = 0; if ( ! s##N.empty() ) w##N = std::max( w##N, unsigned(s##N.size()) ); message << str::form( " %-*s ", w##N, s##N.c_str() )
+#define TABER(N) static unsigned w##N = 0; if ( ! s##N.empty() ) w##N = std::max( w##N, unsigned(s##N.size()) ); message << str::form( " %*s ", w##N, s##N.c_str() )
+  TABER( 1 ); TABEL( 2 ); TABEL( 3 ); TABEL( 4 ); TABEL( 5 );
+#undef TABEL
+  message << endl;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc,++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ZConfig::instance();
+  Pathname sysRoot("/");
+  sat::Pool satpool( sat::Pool::instance() );
+
+  if ( argc && (*argv) == std::string("--root") )
+  {
+    --argc,++argv;
+    if ( ! argc )
+      return errexit("--root requires an argument.");
+
+    if ( ! PathInfo( *argv ).isDir() )
+      return errexit("--root requires a directory.");
+
+    sysRoot = *argv;
+    --argc,++argv;
+  }
+
+  bool onlyInstalled( false );
+  if ( argc && (*argv) == std::string("--installed") )
+  {
+    --argc,++argv;
+    onlyInstalled = true;
+  }
+
+  if ( TestSetup::isTestcase( sysRoot ) )
+  {
+    message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test;
+    test.loadTestcaseRepos( sysRoot );
+    dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  }
+  else if ( TestSetup::isTestSetup( sysRoot ) )
+  {
+    message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test( sysRoot, Arch_x86_64 );
+    test.loadRepos();
+    dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  }
+  else
+  {
+    // a system
+    message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
+    if ( true )
+    {
+      message << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
+      getZYpp()->initializeTarget( sysRoot );
+      getZYpp()->target()->load();
+      message << satpool.systemRepo() << endl;
+    }
+
+    if ( !onlyInstalled )
+    {
+      RepoManager repoManager( sysRoot );
+      RepoInfoList repos = repoManager.knownRepositories();
+      for_( it, repos.begin(), repos.end() )
+      {
+        RepoInfo & nrepo( *it );
+
+        if ( ! nrepo.enabled() )
+          continue;
+
+        if ( ! repoManager.isCached( nrepo ) )
+        {
+          message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+
+        message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
+        try
+        {
+          repoManager.loadFromCache( nrepo );
+          message << satpool.reposFind( nrepo.alias() ) << endl;
+        }
+        catch ( const Exception & exp )
+        {
+          message << exp.asString() + "\n" + exp.historyAsString() << endl;
+          message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+      }
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  bool ignorecase      ( true );
+  bool names           ( true );
+  bool provides                ( false );
+  bool requires                ( false );
+  bool conflicts       ( false );
+  bool obsoletes       ( false );
+  bool recommends      ( false );
+  bool supplements     ( false );
+
+  for ( ; argc; --argc,++argv )
+  {
+    if ( (*argv)[0] == '-' )
+    {
+      switch ( (*argv)[1] )
+      {
+        case 'a':  names =     true,   requires = provides =   true;   break;
+        case 'A':  names =     true,   requires = provides =   false;  break;
+        case 'i': ignorecase = true;   break;
+        case 'I': ignorecase = false;  break;
+        case 'n': names =      true;   break;
+        case 'N': names =      false;  break;
+        case 'r': requires =   true;   break;
+        case 'R': requires =   false;  break;
+        case 'p': provides =   true;   break;
+        case 'P': provides =   false;  break;
+        case 'c': conflicts =  true;   break;
+        case 'C': conflicts =  false;  break;
+        case 'o': obsoletes =  true;   break;
+        case 'O': obsoletes =  false;  break;
+        case 'm': recommends = true;   break;
+        case 'M': recommends = false;  break;
+        case 's': supplements =        true;   break;
+        case 'S': supplements =        false;  break;
+      }
+      continue;
+    }
+
+    PoolQuery q;
+    if ( onlyInstalled )
+      q.setInstalledOnly();
+    std::string qstr( *argv );
+
+    if ( *argv == ResKind::product )
+    {
+      q.addKind( ResKind::product );
+    }
+    else if ( *argv == ResKind::patch )
+    {
+      q.addKind( ResKind::patch );
+    }
+    else if ( *argv == ResKind::pattern )
+    {
+      q.addKind( ResKind::pattern );
+    }
+    else
+    {
+    q.addString( qstr );
+    q.setMatchRegex();
+    q.setCaseSensitive( ! ignorecase );
+
+    if ( names )
+      q.addAttribute( sat::SolvAttr::name );
+    if ( provides )
+      q.addDependency( sat::SolvAttr::provides );
+    if ( requires )
+      q.addDependency( sat::SolvAttr::requires );
+    if ( conflicts )
+      q.addDependency( sat::SolvAttr::conflicts );
+    if ( obsoletes )
+      q.addDependency( sat::SolvAttr::obsoletes );
+    if ( recommends )
+      q.addDependency( sat::SolvAttr::recommends );
+    if ( supplements )
+      q.addDependency( sat::SolvAttr::supplements );
+    }
+
+    message << *argv << " [" << (ignorecase?'i':'_') << (names?'n':'_') << (requires?'r':'_') << (provides?'p':'_')
+           << (conflicts?'c':'_') << (obsoletes?'o':'_') << (recommends?'m':'_') << (supplements?'s':'_') << "] {" << endl;
+
+    for_( it, q.begin(), q.end() )
+    {
+      tableOut( str::numstring( it->id() ), it->asString(), it->repository().name(), it->vendor().asString(),
+                str::numstring( PoolItem(*it)->buildtime() ) );
+      if ( ! it.matchesEmpty() )
+      {
+        for_( match, it.matchesBegin(), it.matchesEnd() )
+        {
+          tableOut( "", "", "", match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() );
+        }
+      }
+    }
+
+    message << "}" << endl;
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
diff --git a/tools/ToolScanRepos.cc b/tools/ToolScanRepos.cc
new file mode 100644 (file)
index 0000000..0acfdbd
--- /dev/null
@@ -0,0 +1,134 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+static std::string appname( "ToolScanRepos" );
+
+void message( const std::string & msg_r )
+{
+  cerr << "*** " << msg_r << endl;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl;
+    message( msg_r );
+    cerr << endl;
+  }
+  cerr << "Usage: " << appname << "[OPTIONS] URL..." << endl;
+  cerr << "  Load repos from URL to test system below /tmp/" << appname << "." << endl;
+  cerr << "  -r ROOT  Use /tmp/ROOT as location of test system (default: " << appname << ")." << endl;
+  cerr << "  -a ARCH  Use ARCH as test system architecture (default: x86_64)." << endl;
+  cerr << "  -c       Clear an existing test system (default)." << endl;
+  cerr << "  -n       Do not clear an existing test system but reuse it." << endl;
+  return exit_r;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc;
+  ++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  Pathname mtmp( "/tmp" );
+  Pathname mroot( mtmp/appname );
+  Arch     march( Arch_x86_64 );
+  bool     oClearRoot = true;
+
+  std::vector<std::string> urls;
+
+  while ( argc )
+  {
+    if ( argv[0] == std::string("-c") )
+    {
+      oClearRoot = true;
+    }
+    else if ( argv[0] == std::string("-n") )
+    {
+      oClearRoot = false;
+    }
+    else if ( argv[0] == std::string("-r") || argv[0] == std::string("--root"))
+    {
+      --argc;
+      ++argv;
+      if ( ! argc )
+        return usage( "Missing arg to -r ROOT", 101 );
+
+      if ( *(argv[0]) ) // empty
+        mroot = mtmp/argv[0];
+      else
+        mroot = mtmp/appname;
+    }
+     else if ( argv[0] == std::string("-a") )
+    {
+      --argc;
+      ++argv;
+      if ( ! argc )
+        return usage( "Missing arg to -a ARCH", 101 );
+
+      if ( *(argv[0]) ) // empty
+        march = Arch( argv[0] );
+      else
+        march = Arch_x86_64;
+    }
+   else
+    {
+      urls.push_back( argv[0] );
+    }
+    --argc;
+    ++argv;
+  }
+
+  if ( urls.empty() )
+  {
+    return usage( "Missing URLs", 102 );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( oClearRoot )
+  {
+    message( "Clear test system at " + mroot.asString() );
+    filesystem::recursive_rmdir( mroot );
+  }
+  else
+  {
+    message( "Use test system at " + mroot.asString() );
+  }
+  filesystem::assert_dir( mroot );
+
+  message( "Use archiecture " + march.asString() );
+  TestSetup test( mroot, march );
+
+  int ret = 0;
+  for_( it, urls.begin(), urls.end() )
+  {
+    message( "Setup " + *it );
+    try
+    {
+      test.loadRepo( *it );
+    }
+    catch ( const Exception & exp )
+    {
+      message( exp.asString() + "\n" + exp.historyAsString() );
+      ++ret;
+    }
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return ret;
+}
\ No newline at end of file
diff --git a/tools/migrate-sources/CMakeLists.txt b/tools/migrate-sources/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d89ebeb
--- /dev/null
@@ -0,0 +1,9 @@
+SET( migrate_SRCS
+migrate-sources.cc
+)
+
+ADD_EXECUTABLE( zypp-migrate-sources ${migrate_SRCS} )
+TARGET_LINK_LIBRARIES( zypp-migrate-sources zypp )
+TARGET_LINK_LIBRARIES( zypp-migrate-sources xml2 )
+
+INSTALL(TARGETS zypp-migrate-sources RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/zypp )
\ No newline at end of file
diff --git a/tools/migrate-sources/migrate-sources.cc b/tools/migrate-sources/migrate-sources.cc
new file mode 100644 (file)
index 0000000..49bdfc9
--- /dev/null
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ZYpp.h"
+#include "zypp/zypp_detail/ZYppReadOnlyHack.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/PathInfo.h"
+#include "zypp/RepoManager.h"
+#include "zypp/cache/CacheFSCK.h"
+
+#include "zypp/parser/xmlstore/XMLSourceCacheParser.h"
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp-cache"
+
+
+using namespace std;
+using namespace zypp;
+
+struct Options
+{
+  Options()
+  : fake(false)
+  , root("/")
+  , sources_dir("/var/lib/zypp/db/sources")
+  {}
+
+  bool fake;
+  Pathname root;
+  Pathname sources_dir;
+};
+
+static void clear_cache( const Options &opt )
+{
+  Pathname path = opt.root + "/var/lib/zypp/cache";
+  if ( PathInfo(path).isDir() )
+  {
+    cout << "Deleting old cache directory (" << path << ")." << endl;
+    if ( ! opt.fake )
+    {
+      if ( filesystem::recursive_rmdir(path) != 0 )
+        ERR << "Error removing cache directory" << path << endl;
+    }
+  }
+
+  path = opt.root + "/var/lib/zypp/db";
+  if ( PathInfo(path).isDir() )
+  {
+    cout << "Deleting old db directory (" << path << ")." << endl;
+    if ( ! opt.fake )
+    {
+      if ( filesystem::recursive_rmdir(path) != 0 )
+        ERR << "Error removing db directory" << path << endl;
+    }
+  }
+}
+
+static void migrate_sources( const Options &opt )
+{
+  if ( getenv("YAST_IS_RUNNING") && (string(getenv("YAST_IS_RUNNING")) == "instsys" ))
+  {
+    MIL << "YaST is running in instsys. Not migrating old sources. YaST will do it." << endl;
+    return;
+  }
+  else
+  {
+    MIL << "YaST not running in instsys." << endl;
+  }
+
+  zypp::zypp_readonly_hack::IWantIt();
+  ZYpp::Ptr Z = zypp::getZYpp();
+  RepoManager manager;
+
+  Pathname source_p = opt.root + opt.sources_dir;
+
+  if ( ! PathInfo(source_p).isExist() )
+  {
+    cout << "No sources to migrate." << endl;
+    clear_cache( opt );
+    return;
+  }
+
+  RepoInfoList sources;
+  DBG << "Reading source cache in " << source_p << std::endl;
+
+  list<Pathname> entries;
+  if ( filesystem::readdir( entries, source_p, false ) != 0 )
+      ZYPP_THROW(Exception("failed to read directory"));
+
+  int i=0;
+  for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+  {
+
+    MIL << "Processing " << *it << endl;
+
+    std::ifstream anIstream((*it).c_str());
+    zypp::parser::xmlstore::XMLSourceCacheParser iter(anIstream, "");
+    for (; ! iter.atEnd(); ++iter) {
+      RepoInfo data = **iter;
+      string alias = "migrated_" + str::numstring(i);
+      try {
+        data.setAlias(alias);
+        data.setEnabled(false);
+        cout << "Migrating repo: " << endl << data << endl;
+        if ( ! opt.fake )
+        {
+          manager.addRepository(data);
+        }
+        cout << "Deleting old source: " << *it << endl;
+        if ( ! opt.fake )
+        {
+          if ( filesystem::unlink(*it) != 0 )
+            ERR << "Error removing source " << *it << endl;
+          // delete old file
+        }
+        cout << "saved as " << alias << endl;
+        ++i;
+      }
+      catch ( const Exception &e )
+      {
+        cout << "Error adding repository: " << e.msg() << endl << data << endl;
+      }
+
+    }
+
+  }
+  cout << i << " sources migrated."<< endl;
+
+  // reread entries
+  if ( filesystem::readdir( entries, source_p, false ) != 0 )
+      ZYPP_THROW(Exception("failed to read directory"));
+  if ( entries.size() == 0 )
+  {
+    cout << "all sources migrated. deleting old source directory"<< endl;
+    if ( ! opt.fake )
+    {
+      if ( filesystem::recursive_rmdir(source_p) != 0 )
+        ERR << "Error removing source directory" << source_p << endl;
+
+      clear_cache( opt );
+    }
+  }
+  else
+  {
+    cout << "Not all sources migrated. leaving old source directory"<< endl;
+  }
+}
+
+void usage(int argc, char **argv)
+{
+  cout << argv[0] << ". Migrates old sources to 10.3 repositories." << endl;
+  cout << "Usage:" << endl;
+  cout << argv[0] << " [--root root-path] [--fake] [--sp sources-path]" << endl;
+}
+
+//-----------------------------------------------------------------------------
+
+int
+main (int argc, char **argv)
+{
+  MIL << "-------------------------------------" << endl;
+  Options opt;
+  int i;
+  for ( i=1; i < argc; ++i )
+  {
+
+    if ( string(argv[i]) == "--help" )
+    {
+      usage(argc, argv);
+      return 0;
+    }
+    if ( string(argv[i]) == "--fake" )
+      opt.fake = true;
+    if ( string(argv[i]) == "--root" )
+      opt.root = argv[++i];
+    if ( string(argv[i]) == "--sp" )
+      opt.sources_dir = argv[++i];
+  }
+  migrate_sources(opt);
+
+  return 0;
+}
+
diff --git a/tools/notify-message b/tools/notify-message
new file mode 100755 (executable)
index 0000000..9bda07a
--- /dev/null
@@ -0,0 +1,32 @@
+#! /bin/bash
+######################################################################
+test "$1" = "--help" && {
+cat <<EOF
+Usage: notify-message [-p PACKAGE]
+
+Per default zypp invokes this command to propagate update
+notification messages. The optional -p argument should
+denote the packages that causes this message to be sent.
+The message text itself is read from stdin.
+
+See update.messages.notify option in /etc/zypp.conf
+for details.
+EOF
+exit 0;
+}
+######################################################################
+
+MAILCMD="/usr/bin/mail"
+test -x "$MAILCMD" || {
+       echo "notify-message: $MAILCMD: command not found" >&2
+       exit 1;
+}
+
+MAILTAG="[zypp-notify-message]"
+MAIL_TO="root"
+# curently just '-p packageident'
+test "$1" = "-p" && {
+       MAILTAG="$MAILTAG $2"
+}
+
+exec $MAILCMD -s "$MAILTAG" $MAIL_TO
diff --git a/tools/package-manager/CMakeLists.txt b/tools/package-manager/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ddae11a
--- /dev/null
@@ -0,0 +1,3 @@
+
+########### install files ###############
+INSTALL(PROGRAMS package-manager package-manager-su DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
diff --git a/tools/package-manager/package-manager b/tools/package-manager/package-manager
new file mode 100755 (executable)
index 0000000..4c7b13a
--- /dev/null
@@ -0,0 +1,195 @@
+#! /bin/sh
+# by http://en.opensuse.org/User:Mvidner
+# https://bugzilla.novell.com/show_bug.cgi?id=222757
+usage() {
+    cat >&2 <<EOF
+$0 version 0.2
+Usage:
+  $0 --update ONE_NAME
+  $0 --remove ONE_NAME
+  $0 [--install [ONE_FILE]]
+EOF
+    exit $1
+}
+
+# quoted concatenation of arguments
+function quote() {
+  # formerly used 'printf %q' breaks UTF-8 strings
+  echo -n "$@" | sed 's/\([]|&;<>()$`\" \t*?#~=%[]\)/\\\1/g'
+}
+
+function mkCmd() {
+  quote "$1"
+  shift
+  for ARG in "$@"; do
+    echo -n " $(quote "$ARG")"
+  done
+}
+
+function check_installed()
+{
+  rpm -q "$1" >/dev/null
+  return $?
+}
+
+function what_do_we_have()
+{
+  echo "Zlm: ${HAVE_ZLM}"
+  echo "openSUSE: ${HAVE_OPENSUSE}"
+  echo "KPackagekit: ${HAVE_KPACKAGEKIT}"
+  echo "GPackagekit: ${HAVE_GPACKAGEKIT}"
+  echo "KDE running: ${KDE_FULL_SESSION}"
+}
+
+# check what we have
+HAVE_ZLM=false
+check_installed "zen-updater"
+if [ "$?" == "0" ]
+then
+    HAVE_ZLM=true
+fi
+
+HAVE_OPENSUSE=false
+check_installed "yast2-packager"
+if [ $? == 0 ]
+then
+    HAVE_OPENSUSE=true
+fi
+
+HAVE_KPACKAGEKIT=false
+check_installed "kpackagekit"
+if [ "$?" == "0" ]
+then
+    HAVE_KPACKAGEKIT=true
+fi
+
+HAVE_GPACKAGEKIT=false
+check_installed "gnome-packagekit"
+if [ $? == 0 ]
+then
+    HAVE_GPACKAGEKIT=true
+fi
+
+if $HAVE_ZLM; then
+    if $HAVE_OPENSUSE; then
+       if [ -f /etc/sysconfig/sw_management ]; then
+           . /etc/sysconfig/sw_management
+           PSMS="$PREFERRED_SW_MANAGER_STACK"
+       fi
+
+       if [ "x$PSMS" = "xzlm" ]; then
+           STACK=zlm
+       elif [ "x$PSMS" = "xopensuse" ]; then
+           STACK=opensuse
+       else
+           echo >&2 "/etc/sysconfig/sw_management:PREFERRED_SW_MANAGER_STACK should contain"
+           echo >&2 "'zlm' or 'opensuse'"
+           STACK=ugh
+       fi
+    else
+       STACK=zlm
+    fi
+else
+    if $HAVE_OPENSUSE; then
+       STACK=opensuse
+    else
+       echo >&2 "No package manager installed?"
+       STACK=ugh
+    fi
+fi
+
+METHOD=yast
+# determine what we can use
+if $HAVE_KPACKAGEKIT && [ "$KDE_FULL_SESSION" ]; then
+  METHOD=kpackagekit
+elif $HAVE_GPACKAGEKIT && [ "$WINDOWMANAGER" == "/usr/bin/gnome" ]; then
+  METHOD=gnome-packagekit
+else
+  if [ "$STACK" == "zlm" ]; then
+    METHOD="zlm"
+  else
+    METHOD="yast"
+  fi
+fi
+
+echo $METHOD
+
+xsu() {
+    # a copy of xdg-su.
+    package-manager-su -c "$(mkCmd "$@")"
+}
+
+# do_* fall back to yast for STACK=ugh
+
+do_update() {
+    case "${METHOD}" in
+    yast)
+      xsu /sbin/yast2 --update "$@"
+    ;;
+    zlm)
+      zen-updater --no-tray "$@"
+    ;;
+    kpackagekit)
+      kpackagekit --updates "$@"
+    ;;
+    gnome-packagekit)
+      if [ -e /usr/bin/gpk-update-viewer2 ]; then
+        /usr/bin/gpk-update-viewer2 "$@"
+      else
+        /usr/bin/gpk-update-viewer "$@"
+      fi
+    ;;
+    esac
+}
+
+do_remove() {
+  # not all support remove
+  case "${METHOD}" in
+    yast|kpackagekit|gnome-packagekit)
+      xsu /sbin/yast2 --remove "$@"
+    ;;
+    zlm)
+      zen-remover "$@"
+    ;;
+    esac
+}
+
+do_install() {
+    case "${METHOD}" in
+    yast)
+      xsu /sbin/yast2 --install "$@"
+    ;;
+    zlm)
+      zen-installer "$@"
+    ;;
+    kpackagekit)
+      if [ $# == 0 ]; then
+          xsu /sbin/yast2 --install
+      else
+          kpackagekit "$@"
+      fi
+    ;;
+    gnome-packagekit)
+      if [ $# == 0 ]; then
+          xsu /sbin/yast2 --install
+      else
+          /usr/bin/gpk-install-local-file "$@"
+      fi
+    ;;
+    esac
+}
+
+if [ "x$1" = "x--update" -a $# = 2 ]; then
+    shift
+    do_update "$@"
+elif [ "x$1" = "x--remove" -a $# = 2 ]; then
+    shift
+    do_remove "$@"
+elif [ "x$1" = "x--install" ]; then
+    shift
+    do_install "$@"
+elif [ $# = 0 ]; then
+    do_install
+else
+    usage 1
+fi
diff --git a/tools/package-manager/package-manager-su b/tools/package-manager/package-manager-su
new file mode 100755 (executable)
index 0000000..e1521f3
--- /dev/null
@@ -0,0 +1,462 @@
+#!/bin/sh
+#---------------------------------------------
+#   xdg-su
+#
+#   Utility script to run a command as an alternate user, generally
+#       the root user, with a graphical prompt for the root
+#       password if needed
+#
+#   Refer to the usage() function below for usage.
+#
+#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>
+#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
+#
+#   LICENSE:
+#
+#   Permission is hereby granted, free of charge, to any person obtaining a
+#   copy of this software and associated documentation files (the "Software"),
+#   to deal in the Software without restriction, including without limitation
+#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+#   and/or sell copies of the Software, and to permit persons to whom the
+#   Software is furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included
+#   in all copies or substantial portions of the Software.
+#
+#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+#   OTHER DEALINGS IN THE SOFTWARE.
+#
+#---------------------------------------------
+
+manualpage()
+{
+cat << _MANUALPAGE
+Name
+
+xdg-su - run a GUI program as root after prompting for the root password
+
+Synopsis
+
+xdg-su [-u user] -c command
+
+xdg-su { --help | --manual | --version }
+
+Description
+
+xdg-su provides a graphical dialog that prompts the user for a password to run
+command as user or as root if no user was specified.
+
+xdg-su is for use inside a desktop session only.
+
+xdg-su discards any stdout and stderr output from command.
+
+Options
+
+-u user
+    run command as user. The default is to run as root.
+--help
+    Show command synopsis.
+--manual
+    Show this manualpage.
+--version
+    Show the xdg-utils version information.
+
+Exit Codes
+
+An exit code of 0 indicates success while a non-zero exit code indicates
+failure. The following failure codes can be returned:
+
+1
+    Error in command line syntax.
+2
+    One of the files passed on the command line did not exist.
+3
+    A required tool could not be found.
+4
+    The action failed.
+
+See Also
+
+su(1)
+
+Examples
+
+xdg-su -u root -c "/opt/shinythings/bin/install-GUI --install fast"
+
+Runs the /opt/shinythings/bin/install-GUI command with root permissions.
+
+_MANUALPAGE
+}
+
+usage()
+{
+cat << _USAGE
+xdg-su - run a GUI program as root after prompting for the root password
+
+Synopsis
+
+xdg-su [-u user] -c command
+
+xdg-su { --help | --manual | --version }
+
+_USAGE
+}
+
+#@xdg-utils-common@
+
+#----------------------------------------------------------------------------
+#   Common utility functions included in all XDG wrapper scripts
+#----------------------------------------------------------------------------
+
+DEBUG()
+{
+  [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt $1 ] && return 0;
+  shift
+  echo "$@" >&2
+}
+
+#-------------------------------------------------------------
+# Exit script on successfully completing the desired operation
+
+exit_success()
+{
+    if [ $# -gt 0 ]; then
+        echo "$@"
+        echo
+    fi
+
+    exit 0
+}
+
+
+#-----------------------------------------
+# Exit script on malformed arguments, not enough arguments
+# or missing required option.
+# prints usage information
+
+exit_failure_syntax()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+        echo "Try 'xdg-su --help' for more information." >&2
+    else
+        usage
+        echo "Use 'man xdg-su' or 'xdg-su --manual' for additional info."
+    fi
+
+    exit 1
+}
+
+#-------------------------------------------------------------
+# Exit script on missing file specified on command line
+
+exit_failure_file_missing()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+    fi
+
+    exit 2
+}
+
+#-------------------------------------------------------------
+# Exit script on failure to locate necessary tool applications
+
+exit_failure_operation_impossible()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+    fi
+
+    exit 3
+}
+
+#-------------------------------------------------------------
+# Exit script on failure returned by a tool application
+
+exit_failure_operation_failed()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+    fi
+
+    exit 4
+}
+
+#------------------------------------------------------------
+# Exit script on insufficient permission to read a specified file
+
+exit_failure_file_permission_read()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+    fi
+
+    exit 5
+}
+
+#------------------------------------------------------------
+# Exit script on insufficient permission to read a specified file
+
+exit_failure_file_permission_write()
+{
+    if [ $# -gt 0 ]; then
+        echo "xdg-su: $@" >&2
+    fi
+
+    exit 6
+}
+
+check_input_file()
+{
+    if [ ! -e "$1" ]; then
+        exit_failure_file_missing "file '$1' does not exist"
+    fi
+    if [ ! -r "$1" ]; then
+        exit_failure_file_permission_read "no permission to read file '$1'"
+    fi
+}
+
+check_vendor_prefix()
+{
+    file=`basename "$1"`
+    case "$file" in
+       [a-zA-Z]*-*)
+         return
+         ;;
+    esac
+
+    echo "xdg-su: filename '$file' does not have a proper vendor prefix" >&2
+    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
+    echo 'with a dash ("-"). An example filename is '"'example-$file'" >&2
+    echo "Use --novendor to override or 'xdg-su --manual' for additional info." >&2
+    exit 1
+}
+
+check_output_file()
+{
+    # if the file exists, check if it is writeable
+    # if it does not exists, check if we are allowed to write on the directory
+    if [ -e "$1" ]; then
+        if [ ! -w "$1" ]; then
+            exit_failure_file_permission_write "no permission to write to file '$1'"
+        fi
+    else
+        DIR=`dirname "$1"`
+        if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
+            exit_failure_file_permission_write "no permission to create file '$1'"
+        fi
+    fi
+}
+
+#----------------------------------------
+# Checks for shared commands, e.g. --help
+
+check_common_commands()
+{
+    while [ $# -gt 0 ] ; do
+        parm="$1"
+        shift
+
+        case "$parm" in
+            --help)
+            usage
+            echo "Use 'man xdg-su' or 'xdg-su --manual' for additional info."
+            exit_success
+            ;;
+
+            --manual)
+            manualpage
+            exit_success
+            ;;
+
+            --version)
+            echo "xdg-su 1.0beta2"
+            exit_success
+            ;;
+        esac
+    done
+}
+
+check_common_commands "$@"
+if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
+    # Be silent
+    xdg_redirect_output=" > /dev/null 2> /dev/null"
+else
+    # All output to stderr
+    xdg_redirect_output=" >&2"
+fi
+
+#--------------------------------------
+# Checks for known desktop environments
+# set variable DE to the desktop environments name, lowercase
+
+detectDE()
+{
+    if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
+    elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
+    elif xprop -root _DT_SAVE_MODE | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
+    elif [ x"$DESKTOP_SESSION" == x"LXDE" ]; then DE=lxde;
+    fi
+}
+
+#----------------------------------------------------------------------------
+
+
+
+su_kde()
+{
+    KDESU=`which kdesu 2>/dev/null`
+    if [ $? -eq 0 ] ; then
+        if [ -z "$user" ] ; then
+             $KDESU -c "$cmd"
+        else
+             $KDESU -u "$user" -c "$cmd"
+        fi
+
+        if [ $? -eq 0 ]; then
+            exit_success
+        else
+            exit_failure_operation_failed
+        fi
+    else
+        su_generic
+    fi
+}
+
+su_gnome()
+{
+    GSU=`which gnomesu 2>/dev/null`
+    if [ $? -ne 0 ] ; then
+        GSU=`which xsu 2>/dev/null`
+    fi
+    if [ $? -eq 0 ] ; then
+        if [ -z "$user" ] ; then
+            $GSU -c "$cmd"
+        else
+            $GSU -u "$user" -c "$cmd"
+        fi
+
+        if [ $? -eq 0 ]; then
+            exit_success
+        else
+            exit_failure_operation_failed
+        fi
+    else
+        su_generic
+    fi
+}
+
+su_generic()
+{
+    if [ -z "$user" ] ; then
+        xterm -geom 60x5 -T "xdg-su: $cmd" -e su -c "$cmd"
+    else
+        xterm -geom 60x5 -T "xdg-su: $cmd" -e su -u "$user" -c "$cmd"
+    fi
+
+    if [ $? -eq 0 ]; then
+        exit_success
+    else
+        exit_failure_operation_failed
+    fi
+}
+
+
+su_xfce()
+{
+    if which gnomesu &>/dev/null ; then
+        su_gnome
+    else
+        su_generic
+    fi
+}
+
+su_lxde()
+{
+    if which gnomesu &>/dev/null ; then
+        su_gnome
+    else
+        su_generic
+    fi
+}
+
+[ x"$1" != x"" ] || exit_failure_syntax
+
+user=
+cmd=
+while [ $# -gt 0 ] ; do
+    parm="$1"
+    shift
+
+    case "$parm" in
+      -u)
+        if [ -z "$1" ] ; then
+            exit_failure_syntax "user argument missing for -u"
+        fi
+        user="$1"
+        shift
+        ;;
+
+      -c)
+        if [ -z "$1" ] ; then
+            exit_failure_syntax "command argument missing for -c"
+        fi
+        cmd="$1"
+        shift
+        ;;
+
+      -*)
+        exit_failure_syntax "unexpected option '$parm'"
+        ;;
+
+      *)
+        exit_failure_syntax "unexpected argument '$parm'"
+        ;;
+    esac
+done
+
+if [ -z "${cmd}" ] ; then
+    exit_failure_syntax "command missing"
+fi
+
+detectDE
+
+if [ x"$DE" = x"" ]; then
+    which xterm 2>/dev/null >&2
+    if [ $? -eq 0 -a -n "$DISPLAY" ] ; then
+        DE=generic
+    fi
+fi
+
+case "$DE" in
+    kde)
+    su_kde
+    ;;
+
+    gnome)
+    su_gnome
+    ;;
+
+    generic)
+    su_generic
+    ;;
+
+    xfce)
+    su_xfce
+    ;;
+
+    lxde)
+    su_lxde
+    ;;
+
+    *)
+    [ x"$user" = x"" ] && user=root 
+    exit_failure_operation_impossible "no graphical method available for invoking '$cmd' as '$user'"
+    ;;
+esac
diff --git a/tools/package-manager/package-manager.desktop b/tools/package-manager/package-manager.desktop
new file mode 100644 (file)
index 0000000..1244e70
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Install/Remove Software
+GenericName=Install Software
+Exec=package-manager --install %F
+#Icon=package-manager-icon
+Terminal=false
+Type=Application
+Categories=PackageManager;X-SuSE-ControlCenter-System;
+MimeType=application/x-rpm;application/x-redhat-package-manager;
+StartupNotify=true
diff --git a/tools/patch_find_bug.cc b/tools/patch_find_bug.cc
new file mode 100644 (file)
index 0000000..8609e5e
--- /dev/null
@@ -0,0 +1,113 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+#include "zypp/PoolQuery.h"
+
+using namespace zypp;
+using std::flush;
+
+static std::string appname( "patch_find_bug" );
+
+int errexit( const std::string & msg_r = std::string(), int exit_r = 101 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  return exit_r;
+}
+
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  cerr << "Usage: " << appname << "[OPTIONS] bugnumber..." << endl;
+  cerr << "  Find patches refering to bugnumber (substring)." << endl;
+  cerr << "  --root SYSROOT: Load system located below directory SYSROOT" << endl;
+  return exit_r;
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc;
+  ++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ZConfig::instance();
+  Pathname sysRoot("/");
+
+  if ( (*argv) == std::string("--root") )
+  {
+    --argc,++argv;
+    if ( ! argc )
+      return errexit("--root requires an argument.");
+
+    if ( ! PathInfo( *argv ).isDir() )
+      return errexit("--root requires a directory.");
+
+    sysRoot = *argv;
+    --argc,++argv;
+  }
+
+  TestSetup::LoadSystemAt( sysRoot );
+
+  for ( ; argc; --argc,++argv )
+  {
+    PoolQuery q;
+    q.setMatchSubstring();
+    q.setCaseSensitive( false );
+    q.addAttribute( sat::SolvAttr::updateReferenceId, *argv );
+
+    if ( q.empty() )
+    {
+      cout << "BUG REFERENCE '" << *argv << "': No match found." << endl;
+    }
+    else
+    {
+      cout << "BUG REFERENCE '" << *argv << endl;
+      for_( it , q.begin(), q.end() )
+      {
+        // print the solvable that has a match:
+        cout << "  - " << *it << endl;
+
+        if ( true )
+        {
+          // Print details about each match in that solvable:
+          for_( d, it.matchesBegin(), it.matchesEnd() )
+          {
+            // directly access specific attribute like "subFind(updateReferenceType)":
+            cout << "    - " << d->inSolvAttr() << "\t\"" << d->asString() << "\" has type \""
+                << d->subFind( sat::SolvAttr::updateReferenceType ).asString() << "\"" << endl;
+
+            // list the whole updateReference structure:
+            for_( s, d->subBegin(), d->subEnd() )
+            {
+              cout << "       -" << s.inSolvAttr() << "\t\"" << s.asString() << "\"" << endl;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
diff --git a/tools/percent-encode.cc b/tools/percent-encode.cc
new file mode 100644 (file)
index 0000000..e9260f3
--- /dev/null
@@ -0,0 +1,38 @@
+#include <iostream>
+#include <zypp/base/IOStream.h>
+#include <zypp/url/UrlUtils.h>
+
+static std::string doEncode( const std::string & str_r )
+{ return zypp::url::encode( str_r ); }
+
+static std::string doDecode( const std::string & str_r )
+{ return zypp::url::decode( str_r ); }
+
+int main( int argc, const char * argv[] )
+{
+  bool encode( true );
+
+  --argc,++argv;
+  if ( argc )
+  {
+    if ( *argv == std::string( "-d" ) || *argv == std::string( "--decode" ) )
+      encode = false;
+    else if ( *argv == std::string( "-h" ) || *argv == std::string( "--help" ) )
+    {
+      std::cout << "Usage: percent-encode [OPTION]" << std::endl;
+      std::cout << "Read lines from stdin and write them percent encoded to stdout." << std::endl;
+      std::cout << "" << std::endl;
+      std::cout << "Option:" << std::endl;
+      std::cout << " -d, --decode  Decode lines read from stdin instead of encoding them." << std::endl;
+      std::cout << " -h --help     Print this message." << std::endl;
+      return 0;
+    }
+  }
+
+  std::string (*coder)( const std::string & str_r ) = encode ? doEncode: doDecode;
+  for( zypp::iostr::EachLine in( std::cin ); in; in.next() )
+  {
+    std::cout << coder( *in ) << std::endl;
+  }
+  return 0;
+}
diff --git a/tools/zypp-CheckAccessDeleted.cc b/tools/zypp-CheckAccessDeleted.cc
new file mode 100644 (file)
index 0000000..ebdcd1e
--- /dev/null
@@ -0,0 +1,140 @@
+#include <cstdio>
+#include <iostream>
+#include <zypp/base/Easy.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Exception.h>
+#include <zypp/Pathname.h>
+#include <zypp/misc/CheckAccessDeleted.h>
+
+/** Collect max string size. */
+struct TableCol
+{
+  TableCol( size_t size_r = 0 )
+  : size( size_r )
+  {}
+  TableCol( const std::string & header_r )
+  : header( header_r ), size( header_r.size() )
+  {}
+  void operator()( const std::string & val_r )
+  { if ( val_r.size() > size ) size = val_r.size(); }
+
+  std::string header;
+  size_t      size;
+};
+
+/** Scan to determine column sizes, then print. */
+struct ProcInfoTable
+{
+  ProcInfoTable()
+  : pid                ( "PID" )
+  , ppid       ( "PPID" )
+  , puid       ( "UID" )
+  , login      ( "LOGIN" )
+  , command    ( "COMMAND" )
+  , service    ( "SERVICE" )
+  , files      ( "FILES" )
+  {}
+
+  void scan( const zypp::CheckAccessDeleted::ProcInfo & val_r )
+  {
+    pid( val_r.pid );
+    ppid( val_r.ppid );
+    puid( val_r.puid );
+    login( val_r.login );
+    command( val_r.command );
+    service( val_r.service() );
+  }
+
+  void printHeader() const
+  {
+    printRow( pid.header,
+              ppid.header,
+              puid.header,
+              login.header,
+              command.header,
+              service.header,
+              files.header );
+  }
+
+  void print( const zypp::CheckAccessDeleted::ProcInfo & val_r ) const
+  {
+    printRow( val_r.pid,
+              val_r.ppid,
+              val_r.puid,
+              val_r.login,
+              val_r.command,
+              val_r.service(),
+              zypp::str::join( val_r.files, ", " ) );
+  }
+
+  void printRow( const std::string & pid_r,
+                 const std::string & ppid_r,
+                 const std::string & puid_r,
+                 const std::string & login_r,
+                 const std::string & command_r,
+                 const std::string & service_r,
+                 const std::string & files_r ) const
+  {
+    printf( "%*s %*s %*s  %-*s %-*s %-*s %-s\n",
+            (int)pid.size, pid_r.c_str(),
+            (int)ppid.size, ppid_r.c_str(),
+            (int)puid.size, puid_r.c_str(),
+            (int)login.size, login_r.c_str(),
+            (int)command.size, command_r.c_str(),
+            (int)service.size, (service_r.empty() ? " -" : service_r.c_str()),
+            files_r.c_str() );
+  }
+
+  TableCol pid;
+  TableCol ppid;
+  TableCol puid;
+  TableCol login;
+  TableCol command;
+  TableCol service;
+  TableCol files;
+};
+
+int main( int argc, char * argv[] )
+{
+  if ( argc >= 2 )
+  {
+    std::string progname( zypp::Pathname::basename( argv[0] ) );
+    if ( strcmp( argv[1], "--help" ) == 0 )
+    {
+      std::cout << "Usage: " << progname << " [--help]" << std::endl;
+      std::cout << "List information about all running processe" << std::endl;
+      std::cout << "which access deleted executables or libraries." << std::endl;
+      std::cout << "  PID     " << "process ID" << std::endl;
+      std::cout << "  PPID    " << "parent process ID" << std::endl;
+      std::cout << "  UID     " << "process user ID" << std::endl;
+      std::cout << "  LOGIN   " << "process login name" << std::endl;
+      std::cout << "  COMMAND " << "process command name" << std::endl;
+      std::cout << "  SERVICE " << "/etc/init.d/ script that might be used to restart the command (guessed)" << std::endl;
+      std::cout << "  FILES   " << "list of deleted executables or libraries accessed" << std::endl;
+      return 0;
+    }
+    std::cerr << progname << ": unexpected argument '" << argv[1] << "'" << std::endl;
+    std::cerr << "Try `" << progname << " --help' for more information." << std::endl;
+    return 1;
+  }
+
+  zypp::CheckAccessDeleted checker(false); // wait for explicit call to check()
+  try {
+    checker.check( /*verbose*/true );
+  }
+  catch( const zypp::Exception & err )
+  {
+    std::cerr << err << std::endl << err.historyAsString();
+    return 2;
+  }
+
+  ProcInfoTable table;
+  for_( it, checker.begin(), checker.end() )
+    table.scan( *it );
+
+  table.printHeader();
+  for_( it, checker.begin(), checker.end() )
+    table.print( *it );
+
+  return 0;
+}
diff --git a/tools/zypp-list.cc b/tools/zypp-list.cc
new file mode 100644 (file)
index 0000000..2bfdaa9
--- /dev/null
@@ -0,0 +1,170 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+
+#include <algorithm>
+#include <zypp/PoolQuery.h>
+
+static std::string appname( "zypp-list" );
+
+#define message        cerr
+#define OUT    cout
+using std::flush;
+
+int errexit( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  return exit_r;
+}
+
+int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  cerr << "Usage: " << appname << " [GLOBALOPTS] COMMAND" << endl;
+  cerr << "List pool items according to command." << endl;
+  cerr << endl;
+  cerr << "GLOBALOPTS:" << endl;
+  cerr << "  --root   Load repos from the system located below ROOTDIR. If ROOTDIR" << endl;
+  cerr << "           denotes a sover testcase, the testcase is loaded." << endl;
+  cerr << "  -i, --installed Process installed packages only." << endl;
+  cerr << endl;
+  cerr << "COMMANDS:" << endl;
+  cerr << "  locks:   List all locked pool items." << endl;
+  cerr << "    ." << endl;
+  cerr << endl;
+  return exit_r;
+}
+
+void startup( const Pathname & sysRoot = "/", bool onlyInstalled = false )
+{
+  ZConfig::instance();
+  sat::Pool satpool( sat::Pool::instance() );
+
+  if ( TestSetup::isTestcase( sysRoot ) )
+  {
+    message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test;
+    test.loadTestcaseRepos( sysRoot );
+    dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  }
+  else if ( TestSetup::isTestSetup( sysRoot ) )
+  {
+    message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
+    TestSetup test( sysRoot, Arch_x86_64 );
+    test.loadRepos();
+    dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
+  }
+  else
+  {
+    // a system
+    message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
+    if ( true )
+    {
+      message << "*** load target '" << Repository::systemRepoAlias() << "'\t";
+      getZYpp()->initializeTarget( sysRoot );
+      getZYpp()->target()->load();
+      message << satpool.systemRepo() << endl;
+    }
+
+    if ( !onlyInstalled )
+    {
+      RepoManager repoManager( sysRoot );
+      RepoInfoList repos = repoManager.knownRepositories();
+      for_( it, repos.begin(), repos.end() )
+      {
+        RepoInfo & nrepo( *it );
+
+        if ( ! nrepo.enabled() )
+          continue;
+
+        if ( ! repoManager.isCached( nrepo ) )
+        {
+          message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+
+        message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
+        try
+        {
+          repoManager.loadFromCache( nrepo );
+          message << satpool.reposFind( nrepo.alias() ) << endl;
+        }
+        catch ( const Exception & exp )
+        {
+          message << exp.asString() + "\n" + exp.historyAsString() << endl;
+          message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+          continue;
+        }
+      }
+    }
+  }
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  INT << "===[START]==========================================" << endl;
+  appname = Pathname::basename( argv[0] );
+  --argc,++argv;
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  Pathname sysRoot( "/" );
+  if ( argc && (*argv) == std::string("--root") )
+  {
+    --argc,++argv;
+    if ( ! argc )
+      return errexit("--root requires an argument.");
+
+    if ( ! PathInfo( *argv ).isDir() )
+      return errexit("--root requires a directory.");
+
+    sysRoot = *argv;
+    --argc,++argv;
+  }
+
+  bool onlyInstalled( false );
+  if ( argc && (*argv) == std::string("--installed") )
+  {
+    --argc,++argv;
+    onlyInstalled = true;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  if ( ! argc )
+  {
+    return usage();
+  }
+
+  startup( sysRoot, onlyInstalled );
+  ResPool pool( ResPool::instance() );
+
+  if ( argc && (*argv) == std::string("locked") )
+  {
+    OUT << "*** Locked:" << endl;
+    for_( it, pool.begin(), pool.end() )
+    {
+      if ( (*it).status().isLocked() )
+       OUT << *it << endl;
+    }
+  }
+
+  INT << "===[END]============================================" << endl << endl;
+  return 0;
+}
diff --git a/tools/zypp-pubkey.cc b/tools/zypp-pubkey.cc
new file mode 100644 (file)
index 0000000..c483a1b
--- /dev/null
@@ -0,0 +1,128 @@
+#define INCLUDE_TESTSETUP_WITHOUT_BOOST
+#include "zypp/../tests/lib/TestSetup.h"
+#undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
+#define message cout
+using std::flush;
+
+#include <boost/program_options.hpp>
+namespace opt = boost::program_options;
+
+#include <zypp/target/rpm/RpmDb.h>
+
+static std::string appname( "unknown" );
+
+int errexit( const std::string & msg_r = std::string(), int exit_r = 100 )
+{
+  if ( ! msg_r.empty() )
+  {
+    cerr << endl << msg_r << endl << endl;
+  }
+  return exit_r;
+}
+
+bool byTTL( const PublicKey & lhs, const PublicKey & rhs )
+{
+  int cmp = lhs.gpgPubkeyVersion().compare( rhs.gpgPubkeyVersion() );
+  if ( cmp ) return cmp < 0;
+  return lhs.gpgPubkeyRelease() > rhs.gpgPubkeyRelease(); // intentionally reverse cdate
+}
+
+/******************************************************************
+**
+**      FUNCTION NAME : main
+**      FUNCTION TYPE : int
+*/
+int main( int argc, char * argv[] )
+{
+  appname = Pathname::basename( argv[0] );
+  ///////////////////////////////////////////////////////////////////
+
+  opt::options_description options( "Options" );
+  options.add_options()
+      ( "key-file",    opt::value<std::vector<std::string> >(),
+                       "ASCII ascii armored public key file")
+      ( "root",                opt::value<std::string>()->default_value( "/" ),
+                       "Use the rmp database from system rooted at ARG")
+      ( "help,?",      "Produce this help message")
+      ;
+
+  opt::positional_options_description positional;
+  positional.add( "key-file", -1 );
+
+  opt::variables_map vm;
+  opt::store( opt::command_line_parser( argc, argv ).options( options ).positional( positional ).run(), vm );
+  opt::notify( vm );
+
+  if ( vm.count( "help" ) )
+  {
+    cerr << "Usage: " << appname << " [OPTIONS] [key-files...]" << endl;
+    cerr << "If no key files are given, list info about all gpg-pubkeys in the rpm database." << endl;
+    cerr << "Otherwise print info about each key and wheter it is present in the rpm database. " << endl;
+    cerr << options << endl;
+    return 1;
+  }
+  ///////////////////////////////////////////////////////////////////
+
+  if ( ! PathInfo( vm["root"].as<std::string>() ).isDir() )
+      return errexit("--root requires a directory");
+
+  target::rpm::RpmDb rpmdb;
+  rpmdb.initDatabase( vm["root"].as<std::string>() );
+  std::list<PublicKey> rpmpubkeys( rpmdb.pubkeys() );
+  rpmpubkeys.sort( byTTL );
+
+  if ( ! vm.count( "key-file" ) )
+  {
+    std::string last;
+    for_each_( it, rpmpubkeys )
+    {
+      if ( last == it->gpgPubkeyVersion() )
+       cout << *it << endl;
+      else
+      {
+       cout << dump( *it ) << endl;
+       last = it->gpgPubkeyVersion();
+      }
+    }
+    return 0;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  const std::vector<std::string> & keyFiles( vm["key-file"].as< std::vector<std::string> >() );
+  for_each_( it, vm["key-file"].as< std::vector<std::string> >() )
+  {
+    cout << "=== " << PathInfo(*it) << endl;
+    PublicKey pubkey( *it );
+    cout << dump( pubkey ) << endl;
+
+    std::string pubkeyV( pubkey.gpgPubkeyVersion() );
+    std::string pubkeyR( pubkey.gpgPubkeyRelease() );
+    unsigned count = 0;
+    for_each_( rpmpub, rpmpubkeys )
+    {
+      if ( rpmpub->gpgPubkeyVersion() == pubkeyV )
+      {
+       int cmp = rpmpub->gpgPubkeyRelease().compare( pubkeyR );
+       if ( cmp < 0 )
+         cout << "<<< ";
+       else if ( cmp > 0 )
+         cout << ">>> ";
+       else
+       {
+         ++count;
+         cout << "*** ";
+       }
+       cout << "gpg-pubkey-" << rpmpub->gpgPubkeyVersion() << "-" << rpmpub->gpgPubkeyRelease() << " " << rpmpub->daysToLive() << endl;
+      }
+    }
+    if ( ! count )
+    {
+      cout << "*** Not in rpm database." << endl;
+    }
+    cout << endl;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  return 0;
+}
diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a651570
--- /dev/null
@@ -0,0 +1,5 @@
+ADD_SUBDIRECTORY(mongoose)
+
+
+
+
diff --git a/vendor/mongoose/CMakeLists.txt b/vendor/mongoose/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ee7268f
--- /dev/null
@@ -0,0 +1,9 @@
+ADD_DEFINITIONS( -DNO_CGI -D_POSIX_SOURCE -D_BSD_SOURCE )
+SET(mongoose_SOURCES
+ mongoose.c
+ mongoose.h
+)
+
+ADD_LIBRARY(mongoose ${mongoose_SOURCES})
+TARGET_LINK_LIBRARIES(mongoose dl pthread)
+
diff --git a/vendor/mongoose/mongoose.c b/vendor/mongoose/mongoose.c
new file mode 100644 (file)
index 0000000..b412f97
--- /dev/null
@@ -0,0 +1,3839 @@
+/*\r
+ * Copyright (c) 2004-2009 Sergey Lyubka\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in\r
+ * all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+ * THE SOFTWARE.\r
+ *\r
+ * $Id: mongoose.c 230 2009-02-15 09:59:53Z valenok $\r
+ */\r
+\r
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <time.h>\r
+#include <errno.h>\r
+#include <signal.h>\r
+#include <fcntl.h>\r
+#endif /* _WIN32_WCE */\r
+\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <assert.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <limits.h>\r
+#include <stddef.h>\r
+#include <stdio.h>\r
+\r
+#if defined(_WIN32)            /* Windows specific     */\r
+#include <windows.h>\r
+\r
+#ifndef _WIN32_WCE\r
+#include <process.h>\r
+#include <direct.h>\r
+#include <io.h>\r
+#else /* _WIN32_WCE */\r
+/* Windows CE-specific definitions */\r
+#define NO_CGI /* WinCE has no pipes */\r
+#define NO_GUI /* temporarily until it is fixed */\r
+/* WinCE has both Unicode and ANSI versions of GetProcAddress */\r
+#undef GetProcAddress\r
+#define GetProcAddress GetProcAddressA\r
+#endif /* _WIN32_WCE */\r
+\r
+/*\r
+ * Do not allow holes in data structures!\r
+ * This is needed so when Mongoose DLL is loaded, other languages that\r
+ * describe struct mg_request_info from mongoose.h, agree with C code.\r
+ */\r
+#pragma pack(1)\r
+\r
+#define        __func__                __FUNCTION__\r
+#define        ERRNO                   GetLastError()\r
+#define        NO_SOCKLEN_T\r
+#define        SSL_LIB                 "ssleay32.dll"\r
+#define        DIRSEP                  '\\'\r
+#define        IS_DIRSEP_CHAR(c)       ((c) == '/' || (c) == '\\')\r
+#define        O_NONBLOCK              0\r
+#define        EWOULDBLOCK             WSAEWOULDBLOCK\r
+#define        dlopen(x,y)             LoadLibrary(x)\r
+#define        dlsym(x,y)              GetProcAddress((HINSTANCE) (x), (y))\r
+#define        _POSIX_\r
+\r
+#if !defined(R_OK)\r
+#define        R_OK                    04 /* for _access() */\r
+#endif /* !R_OK  MINGW #defines R_OK */\r
+\r
+#define        SHUT_WR                 1\r
+#define        snprintf                _snprintf\r
+#define        vsnprintf               _vsnprintf\r
+#define        popen(x, y)             _popen(x, y)\r
+#define        pclose(x)               _pclose(x)\r
+#define        access(x, y)            _access(x, y)\r
+#define        getcwd(x, y)            _getcwd(x, y)\r
+\r
+#ifdef HAVE_STRTOUI64\r
+#define        strtoull(x, y, z)       _strtoui64(x, y, z)\r
+#else\r
+#define        strtoull(x, y, z)       strtoul(x, y, z)\r
+#endif /* HAVE_STRTOUI64 */\r
+\r
+#define        write(x, y, z)          _write(x, y, (unsigned) z)\r
+#define        read(x, y, z)           _read(x, y, (unsigned) z)\r
+#define        open(x, y, z)           _open(x, y, z)\r
+#define        lseek(x, y, z)          _lseek(x, y, z)\r
+#define        close(x)                _close(x)\r
+\r
+#if !defined(fileno)\r
+#define        fileno(x)               _fileno(x)\r
+#endif /* !fileno MINGW #defines fileno */\r
+\r
+typedef HANDLE pthread_mutex_t;\r
+typedef HANDLE pthread_cond_t;\r
+\r
+#if !defined(S_ISDIR)\r
+#define S_ISDIR(x)             ((x) & _S_IFDIR)\r
+#endif /* S_ISDIR */\r
+\r
+#if defined(HAVE_STDINT)\r
+#include <stdint.h>\r
+#else\r
+typedef unsigned int           uint32_t;\r
+typedef unsigned short         uint16_t;\r
+typedef unsigned __int64       uint64_t;\r
+#endif /* HAVE_STDINT */\r
+\r
+/*\r
+ * POSIX dirent interface\r
+ */\r
+struct dirent {\r
+       char    d_name[FILENAME_MAX];\r
+};\r
+\r
+typedef struct DIR {\r
+       HANDLE                  handle;\r
+       WIN32_FIND_DATAW        info;\r
+       struct dirent           result;\r
+} DIR;\r
+\r
+#else                          /* UNIX  specific       */\r
+#include <sys/wait.h>\r
+#include <sys/socket.h>\r
+#include <sys/select.h>\r
+#include <sys/mman.h>\r
+#include <netinet/in.h>\r
+#include <arpa/inet.h>\r
+#include <sys/time.h>\r
+\r
+#include <pwd.h>\r
+#include <unistd.h>\r
+#include <dirent.h>\r
+#include <dlfcn.h>\r
+#include <pthread.h>\r
+#define        SSL_LIB                 "libssl.so"\r
+#define        DIRSEP                  '/'\r
+#define        IS_DIRSEP_CHAR(c)       ((c) == '/')\r
+#define        O_BINARY                0\r
+#define        closesocket(a)          close(a)\r
+#define        mg_mkdir(x, y)          mkdir(x, y)\r
+#define        mg_open(x, y, z)        open(x, y, z)\r
+#define        mg_remove(x)            remove(x)\r
+#define        mg_stat(x, y)           stat(x, y)\r
+#define        ERRNO                   errno\r
+#define        INVALID_SOCKET          (-1)\r
+typedef int SOCKET;\r
+\r
+#endif /* End of Windows and UNIX specific includes */\r
+\r
+#include "mongoose.h"\r
+\r
+#define        MONGOOSE_VERSION        "2.4"\r
+#define        PASSWORDS_FILE_NAME     ".htpasswd"\r
+#define        CGI_ENVIRONMENT_SIZE    4096\r
+#define        MAX_CGI_ENVIR_VARS      64\r
+#define        MAX_REQUEST_SIZE        16384\r
+#define        MAX_LISTENING_SOCKETS   10\r
+#define        MAX_CALLBACKS           20\r
+#define        ARRAY_SIZE(array)       (sizeof(array) / sizeof(array[0]))\r
+#define        UNKNOWN_CONTENT_LENGTH  ((uint64_t) ~0ULL)\r
+\r
+/*\r
+ * Darwin prior to 7.0 and Win32 do not have socklen_t\r
+ */\r
+#ifdef NO_SOCKLEN_T\r
+typedef int socklen_t;\r
+#endif /* NO_SOCKLEN_T */\r
+\r
+#if !defined(FALSE)\r
+enum {FALSE, TRUE};\r
+#endif /* !FALSE */\r
+\r
+typedef int bool_t;\r
+typedef void * (*mg_thread_func_t)(void *);\r
+static void cry(const char *, ...);\r
+\r
+static int tz_offset;\r
+static const char *http_500_error = "Internal Server Error";\r
+static FILE *error_log;\r
+\r
+/*\r
+ * Snatched from OpenSSL includes. I put the prototypes here to be independent\r
+ * from the OpenSSL source installation. Having this, mongoose + SSL can be\r
+ * built on any system with binary SSL libraries installed.\r
+ */\r
+typedef struct ssl_st SSL;\r
+typedef struct ssl_method_st SSL_METHOD;\r
+typedef struct ssl_ctx_st SSL_CTX;\r
+\r
+#define        SSL_ERROR_WANT_READ     2\r
+#define        SSL_ERROR_WANT_WRITE    3\r
+#define SSL_FILETYPE_PEM       1\r
+\r
+/*\r
+ * Dynamically loaded SSL functionality\r
+ */\r
+struct ssl_func {\r
+       const char      *name;          /* SSL function name    */\r
+       void            (*ptr)(void);   /* Function pointer     */\r
+};\r
+\r
+#define        FUNC(x) ssl_sw[x].ptr\r
+\r
+#define        SSL_free(x)     (* (void (*)(SSL *)) FUNC(0))(x)\r
+#define        SSL_accept(x)   (* (int (*)(SSL *)) FUNC(1))(x)\r
+#define        SSL_connect(x)  (* (int (*)(SSL *)) FUNC(2))(x)\r
+#define        SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))\r
+#define        SSL_write(x,y,z) \\r
+       (* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))\r
+#define        SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))\r
+#define        SSL_set_fd(x,y) (* (int (*)(SSL *, SOCKET)) FUNC(6))((x), (y))\r
+#define        SSL_new(x)      (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)\r
+#define        SSL_CTX_new(x)  (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)\r
+#define        SSLv23_server_method()  (* (SSL_METHOD * (*)(void)) FUNC(9))()\r
+#define        SSL_library_init() (* (int (*)(void)) FUNC(10))()\r
+#define        SSL_CTX_use_PrivateKey_file(x,y,z)      (* (int (*)(SSL_CTX *, \\r
+               const char *, int)) FUNC(11))((x), (y), (z))\r
+#define        SSL_CTX_use_certificate_file(x,y,z)     (* (int (*)(SSL_CTX *, \\r
+               const char *, int)) FUNC(12))((x), (y), (z))\r
+#define SSL_CTX_set_default_passwd_cb(x,y) \\r
+       (* (void (*)(SSL_CTX *, mg_spcb_t))FUNC(13))((x),(y))\r
+\r
+static struct ssl_func ssl_sw[] = {\r
+       {"SSL_free",                    NULL},\r
+       {"SSL_accept",                  NULL},\r
+       {"SSL_connect",                 NULL},\r
+       {"SSL_read",                    NULL},\r
+       {"SSL_write",                   NULL},\r
+       {"SSL_get_error",               NULL},\r
+       {"SSL_set_fd",                  NULL},\r
+       {"SSL_new",                     NULL},\r
+       {"SSL_CTX_new",                 NULL},\r
+       {"SSLv23_server_method",        NULL},\r
+       {"SSL_library_init",            NULL},\r
+       {"SSL_CTX_use_PrivateKey_file", NULL},\r
+       {"SSL_CTX_use_certificate_file",NULL},\r
+       {"SSL_CTX_set_default_passwd_cb",NULL},\r
+       {NULL,                          NULL}\r
+};\r
+\r
+struct usa {\r
+       socklen_t len;\r
+       union {\r
+               struct sockaddr sa;\r
+               struct sockaddr_in sin;\r
+       } u;\r
+};\r
+\r
+/*\r
+ * Numeric indexes for the option values in context, ctx->options\r
+ */\r
+enum mg_option_index {\r
+       OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST, OPT_CGI_EXTENSIONS,\r
+       OPT_CGI_INTERPRETER, OPT_SSI_EXTENSIONS, OPT_AUTH_DOMAIN,\r
+       OPT_AUTH_GPASSWD, OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG,\r
+       OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_UID,\r
+       OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_ADMIN_URI, OPT_THREADS,\r
+       NUM_OPTIONS\r
+};\r
+\r
+struct socket {\r
+       SOCKET          sock;           /* Listening socket             */\r
+       struct usa      usa;            /* Socket address               */\r
+\r
+       unsigned int    flags;          /* Flags                        */\r
+#define        FLAG_SSL        1\r
+#define        FLAG_TERMINATE  2\r
+};\r
+\r
+/*\r
+ * Callback function, and where it is bound to\r
+ */\r
+struct callback {\r
+       char            *uri_regex;     /* URI regex to handle          */\r
+       mg_callback_t   func;           /* user callback                */\r
+       bool_t          is_auth;        /* func is auth checker         */\r
+       int             status_code;    /* error code to handle         */\r
+       void            *user_data;     /* opaque user data             */\r
+};\r
+\r
+/*\r
+ * Socket pool.\r
+ * Master thread to enqueue accepted sockets by means of put_socket() function.\r
+ * Worker threads grab sockets from the queue using get_socket() function.\r
+ */\r
+struct socket_pool {\r
+       struct socket   sockets[20];    /* Array of sockets to process  */\r
+\r
+       int             size;           /* Ringbuffer pointers          */\r
+       int             head;\r
+       int             tail;\r
+\r
+       pthread_mutex_t mutex;\r
+       pthread_cond_t  put_cond;\r
+       pthread_cond_t  get_cond;\r
+};\r
+\r
+/*\r
+ * Mongoose context\r
+ */\r
+struct mg_context {\r
+       int             stop_flag;      /* Should we stop event loop    */\r
+       SSL_CTX         *ssl_ctx;       /* SSL context                  */\r
+\r
+       FILE            *access_log;    /* Opened access log            */\r
+       FILE            *error_log;     /* Opened error log             */\r
+\r
+       struct socket   listeners[MAX_LISTENING_SOCKETS];\r
+       int             num_listeners;\r
+\r
+       struct callback callbacks[MAX_CALLBACKS];\r
+       int             num_callbacks;\r
+\r
+       char    *options[NUM_OPTIONS];  /* Configured opions            */\r
+       pthread_mutex_t mutex;          /* Option setter/getter guard   */\r
+       struct socket_pool socket_pool; /* Socket pool                  */\r
+\r
+       mg_spcb_t       ssl_password_callback;\r
+};\r
+\r
+struct mg_connection {\r
+       struct mg_request_info  request_info;\r
+       struct mg_context *ctx;         /* Mongoose context we belong to*/\r
+       SSL             *ssl;           /* SSL descriptor               */\r
+       SOCKET          sock;           /* Connected socket             */\r
+       struct usa      rsa;            /* Remote socket address        */\r
+       struct usa      lsa;            /* Local socket address         */\r
+       time_t          birth_time;     /* Time connection was accepted */\r
+       bool_t          free_post_data; /* post_data was malloc-ed      */\r
+       bool_t          keep_alive;     /* Keep-Alive flag              */\r
+       uint64_t        num_bytes_sent; /* Total bytes sent to client   */\r
+};\r
+\r
+/*\r
+ * In Mongoose, list of values are represented as comma separated\r
+ * string. For example, list of CGI extensions can be represented as\r
+ * ".cgi,.php,.pl", FOR_EACH_WORD_IN_LIST macro allows to\r
+ * loop through the individual values in that list.\r
+ *\r
+ * A "const char *" and "int" variables must be passed to the macro.\r
+ *\r
+ * In every iteration of the loop, "s" points to the current value, and\r
+ * "len" specifies its length. Code inside loop must not change "s" and "len".\r
+ */\r
+#define        FOR_EACH_WORD_IN_LIST(s, len)                                   \\r
+       for (; s != NULL && (len = strcspn(s, ",")) != 0;               \\r
+                       s += len, s+= strspn(s, ","))\r
+\r
+/*\r
+ * Print error message to the opened error log stream.\r
+ */\r
+static void\r
+cry(const char *fmt, ...)\r
+{\r
+       FILE    *fp;\r
+       va_list ap;\r
+\r
+       fp = error_log == NULL ? stderr : error_log;\r
+       va_start(ap, fmt);\r
+       (void) vfprintf(fp, fmt, ap);\r
+       va_end(ap);\r
+\r
+       fputc('\n', fp);\r
+}\r
+\r
+const char *\r
+mg_version(void)\r
+{\r
+       return (MONGOOSE_VERSION);\r
+}\r
+\r
+static void\r
+mg_strlcpy(register char *dst, register const char *src, size_t n)\r
+{\r
+       for (; *src != '\0' && n > 1; n--)\r
+               *dst++ = *src++;\r
+       *dst = '\0';\r
+}\r
+\r
+static int\r
+lowercase(const char *s)\r
+{\r
+       return (tolower(* (unsigned char *) s));\r
+}\r
+\r
+static int\r
+mg_strncasecmp(const char *s1, const char *s2, size_t len)\r
+{\r
+       int     diff = 0;\r
+\r
+       if (len > 0)\r
+               do {\r
+                       diff = lowercase(s1++) - lowercase(s2++);\r
+               } while (diff == 0 && s1[-1] != '\0' && --len > 0);\r
+\r
+       return (diff);\r
+}\r
+\r
+static int\r
+mg_strcasecmp(const char *s1, const char *s2)\r
+{\r
+       int     diff;\r
+\r
+       do {\r
+               diff = lowercase(s1++) - lowercase(s2++);\r
+       } while (diff == 0 && s1[-1] != '\0');\r
+\r
+       return (diff);\r
+}\r
+\r
+static char *\r
+mg_strndup(const char *ptr, size_t len)\r
+{\r
+       char    *p;\r
+\r
+       if ((p = (char *) malloc(len + 1)) != NULL)\r
+               mg_strlcpy(p, ptr, len + 1);\r
+\r
+       return (p);\r
+\r
+}\r
+\r
+static char *\r
+mg_strdup(const char *str)\r
+{\r
+       return (mg_strndup(str, strlen(str)));\r
+}\r
+\r
+/*\r
+ * Like snprintf(), but never returns negative value, or the value\r
+ * that is larger than a supplied buffer.\r
+ * Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability\r
+ * in his audit report.\r
+ */\r
+static int\r
+mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)\r
+{\r
+       int     n;\r
+\r
+       if (buflen == 0)\r
+               return (0);\r
+\r
+       n = vsnprintf(buf, buflen, fmt, ap);\r
+\r
+       if (n < 0) {\r
+               cry("vsnprintf error");\r
+               n = 0;\r
+       } else if (n >= (int) buflen) {\r
+               cry("truncating vsnprintf buffer");\r
+               n = (int) buflen - 1;\r
+       }\r
+       buf[n] = '\0';\r
+\r
+       return (n);\r
+}\r
+\r
+static int\r
+mg_snprintf(char *buf, size_t buflen, const char *fmt, ...)\r
+{\r
+       va_list ap;\r
+       int     n;\r
+\r
+       va_start(ap, fmt);\r
+       n = mg_vsnprintf(buf, buflen, fmt, ap);\r
+       va_end(ap);\r
+\r
+       return (n);\r
+}\r
+\r
+/*\r
+ * Convert string representing a boolean value to a boolean value\r
+ */\r
+static bool_t\r
+is_true(const char *str)\r
+{\r
+       static const char *trues[] = {"1", "yes", "true", "jawohl", NULL};\r
+       const char      **p;\r
+\r
+       for (p = trues; *p != NULL; p++)\r
+               if (str && !mg_strcasecmp(str, *p))\r
+                       return (TRUE);\r
+\r
+       return (FALSE);\r
+}\r
+\r
+/*\r
+ * Skip the characters until one of the delimiters characters found.\r
+ * 0-terminate resulting word. Skip the rest of the delimiters if any.\r
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.\r
+ */\r
+static char *\r
+skip(char **buf, const char *delimiters)\r
+{\r
+       char    *p, *begin_word, *end_word, *end_delimiters;\r
+\r
+       begin_word = *buf;\r
+       end_word = begin_word + strcspn(begin_word, delimiters);\r
+       end_delimiters = end_word + strspn(end_word, delimiters);\r
+\r
+       for (p = end_word; p < end_delimiters; p++)\r
+               *p = '\0';\r
+\r
+       *buf = end_delimiters;\r
+\r
+       return (begin_word);\r
+}\r
+\r
+/*\r
+ * Return HTTP header value, or NULL if not found.\r
+ */\r
+static const char *\r
+get_header(const struct mg_request_info *ri, const char *name)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0; i < ri->num_headers; i++)\r
+               if (!mg_strcasecmp(name, ri->http_headers[i].name))\r
+                       return (ri->http_headers[i].value);\r
+\r
+       return (NULL);\r
+}\r
+\r
+const char *\r
+mg_get_header(const struct mg_connection *conn, const char *name)\r
+{\r
+       return (get_header(&conn->request_info, name));\r
+}\r
+\r
+#if !(defined(NO_CGI) && defined(NO_SSI))\r
+/*\r
+ * Verify that given file has certain extension\r
+ */\r
+static bool_t\r
+match_extension(const char *path, const char *ext_list)\r
+{\r
+       size_t          len, path_len;\r
+\r
+       path_len = strlen(path);\r
+\r
+       FOR_EACH_WORD_IN_LIST(ext_list, len)\r
+               if (len < path_len && path[path_len - (len + 1)] == '.' &&\r
+                   !mg_strncasecmp(path + path_len - len, ext_list, len))\r
+                       return (TRUE);\r
+\r
+       return (FALSE);\r
+}\r
+#endif /* !(NO_CGI && NO_SSI) */\r
+\r
+static bool_t\r
+match_regex(const char *uri, const char *regexp)\r
+{\r
+       if (*regexp == '\0')\r
+               return (*uri == '\0');\r
+\r
+       if (*regexp == '*')\r
+               do {\r
+                       if (match_regex(uri, regexp + 1))\r
+                               return (TRUE);\r
+               } while (*uri++ != '\0');\r
+\r
+       if (*uri != '\0' && *regexp == *uri)\r
+               return (match_regex(uri + 1, regexp + 1));\r
+\r
+       return (FALSE);\r
+}\r
+\r
+static const struct callback *\r
+find_callback(const struct mg_context *ctx, bool_t is_auth,\r
+               const char *uri, int status_code)\r
+{\r
+       const struct callback   *cb;\r
+       int                     i;\r
+\r
+       for (i = 0; i < ctx->num_callbacks; i++) {\r
+               cb = ctx->callbacks + i;\r
+               if ((uri != NULL && cb->uri_regex != NULL &&\r
+                   ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) &&\r
+                   match_regex(uri, cb->uri_regex)) || (uri == NULL &&\r
+                    (cb->status_code == 0 || cb->status_code == status_code)))\r
+                   return (cb);\r
+       }\r
+\r
+       return (NULL);\r
+}\r
+\r
+/*\r
+ * Send error message back to a client.\r
+ */\r
+static void\r
+send_error(struct mg_connection *conn, int status, const char *reason,\r
+               const char *fmt, ...)\r
+{\r
+       const struct callback   *cb;\r
+       char            buf[BUFSIZ];\r
+       va_list         ap;\r
+       int             len;\r
+\r
+       conn->request_info.status_code = status;\r
+\r
+       /* If error handler is set, call it. Otherwise, send error message */\r
+       if ((cb = find_callback(conn->ctx, FALSE, NULL, status)) != NULL) {\r
+               cb->func(conn, &conn->request_info, cb->user_data);\r
+       } else {\r
+               (void) mg_printf(conn,\r
+                   "HTTP/1.1 %d %s\r\n"\r
+                   "Content-Type: text/plain\r\n"\r
+                   "Connection: close\r\n"\r
+                   "\r\n", status, reason);\r
+\r
+               /* Errors 1xx, 204 and 304 MUST NOT send a body */\r
+               if (status > 199 && status != 204 && status != 304) {\r
+                       conn->num_bytes_sent = mg_printf(conn,\r
+                           "Error %d: %s\n", status, reason);\r
+\r
+                       va_start(ap, fmt);\r
+                       len = mg_vsnprintf(buf, sizeof(buf), fmt, ap);\r
+                       va_end(ap);\r
+                       conn->num_bytes_sent += mg_write(conn, buf, len);\r
+               }\r
+       }\r
+}\r
+\r
+#ifdef _WIN32\r
+static int\r
+pthread_mutex_init(pthread_mutex_t *mutex, void *unused)\r
+{\r
+       unused = NULL;\r
+       *mutex = CreateMutex(NULL, FALSE, NULL);\r
+       return (*mutex == NULL ? -1 : 0);\r
+}\r
+\r
+static int\r
+pthread_mutex_destroy(pthread_mutex_t *mutex)\r
+{\r
+       return (CloseHandle(*mutex) == 0 ? -1 : 0);\r
+}\r
+\r
+static int\r
+pthread_mutex_lock(pthread_mutex_t *mutex)\r
+{\r
+       return (WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1);\r
+}\r
+\r
+static int\r
+pthread_mutex_unlock(pthread_mutex_t *mutex)\r
+{\r
+       return (ReleaseMutex(*mutex) == 0 ? -1 : 0);\r
+}\r
+\r
+static int\r
+pthread_cond_init(pthread_cond_t *cv, const void *unused)\r
+{\r
+       unused = NULL;\r
+       *cv = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+       return (*cv == NULL ? -1 : 0);\r
+}\r
+\r
+static int\r
+pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)\r
+{\r
+       SignalObjectAndWait(*mutex, *cv, INFINITE, FALSE);\r
+       WaitForSingleObject(*mutex, INFINITE);\r
+       return (0);\r
+}\r
+\r
+static int\r
+pthread_cond_signal(pthread_cond_t *cv)\r
+{\r
+       return (SetEvent(*cv) == 0 ? -1 : 0);\r
+}\r
+\r
+static int\r
+pthread_cond_destroy(pthread_cond_t *cv)\r
+{\r
+       return (CloseHandle(*cv) == 0 ? -1 : 0);\r
+}\r
+\r
+static void\r
+fix_directory_separators(char *path)\r
+{\r
+       for (; *path != '\0'; path++) {\r
+               if (*path == '/')\r
+                       *path = '\\';\r
+               if (*path == '\\')\r
+                       while (path[1] == '\\' || path[1] == '/')\r
+                               (void) memmove(path + 1,\r
+                                   path + 2, strlen(path + 2) + 1);\r
+       }\r
+}\r
+\r
+static void\r
+to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len)\r
+{\r
+       char    buf[FILENAME_MAX], *p;\r
+\r
+       mg_strlcpy(buf, path, sizeof(buf));\r
+       fix_directory_separators(buf);\r
+\r
+       /* Point p to the end of the file name */\r
+       p = buf + strlen(buf) - 1;\r
+\r
+       /* Trim trailing backslash character */\r
+       while (p > buf && *p == '\\' && p[-1] != ':')\r
+               *p-- = '\0';\r
+\r
+       /*\r
+        * Protect from CGI code disclosure.\r
+        * This is very nasty hole. Windows happily opens files with\r
+        * some garbage in the end of file name. So fopen("a.cgi    ", "r")\r
+        * actually opens "a.cgi", and does not return an error!\r
+        */\r
+       if (*p == 0x20 || *p == 0x2e || *p == 0x2b || (*p & ~0x7f)) {\r
+               cry("Rejecting suspicious path: [%s]", buf);\r
+               buf[0] = '\0';\r
+       }\r
+\r
+       MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);\r
+}\r
+\r
+static int\r
+mg_open(const char *path, int flags, int mode)\r
+{\r
+       wchar_t wbuf[FILENAME_MAX];\r
+\r
+       to_unicode(path, wbuf, ARRAY_SIZE(wbuf));\r
+\r
+       return (_wopen(wbuf, flags, mode));\r
+}\r
+\r
+static int\r
+mg_stat(const char *path, struct stat *stp)\r
+{\r
+       wchar_t wbuf[FILENAME_MAX];\r
+\r
+       to_unicode(path, wbuf, ARRAY_SIZE(wbuf));\r
+\r
+       return (_wstat(wbuf, (struct _stat *) stp));\r
+}\r
+\r
+static int\r
+mg_remove(const char *path)\r
+{\r
+       wchar_t wbuf[FILENAME_MAX];\r
+\r
+       to_unicode(path, wbuf, ARRAY_SIZE(wbuf));\r
+\r
+       return (_wremove(wbuf));\r
+}\r
+\r
+static DIR *\r
+opendir(const char *name)\r
+{\r
+       DIR     *dir = NULL;\r
+       char    path[FILENAME_MAX];\r
+       wchar_t wpath[FILENAME_MAX];\r
+\r
+       if (name == NULL || name[0] == '\0') {\r
+               errno = EINVAL;\r
+       } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {\r
+               errno = ENOMEM;\r
+       } else {\r
+               mg_snprintf(path, sizeof(path), "%s/*", name);\r
+               to_unicode(path, wpath, ARRAY_SIZE(wpath));\r
+               dir->handle = FindFirstFileW(wpath, &dir->info);\r
+\r
+               if (dir->handle != INVALID_HANDLE_VALUE) {\r
+                       dir->result.d_name[0] = '\0';\r
+               } else {\r
+                       free(dir);\r
+                       dir = NULL;\r
+               }\r
+       }\r
+\r
+       return (dir);\r
+}\r
+\r
+static int\r
+closedir(DIR *dir)\r
+{\r
+       int result = -1;\r
+\r
+       if (dir != NULL) {\r
+               if (dir->handle != INVALID_HANDLE_VALUE)\r
+                       result = FindClose(dir->handle) ? 0 : -1;\r
+\r
+               free(dir);\r
+       }\r
+\r
+       if (result == -1)\r
+               errno = EBADF;\r
+\r
+       return (result);\r
+}\r
+\r
+struct dirent *\r
+readdir(DIR *dir)\r
+{\r
+       struct dirent *result = 0;\r
+\r
+       if (dir && dir->handle != INVALID_HANDLE_VALUE) {\r
+               if(!dir->result.d_name ||\r
+                   FindNextFileW(dir->handle, &dir->info)) {\r
+                       result = &dir->result;\r
+\r
+                       WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,\r
+                           -1, result->d_name,\r
+                           sizeof(result->d_name), NULL, NULL);\r
+               }\r
+       } else {\r
+               errno = EBADF;\r
+       }\r
+\r
+       return (result);\r
+}\r
+\r
+#define        set_close_on_exec(fd)   /* No FD_CLOEXEC on Windows */\r
+\r
+static int\r
+start_thread(void * (*func)(void *), void *param)\r
+{\r
+       return (_beginthread((void (__cdecl *)( void *))func, 0, param) == 0);\r
+}\r
+\r
+static bool_t\r
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,\r
+               char *envp[], int fd_stdin, int fd_stdout, const char *dir)\r
+{\r
+       HANDLE  me;\r
+       char    *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];\r
+       FILE    *fp;\r
+       bool_t  retval;\r
+       STARTUPINFOA            si;\r
+       PROCESS_INFORMATION     pi;\r
+\r
+       envp = NULL; /* Unused */\r
+\r
+       (void) memset(&si, 0, sizeof(si));\r
+       (void) memset(&pi, 0, sizeof(pi));\r
+\r
+       /* XXX redirect CGI errors to the error log file */\r
+       si.cb           = sizeof(si);\r
+       si.dwFlags      = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;\r
+       si.wShowWindow  = SW_HIDE;\r
+\r
+       me = GetCurrentProcess();\r
+       DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,\r
+           &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);\r
+       DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,\r
+           &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);\r
+\r
+       /* If CGI file is a script, try to read the interpreter line */\r
+       interp = conn->ctx->options[OPT_CGI_INTERPRETER];\r
+       if (interp == NULL) {\r
+               line[2] = '\0';\r
+               (void) mg_snprintf(cmdline, sizeof(cmdline), "%s%c%s",\r
+                   dir, DIRSEP, prog);\r
+               if ((fp = fopen(cmdline, "r")) != NULL) {\r
+                       (void) fgets(line, sizeof(line), fp);\r
+                       if (memcmp(line, "#!", 2) != 0)\r
+                               line[2] = '\0';\r
+                       /* Trim whitespaces from interpreter name */\r
+                       for (p = &line[strlen(line) - 1]; p > line &&\r
+                           isspace(*p); p--)\r
+                               *p = '\0';\r
+                       (void) fclose(fp);\r
+               }\r
+               interp = line + 2;\r
+               (void) mg_snprintf(cmdline, sizeof(cmdline), "%s%s%s",\r
+                   line + 2, line[2] == '\0' ? "" : " ", prog);\r
+       }\r
+\r
+       if ((p = (char *) strrchr(prog, '/')) != NULL)\r
+               prog = p + 1;\r
+\r
+       (void) mg_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);\r
+       (void) mg_snprintf(line, sizeof(line), "%s", dir);\r
+       fix_directory_separators(line);\r
+       fix_directory_separators(cmdline);\r
+\r
+       if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,\r
+           CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {\r
+               cry("%s: CreateProcess(%s): %d", __func__, cmdline, ERRNO);\r
+               retval = FALSE;\r
+       } else {\r
+               close(fd_stdin);\r
+               close(fd_stdout);\r
+               retval = TRUE;\r
+       }\r
+\r
+       CloseHandle(si.hStdOutput);\r
+       CloseHandle(si.hStdInput);\r
+       CloseHandle(pi.hThread);\r
+       CloseHandle(pi.hProcess);\r
+\r
+       return (retval);\r
+}\r
+\r
+static int\r
+pipe(int *fds)\r
+{\r
+       return (_pipe(fds, BUFSIZ, _O_BINARY));\r
+}\r
+\r
+static int\r
+mg_mkdir(const char *path, int mode)\r
+{\r
+       char    buf[FILENAME_MAX];\r
+       wchar_t wbuf[FILENAME_MAX];\r
+\r
+       mode = 0; /* Unused */\r
+       mg_strlcpy(buf, path, sizeof(buf));\r
+       fix_directory_separators(buf);\r
+\r
+       MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));\r
+\r
+       return (_wmkdir(wbuf));\r
+}\r
+\r
+#else\r
+\r
+static void\r
+set_close_on_exec(int fd)\r
+{\r
+       (void) fcntl(fd, F_SETFD, FD_CLOEXEC);\r
+}\r
+\r
+static int\r
+start_thread(void * (*func)(void *), void *param)\r
+{\r
+       pthread_t       thread_id;\r
+       pthread_attr_t  attr;\r
+       int             retval;\r
+\r
+       (void) pthread_attr_init(&attr);\r
+       (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\r
+\r
+       if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0)\r
+               cry("%s: %s", __func__, strerror(retval));\r
+\r
+       return (retval);\r
+}\r
+\r
+#ifndef NO_CGI\r
+static bool_t\r
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,\r
+               char *envp[], int fd_stdin, int fd_stdout, const char *dir)\r
+{\r
+       int             ret;\r
+       pid_t           pid;\r
+       const char      *interp;\r
+\r
+       envblk = NULL;  /* unused */\r
+       ret = FALSE;\r
+\r
+       if ((pid = fork()) == -1) {\r
+               /* Parent */\r
+               ret = -1;\r
+               send_error(conn, 500, http_500_error,\r
+                   "fork(): %s", strerror(ERRNO));\r
+       } else if (pid == 0) {\r
+               /* Child */\r
+               if (chdir(dir) != 0) {\r
+                       cry("chdir(%s): %s", dir, strerror(ERRNO));\r
+               } else if (dup2(fd_stdin, 0) == -1) {\r
+                       cry("dup2(stdin, %d): %s", fd_stdin, strerror(ERRNO));\r
+               } else if (dup2(fd_stdout, 1) == -1) {\r
+                       cry("dup2(stdout, %d): %s", fd_stdout, strerror(ERRNO));\r
+               } else {\r
+                       /* If error file is specified, send errors there */\r
+                       if (error_log != NULL)\r
+                               (void) dup2(fileno(error_log), 2);\r
+\r
+                       (void) close(fd_stdin);\r
+                       (void) close(fd_stdout);\r
+\r
+                       /* Execute CGI program */\r
+                       interp = conn->ctx->options[OPT_CGI_INTERPRETER];\r
+                       if (interp == NULL) {\r
+                               (void) execle(prog, prog, NULL, envp);\r
+                               cry("execle(%s): %s", prog, strerror(ERRNO));\r
+                       } else {\r
+                               (void) execle(interp, interp, prog, NULL, envp);\r
+                               cry("execle(%s %s): %s",\r
+                                   interp, prog, strerror(ERRNO));\r
+                       }\r
+               }\r
+               exit(EXIT_FAILURE);\r
+       } else {\r
+               /* Parent. Suspended until child does execle() */\r
+               (void) close(fd_stdin);\r
+               (void) close(fd_stdout);\r
+               ret = TRUE;\r
+       }\r
+\r
+       return (ret);\r
+}\r
+#endif /* !NO_CGI */\r
+#endif /* _WIN32 */\r
+\r
+static void\r
+mg_lock(struct mg_context *ctx)\r
+{\r
+       if (pthread_mutex_lock(&ctx->mutex) != 0)\r
+               cry("pthread_mutex_lock: %s", strerror(ERRNO));\r
+}\r
+\r
+static void\r
+mg_unlock(struct mg_context *ctx)\r
+{\r
+       if (pthread_mutex_unlock(&ctx->mutex) != 0)\r
+               cry("pthread_mutex_unlock: %s", strerror(ERRNO));\r
+}\r
+\r
+static int\r
+get_pool_space(const struct socket_pool *pool)\r
+{\r
+       return (pool->size - (pool->head - pool->tail));\r
+}\r
+\r
+static void\r
+init_socket_pool(struct socket_pool *pool)\r
+{\r
+       pool->size = (int) ARRAY_SIZE(pool->sockets);\r
+       pool->head = pool->tail = 0;\r
+\r
+       pthread_mutex_init(&pool->mutex, NULL);\r
+       pthread_cond_init(&pool->put_cond, NULL);\r
+       pthread_cond_init(&pool->get_cond, NULL);\r
+}\r
+\r
+static void\r
+destroy_socket_pool(struct socket_pool *pool)\r
+{\r
+       int     i;\r
+\r
+       pthread_mutex_lock(&pool->mutex);\r
+       for (i = 0; i < get_pool_space(pool); i++)\r
+               (void) closesocket(pool->sockets[i].sock);\r
+       pthread_mutex_unlock(&pool->mutex);\r
+\r
+       /*\r
+        * TODO: all threads in a thread pool are blocked on pool->mutex.\r
+        * Before destroying the mutex, send a termination signal to all\r
+        * of these threads, and let them exit. Only after that destroy\r
+        * the mutex.\r
+        */\r
+\r
+       pthread_mutex_destroy(&pool->mutex);\r
+       pthread_cond_destroy(&pool->put_cond);\r
+       pthread_cond_destroy(&pool->get_cond);\r
+}\r
+\r
+/*\r
+ * Put socket into the pool\r
+ */\r
+static void\r
+put_socket(struct socket_pool *pool, const struct socket *sp)\r
+{\r
+       (void) pthread_mutex_lock(&pool->mutex);\r
+\r
+       while (get_pool_space(pool) == 0)\r
+               pthread_cond_wait(&pool->put_cond, &pool->mutex);\r
+\r
+       pool->sockets[pool->head++ % pool->size] = *sp;\r
+\r
+       pthread_cond_signal(&pool->get_cond);\r
+       pthread_mutex_unlock(&pool->mutex);\r
+}\r
+\r
+/*\r
+ * Get index of the socket to process\r
+ */\r
+static void\r
+get_socket(struct socket_pool *pool, struct socket *sp)\r
+{\r
+       pthread_mutex_lock(&pool->mutex);\r
+\r
+       while (get_pool_space(pool) == pool->size)\r
+               pthread_cond_wait(&pool->get_cond, &pool->mutex);\r
+\r
+       *sp = pool->sockets[pool->tail++];\r
+\r
+       /* Wrap pointers */\r
+       if (pool->tail  == pool->size) {\r
+               pool->head -= pool->size;\r
+               pool->tail = 0;\r
+       }\r
+\r
+       pthread_cond_signal(&pool->put_cond);\r
+       pthread_mutex_unlock(&pool->mutex);\r
+}\r
+\r
+\r
+/*\r
+ * Write data to the IO channel - opened file descriptor, socket or SSL\r
+ * descriptor. Return number of bytes written.\r
+ */\r
+static uint64_t\r
+push(int fd, SOCKET sock, SSL *ssl, const char *buf, uint64_t len)\r
+{\r
+       uint64_t        sent;\r
+       int             n, k;\r
+\r
+       sent = 0;\r
+       while (sent < len) {\r
+\r
+               /* How many bytes we send in this iteration */\r
+               k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);\r
+\r
+               if (ssl != NULL) {\r
+                       n = SSL_write(ssl, buf + sent, k);\r
+               } else if (fd != -1) {\r
+                       n = write(fd, buf + sent, k);\r
+               } else {\r
+                       n = send(sock, buf + sent, k, 0);\r
+               }\r
+\r
+               if (n < 0)\r
+                       break;\r
+\r
+               sent += n;\r
+       }\r
+\r
+       return (sent);\r
+}\r
+\r
+/*\r
+ * Read from IO channel - opened file descriptor, socket, or SSL descriptor.\r
+ * Return number of bytes read.\r
+ */\r
+static int\r
+pull(int fd, SOCKET sock, SSL *ssl, char *buf, int len)\r
+{\r
+       int     nread;\r
+\r
+       if (ssl != NULL) {\r
+               nread = SSL_read(ssl, buf, len);\r
+       } else if (fd != -1) {\r
+               nread = read(fd, buf, (size_t) len);\r
+       } else {\r
+               nread = recv(sock, buf, (size_t) len, 0);\r
+       }\r
+\r
+       return (nread);\r
+}\r
+\r
+int\r
+mg_write(struct mg_connection *conn, const void *buf, int len)\r
+{\r
+       assert(len >= 0);\r
+       return ((int) push(-1, conn->sock, conn->ssl,\r
+                               (const char *) buf, (uint64_t) len));\r
+}\r
+\r
+int\r
+mg_printf(struct mg_connection *conn, const char *fmt, ...)\r
+{\r
+       char    buf[MAX_REQUEST_SIZE];\r
+       int     len;\r
+       va_list ap;\r
+\r
+       va_start(ap, fmt);\r
+       len = mg_vsnprintf(buf, sizeof(buf), fmt, ap);\r
+       va_end(ap);\r
+\r
+       return (mg_write(conn, buf, len));\r
+}\r
+\r
+/*\r
+ * Return content length of the request, or UNKNOWN_CONTENT_LENGTH constant if\r
+ * Content-Length header is not set.\r
+ */\r
+static uint64_t\r
+get_content_length(const struct mg_connection *conn)\r
+{\r
+       const char *cl = mg_get_header(conn, "Content-Length");\r
+       return (cl == NULL ? UNKNOWN_CONTENT_LENGTH : strtoull(cl, NULL, 10));\r
+}\r
+\r
+/*\r
+ * URL-decode input buffer into destination buffer.\r
+ * 0-terminate the destination buffer. Return the length of decoded data.\r
+ * form-url-encoded data differs from URI encoding in a way that it\r
+ * uses '+' as character for space, see RFC 1866 section 8.2.1\r
+ * http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt\r
+ */\r
+static size_t\r
+url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,\r
+               bool_t is_form_url_encoded)\r
+{\r
+       size_t  i, j;\r
+       int     a, b;\r
+#define        HEXTOI(x)  (isdigit(x) ? x - '0' : x - 'W')\r
+\r
+       for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {\r
+               if (src[i] == '%' &&\r
+                   isxdigit(* (unsigned char *) (src + i + 1)) &&\r
+                   isxdigit(* (unsigned char *) (src + i + 2))) {\r
+                       a = tolower(* (unsigned char *) (src + i + 1));\r
+                       b = tolower(* (unsigned char *) (src + i + 2));\r
+                       dst[j] = ((HEXTOI(a) << 4) | HEXTOI(b)) & 0xff;\r
+                       i += 2;\r
+               } else if (is_form_url_encoded && src[i] == '+') {\r
+                       dst[j] = ' ';\r
+               } else {\r
+                       dst[j] = src[i];\r
+               }\r
+       }\r
+\r
+       dst[j] = '\0';  /* Null-terminate the destination */\r
+\r
+       return (j);\r
+}\r
+\r
+/*\r
+ * Search for a form variable in a given buffer.\r
+ * Semantic is the same as for mg_get_var().\r
+ */\r
+static char *\r
+get_var(const char *name, const char *buf, size_t buf_len)\r
+{\r
+       const char      *p, *e, *s;\r
+       char            *val;\r
+       size_t          var_len, len;\r
+\r
+       var_len = strlen(name);\r
+       e = buf + buf_len;\r
+       val = NULL;\r
+\r
+       /* buf is "var1=val1&var2=val2...". Find variable first */\r
+       for (p = buf; p + var_len < e; p++)\r
+               if ((p == buf || p[-1] == '&') && p[var_len] == '=' &&\r
+                   !mg_strncasecmp(name, p, var_len)) {\r
+\r
+                       /* Point p to variable value */\r
+                       p += var_len + 1;\r
+\r
+                       /* Point s to the end of the value */\r
+                       s = (const char *) memchr(p, '&', e - p);\r
+                       if (s == NULL)\r
+                               s = e;\r
+\r
+                       /* Try to allocate the buffer */\r
+                       len = s - p + 1;\r
+                       if ((val = malloc(len)) != NULL)\r
+                               (void) url_decode(p, len, val, len, TRUE);\r
+                       break;\r
+               }\r
+\r
+       return (val);\r
+}\r
+\r
+/*\r
+ * Return form data variable.\r
+ * It can be specified in query string, or in the POST data.\r
+ * Return NULL if the variable not found, or allocated 0-terminated value.\r
+ * It is caller's responsibility to free the returned value.\r
+ */\r
+char *\r
+mg_get_var(const struct mg_connection *conn, const char *name)\r
+{\r
+       const struct mg_request_info    *ri = &conn->request_info;\r
+       char                            *v1, *v2;\r
+\r
+       v1 = v2 = NULL;\r
+\r
+       /* Look in both query_string and POST data */\r
+       if (ri->query_string != NULL)\r
+               v1 = get_var(name, ri->query_string, strlen(ri->query_string));\r
+       if (ri->post_data_len > 0)\r
+               v2 = get_var(name, ri->post_data, ri->post_data_len);\r
+\r
+       /* If they both have queried variable, POST data wins */\r
+       if (v1 != NULL && v2 != NULL)\r
+               free(v1);\r
+\r
+       return (v2 == NULL ? v1 : v2);\r
+}\r
+\r
+/*\r
+ * Transform URI to the file name.\r
+ */\r
+static void\r
+make_path(struct mg_context *ctx, const char *uri, char *buf, size_t buf_len)\r
+{\r
+       char    *p, *s;\r
+       size_t  len;\r
+\r
+       mg_snprintf(buf, buf_len, "%s%s", ctx->options[OPT_ROOT], uri);\r
+\r
+       /* If requested URI has aliased prefix, use alternate root */\r
+       mg_lock(ctx);\r
+               s = ctx->options[OPT_ALIASES];\r
+       FOR_EACH_WORD_IN_LIST(s, len) {\r
+\r
+               p = (char *) memchr(s, '=', len);\r
+               if (p == NULL || p >= s + len || p == s)\r
+                       continue;\r
+\r
+               if (memcmp(uri, s, p - s) == 0) {\r
+                       (void) mg_snprintf(buf, buf_len, "%.*s%s",\r
+                           (s + len) - p - 1, p + 1, uri + (p - s));\r
+                       break;\r
+               }\r
+       }\r
+       mg_unlock(ctx);\r
+\r
+       /* Remove trailing '/' characters, if directory is requested */\r
+       for (p = buf + strlen(buf) - 1; p > buf && *p == '/'; p--)\r
+               *p = '\0';\r
+\r
+#ifdef _WIN32\r
+       for (p = buf; *p != '\0'; p++)\r
+               if (*p == '/')\r
+                       *p = '\\';\r
+#endif /* _WIN32 */\r
+}\r
+\r
+/*\r
+ * Setup listening socket on given port, return socket\r
+ */\r
+static SOCKET\r
+mg_open_listening_port(int port)\r
+{\r
+       SOCKET          sock;\r
+       int             on = 1;\r
+       struct usa      sa;\r
+\r
+       sa.len                          = sizeof(sa.u.sin);\r
+       sa.u.sin.sin_family             = AF_INET;\r
+       sa.u.sin.sin_port               = htons((uint16_t) port);\r
+       sa.u.sin.sin_addr.s_addr        = htonl(INADDR_ANY);\r
+\r
+       if ((sock = socket(PF_INET, SOCK_STREAM, 6)) != INVALID_SOCKET &&\r
+           setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,\r
+           (char *) &on, sizeof(on)) == 0 &&\r
+           bind(sock, &sa.u.sa, sa.len) == 0 &&\r
+           listen(sock, 128) == 0) {\r
+               /* Success */\r
+               set_close_on_exec(sock);\r
+       } else {\r
+               /* Error */\r
+               cry("open_listening_port(%d): %s", port, strerror(errno));\r
+               if (sock != INVALID_SOCKET)\r
+                       (void) closesocket(sock);\r
+               sock = INVALID_SOCKET;\r
+       }\r
+\r
+       return (sock);\r
+}\r
+\r
+/*\r
+ * Check whether full request is buffered Return headers length, or 0\r
+ */\r
+static int\r
+get_request_len(const char *buf, size_t buflen)\r
+{\r
+       const char      *s, *e;\r
+       int             len = 0;\r
+\r
+       for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)\r
+               /* Control characters are not allowed but >=128 is. */\r
+               if (!isprint(* (unsigned char *) s) && *s != '\r' &&\r
+                   *s != '\n' && * (unsigned char *) s < 128)\r
+                       len = -1;\r
+               else if (s[0] == '\n' && s[1] == '\n')\r
+                       len = (int) (s - buf) + 2;\r
+               else if (s[0] == '\n' && &s[1] < e &&\r
+                   s[1] == '\r' && s[2] == '\n')\r
+                       len = (int) (s - buf) + 3;\r
+\r
+       return (len);\r
+}\r
+\r
+/*\r
+ * Convert month to the month number. Return -1 on error, or month number\r
+ */\r
+static int\r
+montoi(const char *s)\r
+{\r
+       static const char *month_names[] = {\r
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",\r
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"\r
+       };\r
+       size_t  i;\r
+\r
+       for (i = 0; i < sizeof(month_names) / sizeof(month_names[0]); i++)\r
+               if (!strcmp(s, month_names[i]))\r
+                       return ((int) i);\r
+\r
+       return (-1);\r
+}\r
+\r
+/*\r
+ * Parse date-time string, and return the corresponding time_t value\r
+ */\r
+static time_t\r
+date_to_epoch(const char *s)\r
+{\r
+       struct tm       tm, *tmp;\r
+       time_t          current_time;\r
+       char            mon[32];\r
+       int             sec, min, hour, mday, month, year;\r
+\r
+       (void) memset(&tm, 0, sizeof(tm));\r
+       sec = min = hour = mday = month = year = 0;\r
+\r
+       if (((sscanf(s, "%d/%3s/%d %d:%d:%d",\r
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||\r
+           (sscanf(s, "%d %3s %d %d:%d:%d",\r
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||\r
+           (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",\r
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||\r
+           (sscanf(s, "%d-%3s-%d %d:%d:%d",\r
+           &mday, mon, &year, &hour, &min, &sec) == 6)) &&\r
+           (month = montoi(mon)) != -1) {\r
+               tm.tm_mday      = mday;\r
+               tm.tm_mon       = month;\r
+               tm.tm_year      = year;\r
+               tm.tm_hour      = hour;\r
+               tm.tm_min       = min;\r
+               tm.tm_sec       = sec;\r
+       }\r
+\r
+       if (tm.tm_year > 1900)\r
+               tm.tm_year -= 1900;\r
+       else if (tm.tm_year < 70)\r
+               tm.tm_year += 100;\r
+\r
+       /* Set Daylight Saving Time field */\r
+       current_time = time(NULL);\r
+       tmp = localtime(&current_time);\r
+       tm.tm_isdst = tmp->tm_isdst;\r
+\r
+       return (mktime(&tm));\r
+}\r
+\r
+static void\r
+remove_double_dots(char *s)\r
+{\r
+       char    *p = s;\r
+\r
+       while (*s != '\0') {\r
+               *p++ = *s++;\r
+               if (s[-1] == '/' || s[-1] == '\\')\r
+                       while (*s == '.' || *s == '/' || *s == '\\')\r
+                               s++;\r
+       }\r
+       *p = '\0';\r
+}\r
+\r
+static const struct {\r
+       const char      *extension;\r
+       const char      *mime_type;\r
+} mime_types[] = {\r
+       {"html",        "text/html"                     },\r
+       {"htm",         "text/html"                     },\r
+       {"shtm",        "text/html"                     },\r
+       {"shtml",       "text/html"                     },\r
+       {"css",         "text/css"                      },\r
+       {"js",          "application/x-javascript"      },\r
+       {"ico",         "image/x-icon"                  },\r
+       {"gif",         "image/gif"                     },\r
+       {"jpg",         "image/jpeg"                    },\r
+       {"jpeg",        "image/jpeg"                    },\r
+       {"png",         "image/png"                     },\r
+       {"svg",         "image/svg+xml"                 },\r
+       {"torrent",     "application/x-bittorrent"      },\r
+       {"wav",         "audio/x-wav"                   },\r
+       {"mp3",         "audio/x-mp3"                   },\r
+       {"mid",         "audio/mid"                     },\r
+       {"m3u",         "audio/x-mpegurl"               },\r
+       {"ram",         "audio/x-pn-realaudio"          },\r
+       {"ra",          "audio/x-pn-realaudio"          },\r
+       {"doc",         "application/msword",           },\r
+       {"exe",         "application/octet-stream"      },\r
+       {"zip",         "application/x-zip-compressed"  },\r
+       {"xls",         "application/excel"             },\r
+       {"tgz",         "application/x-tar-gz"          },\r
+       {"tar",         "application/x-tar"             },\r
+       {"gz",          "application/x-gunzip"          },\r
+       {"arj",         "application/x-arj-compressed"  },\r
+       {"rar",         "application/x-arj-compressed"  },\r
+       {"rtf",         "application/rtf"               },\r
+       {"pdf",         "application/pdf"               },\r
+       {"swf",         "application/x-shockwave-flash" },\r
+       {"mpg",         "video/mpeg"                    },\r
+       {"mpeg",        "video/mpeg"                    },\r
+       {"asf",         "video/x-ms-asf"                },\r
+       {"avi",         "video/x-msvideo"               },\r
+       {"bmp",         "image/bmp"                     },\r
+       {NULL,          NULL                            }\r
+};\r
+\r
+static const char *\r
+get_mime_type(const char *path)\r
+{\r
+       size_t          i;\r
+       const char      *ext;\r
+\r
+       if ((ext = strrchr(path, '.')) != NULL) {\r
+               ext++;\r
+               for (i = 0; mime_types[i].extension != NULL; i++)\r
+                       if (!mg_strcasecmp(ext, mime_types[i].extension))\r
+                               return (mime_types[i].mime_type);\r
+       }\r
+\r
+       return ("text/plain");\r
+}\r
+\r
+#if !defined(NO_AUTH)\r
+#ifndef HAVE_MD5\r
+typedef struct MD5Context {\r
+       uint32_t        buf[4];\r
+       uint32_t        bits[2];\r
+       unsigned char   in[64];\r
+} MD5_CTX;\r
+\r
+#if __BYTE_ORDER == 1234\r
+#define byteReverse(buf, len)  /* Nothing */\r
+#else\r
+/*\r
+ * Note: this code is harmless on little-endian machines.\r
+ */\r
+static void\r
+byteReverse(unsigned char *buf, unsigned longs)\r
+{\r
+       uint32_t t;\r
+       do {\r
+               t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |\r
+                       ((unsigned) buf[1] << 8 | buf[0]);\r
+               *(uint32_t *) buf = t;\r
+               buf += 4;\r
+       } while (--longs);\r
+}\r
+#endif /* __BYTE_ORDER */\r
+\r
+/* The four core functions - F1 is optimized somewhat */\r
+\r
+/* #define F1(x, y, z) (x & y | ~x & z) */\r
+#define F1(x, y, z) (z ^ (x & (y ^ z)))\r
+#define F2(x, y, z) F1(z, x, y)\r
+#define F3(x, y, z) (x ^ y ^ z)\r
+#define F4(x, y, z) (y ^ (x | ~z))\r
+\r
+/* This is the central step in the MD5 algorithm. */\r
+#define MD5STEP(f, w, x, y, z, data, s) \\r
+( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )\r
+\r
+/*\r
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious\r
+ * initialization constants.\r
+ */\r
+static void\r
+MD5Init(MD5_CTX *ctx)\r
+{\r
+       ctx->buf[0] = 0x67452301;\r
+       ctx->buf[1] = 0xefcdab89;\r
+       ctx->buf[2] = 0x98badcfe;\r
+       ctx->buf[3] = 0x10325476;\r
+\r
+       ctx->bits[0] = 0;\r
+       ctx->bits[1] = 0;\r
+}\r
+\r
+/*\r
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to\r
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks\r
+ * the data and converts bytes into longwords for this routine.\r
+ */\r
+static void\r
+MD5Transform(uint32_t buf[4], uint32_t const in[16])\r
+{\r
+       register uint32_t a, b, c, d;\r
+\r
+       a = buf[0];\r
+       b = buf[1];\r
+       c = buf[2];\r
+       d = buf[3];\r
+\r
+       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);\r
+       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);\r
+       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);\r
+       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);\r
+       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);\r
+       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);\r
+       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);\r
+       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);\r
+       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);\r
+       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);\r
+       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);\r
+       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);\r
+       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);\r
+       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);\r
+       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);\r
+       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);\r
+\r
+       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);\r
+       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);\r
+       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);\r
+       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);\r
+       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);\r
+       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);\r
+       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);\r
+       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);\r
+       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);\r
+       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);\r
+       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);\r
+       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);\r
+       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);\r
+       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);\r
+       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);\r
+       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);\r
+\r
+       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);\r
+       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);\r
+       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);\r
+       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);\r
+       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);\r
+       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);\r
+       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);\r
+       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);\r
+       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);\r
+       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);\r
+       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);\r
+       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);\r
+       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);\r
+       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);\r
+       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);\r
+       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);\r
+\r
+       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);\r
+       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);\r
+       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);\r
+       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);\r
+       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);\r
+       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);\r
+       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);\r
+       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);\r
+       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);\r
+       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);\r
+       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);\r
+       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);\r
+       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);\r
+       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);\r
+       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);\r
+       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);\r
+\r
+       buf[0] += a;\r
+       buf[1] += b;\r
+       buf[2] += c;\r
+       buf[3] += d;\r
+}\r
+\r
+/*\r
+ * Update context to reflect the concatenation of another buffer full\r
+ * of bytes.\r
+ */\r
+static void\r
+MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)\r
+{\r
+       uint32_t t;\r
+\r
+       /* Update bitcount */\r
+\r
+       t = ctx->bits[0];\r
+       if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)\r
+               ctx->bits[1]++;         /* Carry from low to high */\r
+       ctx->bits[1] += len >> 29;\r
+\r
+       t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */\r
+\r
+       /* Handle any leading odd-sized chunks */\r
+\r
+       if (t) {\r
+               unsigned char *p = (unsigned char *) ctx->in + t;\r
+\r
+               t = 64 - t;\r
+               if (len < t) {\r
+                       memcpy(p, buf, len);\r
+                       return;\r
+               }\r
+               memcpy(p, buf, t);\r
+               byteReverse(ctx->in, 16);\r
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);\r
+               buf += t;\r
+               len -= t;\r
+       }\r
+       /* Process data in 64-byte chunks */\r
+\r
+       while (len >= 64) {\r
+               memcpy(ctx->in, buf, 64);\r
+               byteReverse(ctx->in, 16);\r
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);\r
+               buf += 64;\r
+               len -= 64;\r
+       }\r
+\r
+       /* Handle any remaining bytes of data. */\r
+\r
+       memcpy(ctx->in, buf, len);\r
+}\r
+\r
+/*\r
+ * Final wrapup - pad to 64-byte boundary with the bit pattern\r
+ * 1 0* (64-bit count of bits processed, MSB-first)\r
+ */\r
+static void\r
+MD5Final(unsigned char digest[16], MD5_CTX *ctx)\r
+{\r
+       unsigned count;\r
+       unsigned char *p;\r
+\r
+       /* Compute number of bytes mod 64 */\r
+       count = (ctx->bits[0] >> 3) & 0x3F;\r
+\r
+       /* Set the first char of padding to 0x80.  This is safe since there is\r
+          always at least one byte free */\r
+       p = ctx->in + count;\r
+       *p++ = 0x80;\r
+\r
+       /* Bytes of padding needed to make 64 bytes */\r
+       count = 64 - 1 - count;\r
+\r
+       /* Pad out to 56 mod 64 */\r
+       if (count < 8) {\r
+               /* Two lots of padding:  Pad the first block to 64 bytes */\r
+               memset(p, 0, count);\r
+               byteReverse(ctx->in, 16);\r
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);\r
+\r
+               /* Now fill the next block with 56 bytes */\r
+               memset(ctx->in, 0, 56);\r
+       } else {\r
+               /* Pad block to 56 bytes */\r
+               memset(p, 0, count - 8);\r
+       }\r
+       byteReverse(ctx->in, 14);\r
+\r
+       /* Append length in bits and transform */\r
+       ((uint32_t *) ctx->in)[14] = ctx->bits[0];\r
+       ((uint32_t *) ctx->in)[15] = ctx->bits[1];\r
+\r
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);\r
+       byteReverse((unsigned char *) ctx->buf, 4);\r
+       memcpy(digest, ctx->buf, 16);\r
+       memset((char *) ctx, 0, sizeof(ctx));   /* In case it's sensitive */\r
+}\r
+#endif /* !HAVE_MD5 */\r
+\r
+/*\r
+ * Stringify binary data. Output buffer must be twice as big as input,\r
+ * because each byte takes 2 bytes in string representation\r
+ */\r
+static void\r
+bin2str(char *to, const unsigned char *p, size_t len)\r
+{\r
+       static const char *hex = "0123456789abcdef";\r
+\r
+       for (; len--; p++) {\r
+               *to++ = hex[p[0] >> 4];\r
+               *to++ = hex[p[0] & 0x0f];\r
+       }\r
+       *to = '\0';\r
+}\r
+\r
+/*\r
+ * Return stringified MD5 hash for list of vectors.\r
+ * buf must point to 33-bytes long buffer\r
+ */\r
+void\r
+mg_md5(char *buf, ...)\r
+{\r
+       unsigned char   hash[16];\r
+       const char      *p;\r
+       va_list         ap;\r
+       MD5_CTX         ctx;\r
+\r
+       MD5Init(&ctx);\r
+\r
+       va_start(ap, buf);\r
+       while ((p = va_arg(ap, const char *)) != NULL)\r
+               MD5Update(&ctx, (unsigned char *) p, (int) strlen(p));\r
+       va_end(ap);\r
+\r
+       MD5Final(hash, &ctx);\r
+       bin2str(buf, hash, sizeof(hash));\r
+}\r
+\r
+/*\r
+ * Check the user's password, return 1 if OK\r
+ */\r
+static bool_t\r
+check_password(const char *method, const char *ha1, const char *uri,\r
+               const char *nonce, const char *nc, const char *cnonce,\r
+               const char *qop, const char *response)\r
+{\r
+       char    ha2[32 + 1], expected_response[32 + 1];\r
+\r
+       /* XXX  Due to a bug in MSIE, we do not compare the URI  */\r
+       /* Also, we do not check for authentication timeout */\r
+       if (/*strcmp(dig->uri, c->ouri) != 0 || */\r
+           strlen(response) != 32 /*||\r
+           now - strtoul(dig->nonce, NULL, 10) > 3600 */)\r
+               return (FALSE);\r
+\r
+       mg_md5(ha2, method, ":", uri, NULL);\r
+       mg_md5(expected_response, ha1, ":", nonce, ":", nc,\r
+           ":", cnonce, ":", qop, ":", ha2, NULL);\r
+\r
+       return (!mg_strcasecmp(response, expected_response));\r
+}\r
+\r
+/*\r
+ * Use the global passwords file, if specified by auth_gpass option,\r
+ * or search for .htpasswd in the requested directory.\r
+ */\r
+static FILE *\r
+open_auth_file(struct mg_context *ctx, const char *path)\r
+{\r
+       char            name[FILENAME_MAX];\r
+       const char      *p, *e;\r
+       struct stat     st;\r
+       FILE            *fp;\r
+\r
+       if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {\r
+               /* Use global passwords file */\r
+               if ((fp = fopen(ctx->options[OPT_AUTH_GPASSWD], "r")) == NULL)\r
+                       cry("fopen(%s): %s",\r
+                           ctx->options[OPT_AUTH_GPASSWD], strerror(ERRNO));\r
+       } else if (!mg_stat(path, &st) && S_ISDIR(st.st_mode)) {\r
+               (void) mg_snprintf(name, sizeof(name), "%s%c%s",\r
+                   path, DIRSEP, PASSWORDS_FILE_NAME);\r
+               fp = fopen(name, "r");\r
+       } else {\r
+               /*\r
+                * Try to find .htpasswd in requested directory.\r
+                * Given the path, create the path to .htpasswd file\r
+                * in the same directory. Find the right-most\r
+                * directory separator character first. That would be the\r
+                * directory name. If directory separator character is not\r
+                * found, 'e' will point to 'p'.\r
+                */\r
+               for (p = path, e = p + strlen(p) - 1; e > p; e--)\r
+                       if (IS_DIRSEP_CHAR(*e))\r
+                               break;\r
+\r
+               /*\r
+                * Make up the path by concatenating directory name and\r
+                * .htpasswd file name.\r
+                */\r
+               (void) mg_snprintf(name, sizeof(name), "%.*s%c%s",\r
+                   (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);\r
+               fp = fopen(name, "r");\r
+       }\r
+\r
+       return (fp);\r
+}\r
+\r
+struct ah {\r
+       char    *user, *uri, *cnonce, *response, *qop, *nc, *nonce;\r
+};\r
+\r
+static bool_t\r
+parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size,\r
+               struct ah *ah)\r
+{\r
+       char            *name, *value, *s;\r
+       const char      *auth_header;\r
+\r
+       if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||\r
+           mg_strncasecmp(auth_header, "Digest ", 7) != 0)\r
+               return (FALSE);\r
+\r
+       /* Make modifiable copy of the auth header */\r
+       (void) mg_strlcpy(buf, auth_header + 7, buf_size);\r
+\r
+       s = buf;\r
+       (void) memset(ah, 0, sizeof(*ah));\r
+\r
+       /* Gobble initial spaces */\r
+       while (isspace(* (unsigned char *) s))\r
+               s++;\r
+\r
+       /* Parse authorization header */\r
+       for (;;) {\r
+               name = skip(&s, "=");\r
+               value = skip(&s, ", ");\r
+\r
+               if (*value == '"') {\r
+                       value++;\r
+                       value[strlen(value) - 1] = '\0';\r
+               } else if (*value == '\0') {\r
+                       break;\r
+               }\r
+\r
+               if (!strcmp(name, "username")) {\r
+                       ah->user = value;\r
+               } else if (!strcmp(name, "cnonce")) {\r
+                       ah->cnonce = value;\r
+               } else if (!strcmp(name, "response")) {\r
+                       ah->response = value;\r
+               } else if (!strcmp(name, "uri")) {\r
+                       ah->uri = value;\r
+               } else if (!strcmp(name, "qop")) {\r
+                       ah->qop = value;\r
+               } else if (!strcmp(name, "nc")) {\r
+                       ah->nc = value;\r
+               } else if (!strcmp(name, "nonce")) {\r
+                       ah->nonce = value;\r
+               }\r
+       }\r
+\r
+       /* CGI needs it as REMOTE_USER */\r
+       if (ah->user != NULL)\r
+               conn->request_info.remote_user = mg_strdup(ah->user);\r
+\r
+       return (TRUE);\r
+}\r
+\r
+/*\r
+ * Authorize against the opened passwords file. Return 1 if authorized.\r
+ */\r
+static bool_t\r
+authorize(struct mg_connection *conn, FILE *fp)\r
+{\r
+       struct ah       ah;\r
+       char            line[256], f_user[256], domain[256], ha1[256],\r
+                       buf[MAX_REQUEST_SIZE];\r
+\r
+       if (!parse_auth_header(conn, buf, sizeof(buf), &ah))\r
+               return (FALSE);\r
+\r
+       /* Loop over passwords file */\r
+       while (fgets(line, sizeof(line), fp) != NULL) {\r
+\r
+               if (sscanf(line, "%[^:]:%[^:]:%s", f_user, domain, ha1) != 3)\r
+                       continue;\r
+\r
+               if (!strcmp(ah.user, f_user) &&\r
+                   !strcmp(domain, conn->ctx->options[OPT_AUTH_DOMAIN]))\r
+                       return (check_password(\r
+                           conn->request_info.request_method, ha1,\r
+                           ah.uri, ah.nonce, ah.nc, ah.cnonce,\r
+                           ah.qop, ah.response));\r
+       }\r
+\r
+       return (FALSE);\r
+}\r
+\r
+/*\r
+ * Return TRUE if request is authorised, FALSE otherwise.\r
+ */\r
+static bool_t\r
+check_authorization(struct mg_connection *conn, const char *path)\r
+{\r
+       FILE            *fp;\r
+       size_t          len, n;\r
+       char            protected_path[FILENAME_MAX];\r
+       const char      *p, *s;\r
+       const struct callback *cb;\r
+       bool_t          authorized;\r
+\r
+       fp = NULL;\r
+       authorized = TRUE;\r
+\r
+       mg_lock(conn->ctx);\r
+       s = conn->ctx->options[OPT_PROTECT];\r
+       FOR_EACH_WORD_IN_LIST(s, len) {\r
+\r
+               p = (const char *) memchr(s, '=', len);\r
+               if (p == NULL || p >= s + len || p == s)\r
+                       continue;\r
+\r
+               if (!memcmp(conn->request_info.uri, s, p - s)) {\r
+\r
+                       n = (size_t) (s + len - p);\r
+                       if (n > sizeof(protected_path) - 1)\r
+                               n = sizeof(protected_path) - 1;\r
+\r
+                       mg_strlcpy(protected_path, p + 1, n);\r
+\r
+                       if ((fp = fopen(protected_path, "r")) == NULL)\r
+                               cry("check_auth: cannot open %s: %s",\r
+                                   protected_path, strerror(errno));\r
+                       break;\r
+               }\r
+       }\r
+       mg_unlock(conn->ctx);\r
+\r
+       if (fp == NULL)\r
+               fp = open_auth_file(conn->ctx, path);\r
+\r
+       if (fp != NULL) {\r
+               authorized = authorize(conn, fp);\r
+               (void) fclose(fp);\r
+       }\r
+\r
+       if ((cb = find_callback(conn->ctx, TRUE,\r
+           conn->request_info.uri, -1)) != NULL) {\r
+               struct ah       ah;\r
+               char            buf[MAX_REQUEST_SIZE];\r
+               void            *user_data = cb->user_data;\r
+\r
+               authorized = FALSE;\r
+               if (parse_auth_header(conn, buf, sizeof(buf), &ah)) {\r
+                       cb->func(conn, &conn->request_info, &user_data);\r
+                       authorized = (bool_t) (long) user_data;\r
+               }\r
+       }\r
+\r
+       return (authorized);\r
+}\r
+\r
+static void\r
+send_authorization_request(struct mg_connection *conn)\r
+{\r
+       (void) mg_printf(conn,\r
+           "HTTP/1.1 401 Unauthorized\r\n"\r
+           "WWW-Authenticate: Digest qop=\"auth\", "\r
+           "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",\r
+           conn->ctx->options[OPT_AUTH_DOMAIN], (unsigned long) time(NULL));\r
+}\r
+\r
+static bool_t\r
+is_authorized_for_put(struct mg_connection *conn)\r
+{\r
+       FILE    *fp;\r
+       int     ret = FALSE;\r
+\r
+       if ((fp = fopen(conn->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {\r
+               set_close_on_exec(fileno(fp));\r
+               ret = authorize(conn, fp);\r
+               (void) fclose(fp);\r
+       }\r
+\r
+       return (ret);\r
+}\r
+#endif /* NO_AUTH */\r
+\r
+static bool_t\r
+does_client_want_keep_alive(const struct mg_connection *conn)\r
+{\r
+       const char *value = mg_get_header(conn, "Connection");\r
+\r
+       /* HTTP/1.1 assumes keep-alive, if Connection header is not set */\r
+       return ((value == NULL && conn->request_info.http_version_major == 1 &&\r
+           conn->request_info.http_version_minor == 1) || (value != NULL &&\r
+           !mg_strcasecmp(value, "keep-alive")));\r
+}\r
+\r
+struct de {\r
+       struct mg_connection    *conn;\r
+       char                    *file_name;\r
+       struct stat             st;\r
+};\r
+\r
+static void\r
+print_dir_entry(struct de *de)\r
+{\r
+       char            size[64], mod[64];\r
+\r
+       if (S_ISDIR(de->st.st_mode)) {\r
+               (void) mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");\r
+       } else {\r
+               if (de->st.st_size < 1024)\r
+                       (void) mg_snprintf(size, sizeof(size),\r
+                           "%lu", (unsigned long) de->st.st_size);\r
+               else if (de->st.st_size < 1024 * 1024)\r
+                       (void) mg_snprintf(size, sizeof(size),\r
+                           "%.1fk", (double) de->st.st_size / 1024);\r
+               else if (de->st.st_size < 1024 * 1024 * 1024)\r
+                       (void) mg_snprintf(size, sizeof(size),\r
+                           "%.1fM", (double) de->st.st_size / 1048576);\r
+               else\r
+                       (void) mg_snprintf(size, sizeof(size),\r
+                           "%.1fG", (double) de->st.st_size / 1073741824);\r
+       }\r
+       (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",\r
+               localtime(&de->st.st_mtime));\r
+       de->conn->num_bytes_sent += mg_printf(de->conn,\r
+           "<tr><td><a href=\"%s%s\">%s%s</a></td>"\r
+           "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",\r
+           de->conn->request_info.uri, de->file_name, de->file_name,\r
+           S_ISDIR(de->st.st_mode) ? "/" : "", mod, size);\r
+}\r
+\r
+static int\r
+compare_dir_entries(const void *p1, const void *p2)\r
+{\r
+       const struct de *a = (struct de *) p1, *b = (struct de *) p2;\r
+       const char      *query_string = a->conn->request_info.query_string;\r
+       int             cmp_result = 0;\r
+\r
+       if (query_string == NULL)\r
+               query_string = "na";\r
+\r
+       if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) {\r
+               return (-1);  /* Always put directories on top */\r
+       } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) {\r
+               return (1);   /* Always put directories on top */\r
+       } else if (*query_string == 'n') {\r
+               cmp_result = strcmp(a->file_name, b->file_name);\r
+       } else if (*query_string == 's') {\r
+               cmp_result = a->st.st_size == b->st.st_size ? 0 :\r
+                       a->st.st_size > b->st.st_size ? 1 : -1;\r
+       } else if (*query_string == 'd') {\r
+               cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :\r
+                       a->st.st_mtime > b->st.st_mtime ? 1 : -1;\r
+       }\r
+\r
+       return (query_string[1] == 'd' ? -cmp_result : cmp_result);\r
+}\r
+\r
+static void\r
+send_directory(struct mg_connection *conn, const char *dir)\r
+{\r
+       struct dirent   *dp;\r
+       DIR             *dirp;\r
+       struct de       *entries = NULL;\r
+       char            path[FILENAME_MAX], sort_direction;\r
+       int             i, num_entries = 0, arr_size = 128;\r
+\r
+       if ((dirp = opendir(dir)) == NULL) {\r
+               send_error(conn, 500, "Cannot open directory",\r
+                   "Error: opendir(%s): %s", path, strerror(ERRNO));\r
+               return;\r
+       }\r
+\r
+       (void) mg_printf(conn, "%s",\r
+           "HTTP/1.1 200 OK\r\n"\r
+           "Connection: close\r\n"\r
+           "Content-Type: text/html; charset=utf-8\r\n\r\n");\r
+       \r
+       sort_direction = conn->request_info.query_string != NULL &&\r
+           conn->request_info.query_string[1] == 'd' ? 'a' : 'd';\r
+\r
+       while ((dp = readdir(dirp)) != NULL) {\r
+\r
+               /* Do not show current dir and passwords file */\r
+               if (!strcmp(dp->d_name, ".") ||\r
+                   !strcmp(dp->d_name, "..") ||\r
+                   !strcmp(dp->d_name, PASSWORDS_FILE_NAME))\r
+                       continue;\r
+\r
+               if (entries == NULL || num_entries >= arr_size) {\r
+                       arr_size *= 2;\r
+                       entries = (struct de *) realloc(entries,\r
+                           arr_size * sizeof(entries[0]));\r
+               }\r
+               \r
+               if (entries == NULL) {\r
+                       send_error(conn, 500, "Cannot open directory",\r
+                           "%s", "Error: cannot allocate memory");\r
+                       return;\r
+               }\r
+\r
+               (void) mg_snprintf(path, sizeof(path), "%s%c%s",\r
+                   dir, DIRSEP, dp->d_name);\r
+               \r
+               (void) stat(path, &entries[num_entries].st);\r
+               entries[num_entries].conn = conn;\r
+               entries[num_entries].file_name = mg_strdup(dp->d_name);\r
+               num_entries++;\r
+       }\r
+       (void) closedir(dirp);\r
+\r
+       conn->num_bytes_sent += mg_printf(conn,\r
+           "<html><head><title>Index of %s</title>"\r
+           "<style>th {text-align: left;}</style></head>"\r
+           "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"\r
+           "<tr><th><a href=\"?n%c\">Name</a></th>"\r
+           "<th><a href=\"?d%c\">Modified</a></th>"\r
+           "<th><a href=\"?s%c\">Size</a></th></tr>"\r
+           "<tr><td colspan=\"3\"><hr></td></tr>",\r
+           conn->request_info.uri, conn->request_info.uri,\r
+           sort_direction, sort_direction, sort_direction);\r
+       \r
+       /* Print first entry - link to a parent directory */\r
+       conn->num_bytes_sent += mg_printf(conn,\r
+           "<tr><td><a href=\"%s%s\">%s</a></td>"\r
+           "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",\r
+           conn->request_info.uri, "..", "Parent directory", "-", "-");        \r
+\r
+       /* Sort and print directory entries */\r
+       qsort(entries, num_entries, sizeof(entries[0]), compare_dir_entries);\r
+       for (i = 0; i < num_entries; i++) {\r
+               print_dir_entry(&entries[i]);\r
+               free(entries[i].file_name);\r
+       }\r
+       free(entries);\r
+\r
+       conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");\r
+       conn->request_info.status_code = 200;\r
+}\r
+\r
+/*\r
+ * Send len bytes from the opened file to the client.\r
+ */\r
+static void\r
+send_opened_file_stream(struct mg_connection *conn, int fd, uint64_t len)\r
+{\r
+       char    buf[BUFSIZ];\r
+       int     n;\r
+\r
+       while (len > 0) {\r
+               n = sizeof(buf);\r
+               if ((uint64_t) n > len)\r
+                       n = (int) len;\r
+               if ((n = read(fd, buf, n)) <= 0)\r
+                       break;\r
+               conn->num_bytes_sent += mg_write(conn, buf, n);\r
+               len -= n;\r
+       }\r
+}\r
+\r
+static void\r
+send_file(struct mg_connection *conn, const char *path, struct stat *stp)\r
+{\r
+       char            date[64], lm[64], etag[64], range[64];\r
+       const char      *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";\r
+       const char      *mime_type, *s;\r
+       time_t          curtime = time(NULL);\r
+       unsigned long long cl, r1, r2;\r
+       int             fd, n;\r
+\r
+       mime_type = get_mime_type(path);\r
+       cl = stp->st_size;\r
+       conn->request_info.status_code = 200;\r
+       range[0] = '\0';\r
+\r
+       if ((fd = mg_open(path, O_RDONLY | O_BINARY, 0644)) == -1) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "fopen(%s): %s", path, strerror(ERRNO));\r
+               return;\r
+       }\r
+       set_close_on_exec(fd);\r
+\r
+       /* If Range: header specified, act accordingly */\r
+       s = mg_get_header(conn, "Range");\r
+       r1 = r2 = 0;\r
+       if (s != NULL && (n = sscanf(s,"bytes=%llu-%llu", &r1, &r2)) > 0) {\r
+               conn->request_info.status_code = 206;\r
+               (void) lseek(fd, (long) r1, SEEK_SET);\r
+               cl = n == 2 ? r2 - r1 + 1: cl - r1;\r
+               (void) mg_snprintf(range, sizeof(range),\r
+                   "Content-Range: bytes %llu-%llu/%llu\r\n",\r
+                   r1, r1 + cl - 1, cl);\r
+               msg = "Partial Content";\r
+       }\r
+\r
+       /* Prepare Etag, Date, Last-Modified headers */\r
+       (void) strftime(date, sizeof(date), fmt, localtime(&curtime));\r
+       (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));\r
+       (void) mg_snprintf(etag, sizeof(etag), "%lx.%lx",\r
+           (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);\r
+\r
+       /* Since we send Content-Length, we can keep the connection alive */\r
+       conn->keep_alive = does_client_want_keep_alive(conn);\r
+\r
+       (void) mg_printf(conn,\r
+           "HTTP/1.1 %d %s\r\n"\r
+           "Date: %s\r\n"\r
+           "Last-Modified: %s\r\n"\r
+           "Etag: \"%s\"\r\n"\r
+           "Content-Type: %s\r\n"\r
+           "Content-Length: %llu\r\n"\r
+           "Connection: %s\r\n"\r
+           "Accept-Ranges: bytes\r\n"\r
+           "%s\r\n",\r
+           conn->request_info.status_code, msg, date, lm, etag, mime_type, cl,\r
+           conn->keep_alive ? "keep-alive" : "close", range);\r
+\r
+       if (strcmp(conn->request_info.request_method, "HEAD") != 0)\r
+               send_opened_file_stream(conn, fd, cl);\r
+       (void) close(fd);\r
+}\r
+\r
+static void\r
+parse_http_headers(char **buf, struct mg_request_info *ri)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0; i < MAX_HTTP_HEADERS; i++) {\r
+               ri->http_headers[i].name = skip(buf, ": ");\r
+               ri->http_headers[i].value = skip(buf, "\r\n");\r
+               if (ri->http_headers[i].name[0] == '\0')\r
+                       break;\r
+               ri->num_headers = i + 1;\r
+       }\r
+}\r
+\r
+static bool_t\r
+is_known_http_method(const char *method)\r
+{\r
+       return (!strcmp(method, "GET") ||\r
+           !strcmp(method, "POST") ||\r
+           !strcmp(method, "HEAD") ||\r
+           !strcmp(method, "PUT") ||\r
+           !strcmp(method, "DELETE"));\r
+}\r
+\r
+static bool_t\r
+parse_http_request(char *buf, struct mg_request_info *ri, const struct usa *usa)\r
+{\r
+       char    *http_version;\r
+       int     n, success_code = FALSE;\r
+\r
+       ri->request_method = skip(&buf, " ");\r
+       ri->uri = skip(&buf, " ");\r
+       http_version = skip(&buf, "\r\n");\r
+\r
+       if (is_known_http_method(ri->request_method) &&\r
+           ri->uri[0] == '/' &&\r
+           sscanf(http_version, "HTTP/%d.%d%n",\r
+           &ri->http_version_major, &ri->http_version_minor, &n) == 2 &&\r
+           http_version[n] == '\0') {\r
+               parse_http_headers(&buf, ri);\r
+               ri->remote_port = ntohs(usa->u.sin.sin_port);\r
+               (void) memcpy(&ri->remote_ip, &usa->u.sin.sin_addr.s_addr, 4);\r
+               ri->remote_ip = ntohl(ri->remote_ip);\r
+               success_code = TRUE;\r
+       }\r
+\r
+       return (success_code);\r
+}\r
+\r
+static int\r
+read_request(int fd, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread)\r
+{\r
+       int     n, request_len;\r
+\r
+       request_len = 0;\r
+       while (*nread < bufsiz && request_len == 0) {\r
+               n = pull(fd, sock, ssl, buf + *nread, bufsiz - *nread);\r
+               if (n <= 0) {\r
+                       break;\r
+               } else {\r
+                       *nread += n;\r
+                       request_len = get_request_len(buf, (size_t) *nread);\r
+               }\r
+       }\r
+\r
+       return (request_len);\r
+}\r
+\r
+/*\r
+ * For given directory path, substitute it to valid index file.\r
+ * Return 0 if index file has been found, -1 if not found\r
+ */\r
+static bool_t\r
+substitute_index_file(struct mg_connection *conn,\r
+               char *path, size_t path_len, struct stat *stp)\r
+{\r
+       const char      *s;\r
+       struct stat     st;\r
+       size_t          len, n;\r
+       bool_t          found;\r
+\r
+       n = strlen(path);\r
+       path[n] = DIRSEP;\r
+       found = FALSE;\r
+\r
+       mg_lock(conn->ctx);\r
+       s = conn->ctx->options[OPT_INDEX_FILES];\r
+       FOR_EACH_WORD_IN_LIST(s, len) {\r
+               if (len > path_len - n - 1)\r
+                       continue;\r
+               (void) mg_strlcpy(path + n + 1, s, len + 1);\r
+               if (stat(path, &st) == 0) {\r
+                       *stp = st;\r
+                       found = TRUE;\r
+                       break;\r
+               }\r
+       }\r
+       mg_unlock(conn->ctx);\r
+\r
+       if (found == FALSE)\r
+               path[n] = '\0';\r
+\r
+       return (found);\r
+}\r
+\r
+static void\r
+mg_bind(struct mg_context *ctx, const char *uri_regex, int status_code,\r
+               mg_callback_t func, bool_t is_auth, void *user_data)\r
+{\r
+       struct callback *cb;\r
+\r
+       if (ctx->num_callbacks >= (int) ARRAY_SIZE(ctx->callbacks) - 1) {\r
+               cry("Too many callbacks! Increase MAX_CALLBACKS.");\r
+       } else {\r
+               cb = &ctx->callbacks[ctx->num_callbacks];\r
+               cb->uri_regex = uri_regex ? mg_strdup(uri_regex) : NULL;\r
+               cb->func = func;\r
+               cb->is_auth = is_auth;\r
+               cb->status_code = status_code;\r
+               cb->user_data = user_data;\r
+               ctx->num_callbacks++;\r
+       }\r
+}\r
+\r
+void\r
+mg_bind_to_uri(struct mg_context *ctx, const char *uri_regex,\r
+               mg_callback_t func, void *user_data)\r
+{\r
+       assert(func != NULL);\r
+       assert(uri_regex != NULL);\r
+       mg_bind(ctx, uri_regex, -1, func, FALSE, user_data);\r
+}\r
+\r
+void\r
+mg_bind_to_error_code(struct mg_context *ctx, int error_code,\r
+               mg_callback_t func, void *user_data)\r
+{\r
+       assert(error_code >= 0 && error_code < 1000);\r
+       assert(func != NULL);\r
+       mg_bind(ctx, NULL, error_code, func, FALSE, user_data);\r
+}\r
+\r
+void\r
+mg_protect_uri(struct mg_context *ctx, const char *uri_regex,\r
+               mg_callback_t func, void *user_data)\r
+{\r
+       assert(func != NULL);\r
+       assert(uri_regex != NULL);\r
+       mg_bind(ctx, uri_regex, -1, func, TRUE, user_data);\r
+}\r
+\r
+static int\r
+not_modified(const struct mg_connection *conn, const struct stat *stp)\r
+{\r
+       const char *ims = mg_get_header(conn, "If-Modified-Since");\r
+       return (ims != NULL && stp->st_mtime < date_to_epoch(ims));\r
+}\r
+\r
+static bool_t\r
+append_chunk(struct mg_request_info *ri, int fd, const char *buf, int len)\r
+{\r
+       bool_t  ret_code = TRUE;\r
+\r
+       if (fd == -1) {\r
+               /* TODO: check for NULL here */\r
+               ri->post_data = (char *) realloc(ri->post_data,\r
+                   ri->post_data_len + len);\r
+               (void) memcpy(ri->post_data + ri->post_data_len, buf, len);\r
+               ri->post_data_len += len;\r
+       } else if (push(fd, INVALID_SOCKET,\r
+           NULL, buf, (uint64_t) len) != (uint64_t) len) {\r
+               ret_code = FALSE;\r
+       }\r
+\r
+       return (ret_code);\r
+}\r
+\r
+static bool_t\r
+handle_request_body(struct mg_connection *conn, int fd)\r
+{\r
+       struct mg_request_info  *ri = &conn->request_info;\r
+       const char      *expect, *tmp;\r
+       uint64_t        content_len;\r
+       char            buf[BUFSIZ];\r
+       int             to_read, nread, already_read;\r
+       bool_t          success_code = FALSE;\r
+\r
+       content_len = get_content_length(conn);\r
+       expect = mg_get_header(conn, "Expect");\r
+\r
+       if (content_len == UNKNOWN_CONTENT_LENGTH) {\r
+               send_error(conn, 411, "Length Required", "");\r
+       } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {\r
+               send_error(conn, 417, "Expectation Failed", "");\r
+       } else {\r
+               if (expect != NULL)\r
+                       (void) mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n");\r
+\r
+               already_read = ri->post_data_len;\r
+               assert(already_read >= 0);\r
+\r
+               if (content_len <= (uint64_t) already_read) {\r
+                       ri->post_data_len = (int) content_len;\r
+                       /*\r
+                        * If fd == -1, this is embedded mode, and we do not\r
+                        * have to do anything: POST data is already there,\r
+                        * no need to allocate a buffer and copy it in.\r
+                        * If fd != -1, we need to write the data.\r
+                        */\r
+                       success_code = (fd == -1) || (push(fd, INVALID_SOCKET,\r
+                           NULL, ri->post_data, content_len) == content_len) ?\r
+                           TRUE : FALSE;\r
+               } else {\r
+\r
+                       if (fd == -1) {\r
+                               conn->free_post_data = TRUE;\r
+                               tmp = ri->post_data;\r
+                               /* +1 in case if already_read == 0 */\r
+                               ri->post_data = (char*)malloc(already_read + 1);\r
+                               (void) memcpy(ri->post_data, tmp, already_read);\r
+                       } else {\r
+                               (void) push(fd, INVALID_SOCKET, NULL,\r
+                                   ri->post_data, (uint64_t) already_read);\r
+                       }\r
+\r
+                       content_len -= already_read;\r
+\r
+                       while (content_len > 0) {\r
+                               to_read = sizeof(buf);\r
+                               if ((uint64_t) to_read > content_len)\r
+                                       to_read = (int) content_len;\r
+                               nread = pull(-1, conn->sock,\r
+                                   conn->ssl, buf, to_read);\r
+                               if (nread <= 0)\r
+                                       break;\r
+                               if (!append_chunk(ri, fd, buf, nread))\r
+                                       break;\r
+                               content_len -= nread;\r
+                       }\r
+                       success_code = content_len == 0 ? TRUE : FALSE;\r
+               }\r
+\r
+               /* Each error code path in this function must send an error */\r
+               if (success_code != TRUE)\r
+                       send_error(conn, 577, http_500_error,\r
+                          "%s", "Error handling body data");\r
+       }\r
+\r
+       return (success_code);\r
+}\r
+\r
+#if !defined(NO_CGI)\r
+struct cgi_env_block {\r
+       char    buf[CGI_ENVIRONMENT_SIZE];      /* Environment buffer   */\r
+       int     len;                            /* Space taken          */\r
+       char    *vars[MAX_CGI_ENVIR_VARS];      /* char **envp          */\r
+       int     nvars;                          /* Number of variables  */\r
+};\r
+\r
+static char *\r
+addenv(struct cgi_env_block *block, const char *fmt, ...)\r
+{\r
+       int     n, space;\r
+       char    *added;\r
+       va_list ap;\r
+\r
+       space = sizeof(block->buf) - block->len - 2;\r
+       assert(space >= 0);\r
+       added = block->buf + block->len;\r
+\r
+       va_start(ap, fmt);\r
+       n = mg_vsnprintf(added, (size_t) space, fmt, ap);\r
+       va_end(ap);\r
+\r
+       if (n > 0 && n < space &&\r
+           block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {\r
+               block->vars[block->nvars++] = block->buf + block->len;\r
+               block->len += n + 1;    /* Include \0 terminator */\r
+       }\r
+\r
+       return (added);\r
+}\r
+\r
+static void\r
+prepare_cgi_environment(struct mg_connection *conn, const char *prog,\r
+               struct cgi_env_block *blk)\r
+{\r
+       const char      *s, *script_filename, *root;\r
+       char            *p;\r
+       int             i;\r
+\r
+       blk->len = blk->nvars = 0;\r
+\r
+       /* SCRIPT_FILENAME */\r
+       script_filename = prog;\r
+       if ((s = strrchr(prog, '/')) != NULL)\r
+               script_filename = s + 1;\r
+\r
+       mg_lock(conn->ctx);\r
+       root = conn->ctx->options[OPT_ROOT];\r
+       addenv(blk, "SERVER_NAME=%s", conn->ctx->options[OPT_AUTH_DOMAIN]);\r
+       mg_unlock(conn->ctx);\r
+\r
+       /* Prepare the environment block */\r
+       addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");\r
+       addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");\r
+       addenv(blk, "%s", "REDIRECT_STATUS=200");       /* PHP */\r
+       addenv(blk, "SERVER_PORT=%d", ntohs(conn->lsa.u.sin.sin_port));\r
+       addenv(blk, "SERVER_ROOT=%s", root);\r
+       addenv(blk, "DOCUMENT_ROOT=%s", root);\r
+       addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);\r
+       addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(conn->rsa.u.sin.sin_addr));\r
+       addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);\r
+       addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);\r
+       addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));\r
+       addenv(blk, "SCRIPT_FILENAME=%s", script_filename);     /* PHP */\r
+       addenv(blk, "PATH_TRANSLATED=%s", prog);\r
+\r
+       if ((s = mg_get_header(conn, "Content-Type")) != NULL)\r
+               addenv(blk, "CONTENT_TYPE=%s", s);\r
+\r
+       if (conn->request_info.query_string != NULL)\r
+               addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);\r
+\r
+       if ((s = mg_get_header(conn, "Content-Length")) != NULL)\r
+               addenv(blk, "CONTENT_LENGTH=%s", s);\r
+\r
+       if ((s = getenv("PATH")) != NULL)\r
+               addenv(blk, "PATH=%s", s);\r
+\r
+#if defined(_WIN32)\r
+       if ((s = getenv("COMSPEC")) != NULL)\r
+               addenv(blk, "COMSPEC=%s", s);\r
+       if ((s = getenv("SYSTEMROOT")) != NULL)\r
+               addenv(blk, "SYSTEMROOT=%s", s);\r
+#else\r
+       if ((s = getenv("LD_LIBRARY_PATH")) != NULL)\r
+               addenv(blk, "LD_LIBRARY_PATH=%s", s);\r
+#endif /* _WIN32 */\r
+\r
+       if ((s = getenv("PERLLIB")) != NULL)\r
+               addenv(blk, "PERLLIB=%s", s);\r
+\r
+       if (conn->request_info.remote_user != NULL) {\r
+               addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);\r
+               addenv(blk, "%s", "AUTH_TYPE=Digest");\r
+       }\r
+\r
+       /* Add all headers as HTTP_* variables */\r
+       for (i = 0; i < conn->request_info.num_headers; i++) {\r
+               p = addenv(blk, "HTTP_%s=%s",\r
+                   conn->request_info.http_headers[i].name,\r
+                   conn->request_info.http_headers[i].value);\r
+\r
+               /* Convert variable name into uppercase, and change - to _ */\r
+               for (; *p != '=' && *p != '\0'; p++) {\r
+                       if (*p == '-')\r
+                               *p = '_';\r
+                       *p = toupper(* (unsigned char *) p) & 0xff;\r
+               }\r
+       }\r
+\r
+       blk->vars[blk->nvars++] = NULL;\r
+       blk->buf[blk->len++] = '\0';\r
+\r
+       assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));\r
+       assert(blk->len > 0);\r
+       assert(blk->len < (int) sizeof(blk->buf));\r
+}\r
+\r
+static void\r
+send_cgi(struct mg_connection *conn, const char *prog)\r
+{\r
+       int                     headers_len, data_len, i, n;\r
+       const char              *status;\r
+       char                    buf[MAX_REQUEST_SIZE], *pbuf;\r
+       struct mg_request_info  ri;\r
+       struct cgi_env_block    blk;\r
+       char                    dir[FILENAME_MAX], *p;\r
+       int                     fd_stdin[2], fd_stdout[2];\r
+\r
+       prepare_cgi_environment(conn, prog, &blk);\r
+\r
+       /* CGI must be executed in its own directory */\r
+       (void) mg_snprintf(dir, sizeof(dir), "%s", prog);\r
+       if ((p = strrchr(dir, DIRSEP)) != NULL)\r
+               *p++ = '\0';\r
+\r
+       fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;\r
+       if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "Cannot create CGI pipe: %s", strerror(ERRNO));\r
+               goto done;\r
+       }\r
+\r
+       if (!spawn_process(conn, p, blk.buf, blk.vars,\r
+           fd_stdin[0], fd_stdout[1], dir)) {\r
+               goto done;\r
+       }\r
+\r
+       /*\r
+        * spawn_process() must close those!\r
+        * If we don't mark them as closed, close() attempt before\r
+        * return from this function throws an exception on Windows.\r
+        * Windows does not like when closed descriptor is closed again.\r
+        */\r
+       fd_stdin[0] = fd_stdout[1] = -1;\r
+\r
+       /* Send POST data to the CGI process if needed */\r
+       if (!strcmp(conn->request_info.request_method, "POST") &&\r
+           !handle_request_body(conn, fd_stdin[1])) {\r
+               goto done;\r
+       }\r
+\r
+       /*\r
+        * Now read CGI reply into a buffer. We need to set correct\r
+        * status code, thus we need to see all HTTP headers first.\r
+        * Do not send anything back to client, until we buffer in all\r
+        * HTTP headers.\r
+        */\r
+       data_len = 0;\r
+       headers_len = read_request(fd_stdout[0], INVALID_SOCKET, NULL,\r
+           buf, sizeof(buf), &data_len);\r
+       if (headers_len <= 0) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "CGI program sent malformed HTTP headers: [%.*s]",\r
+                   data_len, buf);\r
+               goto done;\r
+       }\r
+       pbuf = buf;\r
+       buf[headers_len - 1] = '\0';\r
+       parse_http_headers(&pbuf, &ri);\r
+\r
+       /* Make up and send the status line */\r
+       status = get_header(&ri, "Status");\r
+       conn->request_info.status_code = status == NULL ? 200 : atoi(status);\r
+       (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n",\r
+           conn->request_info.status_code);\r
+\r
+       /* Send headers */\r
+       for (i = 0; i < ri.num_headers; i++)\r
+               (void) mg_printf(conn, "%s: %s\r\n",\r
+                   ri.http_headers[i].name,\r
+                   ri.http_headers[i].value);\r
+       (void) mg_write(conn, "\r\n", 2);\r
+\r
+       /* Send headers, and the rest of the data */\r
+       conn->num_bytes_sent += mg_write(conn,\r
+           buf + headers_len, data_len - headers_len);\r
+       while ((n = pull(fd_stdout[0],\r
+           INVALID_SOCKET, NULL, buf, sizeof(buf))) > 0)\r
+               conn->num_bytes_sent += mg_write(conn, buf, n);\r
+\r
+done:\r
+       if (fd_stdin[0] != -1) (void) close(fd_stdin[0]);\r
+       if (fd_stdin[1] != -1) (void) close(fd_stdin[1]);\r
+       if (fd_stdout[0] != -1) (void) close(fd_stdout[0]);\r
+       if (fd_stdout[1] != -1) (void) close(fd_stdout[1]);\r
+}\r
+#endif /* !NO_CGI */\r
+\r
+#if !defined(NO_AUTH)\r
+/*\r
+ * For a given PUT path, create all intermediate subdirectories\r
+ * for given path. Return 0 if the path itself is a directory,\r
+ * or -1 on error, 1 if OK.\r
+ */\r
+static int\r
+put_dir(const char *path)\r
+{\r
+       char            buf[FILENAME_MAX];\r
+       const char      *s, *p;\r
+       struct stat     st;\r
+       size_t          len;\r
+\r
+       for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {\r
+               len = p - path;\r
+               assert(len < sizeof(buf));\r
+               (void) memcpy(buf, path, len);\r
+               buf[len] = '\0';\r
+\r
+               /* Try to create intermediate directory */\r
+               if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0)\r
+                       return (-1);\r
+\r
+               /* Is path itself a directory ? */\r
+               if (p[1] == '\0')\r
+                       return (0);\r
+       }\r
+\r
+       return (1);\r
+}\r
+\r
+static void\r
+put_file(struct mg_connection *conn, const char *path)\r
+{\r
+       struct stat     st;\r
+       int             rc, fd;\r
+\r
+       conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;\r
+\r
+       if (mg_get_header(conn, "Range")) {\r
+               send_error(conn, 501, "Not Implemented",\r
+                   "%s", "Range support for PUT requests is not implemented");\r
+       } else if ((rc = put_dir(path)) == 0) {\r
+               send_error(conn, 200, "OK", "");\r
+       } else if (rc == -1) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "put_dir(%s): %s", path, strerror(ERRNO));\r
+       } else if ((fd = mg_open(path,\r
+           O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644)) == -1) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "open(%s): %s", path, strerror(ERRNO));\r
+       } else {\r
+               set_close_on_exec(fd);\r
+               if (handle_request_body(conn, fd))\r
+                       send_error(conn, conn->request_info.status_code,\r
+                           "OK", "");\r
+               (void) close(fd);\r
+       }\r
+}\r
+#endif /* NO_AUTH */\r
+\r
+#if !defined(NO_SSI)\r
+static void\r
+do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag)\r
+{\r
+       char    file_name[BUFSIZ], path[FILENAME_MAX], *p;\r
+       FILE    *fp;\r
+\r
+       /*\r
+        * sscanf() is safe here, since send_ssi_file() also uses buffer\r
+        * of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.\r
+        */\r
+       if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {\r
+               /* File name is relative to the webserver root */\r
+               mg_lock(conn->ctx);\r
+               (void) mg_snprintf(path, sizeof(path), "%s%c%s",\r
+                   conn->ctx->options[OPT_ROOT], DIRSEP, file_name);\r
+               mg_unlock(conn->ctx);\r
+       } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {\r
+               /*\r
+                * File name is relative to the webserver working directory\r
+                * or it is absolute system path\r
+                */\r
+               (void) mg_snprintf(path, sizeof(path), "%s", file_name);\r
+       } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {\r
+               /* File name is relative to the currect document */\r
+               (void) mg_snprintf(path, sizeof(path), "%s", ssi);\r
+               if ((p = strrchr(path, DIRSEP)) != NULL)\r
+                       p[1] = '\0';\r
+               (void) mg_snprintf(path + strlen(path),\r
+                   sizeof(path) - strlen(path), "%s", file_name);\r
+       } else {\r
+               cry("Bad SSI #include: [%s]", tag);\r
+               return;\r
+       }\r
+\r
+       if ((fp = fopen(path, "rb")) == NULL) {\r
+               cry("Cannot open SSI #include: [%s]: fopen(%s): %s",\r
+                   tag, path, strerror(ERRNO));\r
+       } else {\r
+               set_close_on_exec(fileno(fp));\r
+               send_opened_file_stream(conn, fileno(fp), ~0ULL);\r
+               (void) fclose(fp);\r
+       }\r
+}\r
+\r
+static void\r
+do_ssi_exec(struct mg_connection *conn, char *tag)\r
+{\r
+       char    cmd[BUFSIZ];\r
+       FILE    *fp;\r
+\r
+       if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {\r
+               cry("Bad SSI #exec: [%s]", tag);\r
+       } else if ((fp = popen(cmd, "r")) == NULL) {\r
+               cry("Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));\r
+       } else {\r
+               send_opened_file_stream(conn, fileno(fp), ~0ULL);\r
+               (void) pclose(fp);\r
+       }\r
+}\r
+\r
+static void\r
+send_ssi_file(struct mg_connection *conn, const char *path, FILE *fp)\r
+{\r
+       char    buf[BUFSIZ];\r
+       int     ch, len, in_ssi_tag;\r
+\r
+       in_ssi_tag = FALSE;\r
+       len = 0;\r
+\r
+       while ((ch = fgetc(fp)) != EOF) {\r
+               if (in_ssi_tag && ch == '>') {\r
+                       in_ssi_tag = FALSE;\r
+                       buf[len++] = ch & 0xff;\r
+                       buf[len] = '\0';\r
+                       assert(len <= (int) sizeof(buf));\r
+                       if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {\r
+                               /* Not an SSI tag, pass it */\r
+                               (void) mg_write(conn, buf, len);\r
+                       } else {\r
+                               if (!memcmp(buf + 5, "include", 7)) {\r
+                                       do_ssi_include(conn, path, buf + 12);\r
+                               } else if (!memcmp(buf + 5, "exec", 4)) {\r
+                                       do_ssi_exec(conn, buf + 9);\r
+                               } else {\r
+                                       cry("%s: unknown SSI command: \"%s\"",\r
+                                               path, buf);\r
+                               }\r
+                       }\r
+                       len = 0;\r
+               } else if (in_ssi_tag) {\r
+                       if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {\r
+                               /* Not an SSI tag */\r
+                               in_ssi_tag = FALSE;\r
+                       } else if (len == (int) sizeof(buf) - 2) {\r
+                               cry("%s: SSI tag is too large", path);\r
+                               len = 0;\r
+                       }\r
+                       buf[len++] = ch & 0xff;\r
+               } else if (ch == '<') {\r
+                       in_ssi_tag = TRUE;\r
+                       if (len > 0)\r
+                               (void) mg_write(conn, buf, len);\r
+                       len = 0;\r
+                       buf[len++] = ch & 0xff;\r
+               } else {\r
+                       buf[len++] = ch & 0xff;\r
+                       if (len == (int) sizeof(buf)) {\r
+                               (void) mg_write(conn, buf, len);\r
+                               len = 0;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /* Send the rest of buffered data */\r
+       if (len > 0)\r
+               (void) mg_write(conn, buf, len);\r
+\r
+}\r
+\r
+static void\r
+send_ssi(struct mg_connection *conn, const char *path)\r
+{\r
+       FILE    *fp;\r
+\r
+       if ((fp = fopen(path, "rb")) == NULL) {\r
+               send_error(conn, 500, http_500_error,\r
+                   "fopen(%s): %s", path, strerror(ERRNO));\r
+       } else {\r
+               set_close_on_exec(fileno(fp));\r
+               (void) mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n"\r
+                   "Content-Type: text/html\r\nConnection: close\r\n\r\n");\r
+               send_ssi_file(conn, path, fp);\r
+               (void) fclose(fp);\r
+       }\r
+}\r
+#endif /* !NO_SSI */\r
+\r
+static void\r
+analyze_request(struct mg_connection *conn)\r
+{\r
+       struct mg_request_info *ri = &conn->request_info;\r
+       char                    path[FILENAME_MAX], *uri = ri->uri;\r
+       struct stat             st;\r
+       const struct callback   *cb;\r
+\r
+       if ((conn->request_info.query_string = strchr(uri, '?')) != NULL)\r
+               * conn->request_info.query_string++ = '\0';\r
+\r
+       (void) url_decode(uri, (int) strlen(uri), uri, strlen(uri) + 1, FALSE);\r
+       remove_double_dots(uri);\r
+       make_path(conn->ctx, uri, path, sizeof(path));\r
+\r
+#if !defined(NO_AUTH)\r
+       if (!check_authorization(conn, path)) {\r
+               send_authorization_request(conn);\r
+       } else\r
+#endif /* !NO_AUTH */\r
+       if ((cb = find_callback(conn->ctx, FALSE, uri, -1)) != NULL) {\r
+               if (strcmp(ri->request_method, "POST") ||\r
+                   (!strcmp(ri->request_method, "POST") &&\r
+                   handle_request_body(conn, -1)))\r
+                       cb->func(conn, &conn->request_info, cb->user_data);\r
+       } else\r
+#if !defined(NO_AUTH)\r
+       if (strstr(path, PASSWORDS_FILE_NAME)) {\r
+               /* Do not allow to view passwords files */\r
+               send_error(conn, 403, "Forbidden", "");\r
+       } else if ((!strcmp(ri->request_method, "PUT") ||\r
+           !strcmp(ri->request_method, "DELETE")) &&\r
+           (conn->ctx->options[OPT_AUTH_PUT] == NULL ||\r
+            !is_authorized_for_put(conn))) {\r
+               send_authorization_request(conn);\r
+       } else if (!strcmp(ri->request_method, "PUT")) {\r
+               put_file(conn, path);\r
+       } else if (!strcmp(ri->request_method, "DELETE")) {\r
+               if (mg_remove(path) == 0)\r
+                       send_error(conn, 200, "OK", "");\r
+               else\r
+                       send_error(conn, 500, http_500_error,\r
+                           "remove(%s): %s", path, strerror(ERRNO));\r
+       } else\r
+#endif /* NO_AUTH */\r
+       if (mg_stat(path, &st) != 0) {\r
+               send_error(conn, 404, "Not Found", "%s", "");\r
+       } else if (S_ISDIR(st.st_mode) && uri[strlen(uri) - 1] != '/') {\r
+               (void) mg_printf(conn,\r
+                   "HTTP/1.1 301 Moved Permanently\r\n"\r
+                   "Location: %s/\r\n\r\n", uri);\r
+       } else if (S_ISDIR(st.st_mode) &&\r
+           substitute_index_file(conn, path, sizeof(path), &st) == FALSE) {\r
+               if (is_true(conn->ctx->options[OPT_DIR_LIST])) {\r
+                       send_directory(conn, path);\r
+               } else {\r
+                       send_error(conn, 403, "Directory Listing Denied",\r
+                           "Directory listing denied");\r
+               }\r
+#if !defined(NO_CGI)\r
+       } else if (match_extension(path,\r
+           conn->ctx->options[OPT_CGI_EXTENSIONS])) {\r
+               if (strcmp(ri->request_method, "POST") &&\r
+                   strcmp(ri->request_method, "GET")) {\r
+                       send_error(conn, 501, "Not Implemented",\r
+                           "Method %s is not implemented", ri->request_method);\r
+               } else {\r
+                       send_cgi(conn, path);\r
+               }\r
+#endif /* NO_CGI */\r
+#if !defined(NO_SSI)\r
+       } else if (match_extension(path,\r
+           conn->ctx->options[OPT_SSI_EXTENSIONS])) {\r
+               send_ssi(conn, path);\r
+#endif /* NO_SSI */\r
+       } else if (not_modified(conn, &st)) {\r
+               send_error(conn, 304, "Not Modified", "");\r
+       } else {\r
+               send_file(conn, path, &st);\r
+       }\r
+}\r
+\r
+static void\r
+close_all_listening_sockets(struct mg_context *ctx)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0; i < ctx->num_listeners; i++)\r
+               (void) closesocket(ctx->listeners[i].sock);\r
+       ctx->num_listeners = 0;\r
+}\r
+\r
+static bool_t\r
+set_ports_option(struct mg_context *ctx, const char *p)\r
+{\r
+       SOCKET  sock;\r
+       size_t  len;\r
+       int     flags, port;\r
+\r
+       close_all_listening_sockets(ctx);\r
+\r
+       FOR_EACH_WORD_IN_LIST(p, len) {\r
+\r
+               flags   = p[len - 1] == 's' ? FLAG_SSL : 0;\r
+               port    = atoi(p);\r
+\r
+               if (ctx->num_listeners >=\r
+                   (int) (ARRAY_SIZE(ctx->listeners) - 1)) {\r
+                       cry("%s", "Too many listeninig sockets");\r
+                       return (FALSE);\r
+               } else if ((sock = mg_open_listening_port(port)) == -1) {\r
+                       cry("cannot open port %d", port);\r
+                       return (FALSE);\r
+               } else if (flags == FLAG_SSL && ctx->ssl_ctx == NULL) {\r
+                       (void) closesocket(sock);\r
+                       cry("cannot add SSL socket, "\r
+                           "please specify certificate file");\r
+                       return (FALSE);\r
+               } else {\r
+                       ctx->listeners[ctx->num_listeners].sock = sock;\r
+                       ctx->listeners[ctx->num_listeners].flags = flags;\r
+                       ctx->num_listeners++;\r
+               }\r
+       }\r
+\r
+       return (TRUE);\r
+}\r
+\r
+static void\r
+log_header(const struct mg_connection *conn, const char *header, FILE *fp)\r
+{\r
+       const char      *header_value;\r
+\r
+       if ((header_value = mg_get_header(conn, header)) == NULL) {\r
+               (void) fprintf(fp, "%s", " -");\r
+       } else {\r
+               (void) fprintf(fp, " \"%s\"", header_value);\r
+       }\r
+}\r
+\r
+static void\r
+log_access(const struct mg_connection *conn)\r
+{\r
+       const struct mg_request_info *ri;\r
+       char            date[64];\r
+\r
+       if (conn->ctx->access_log == NULL)\r
+               return;\r
+\r
+       (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",\r
+                       localtime(&conn->birth_time));\r
+\r
+       ri = &conn->request_info;\r
+       (void) fprintf(conn->ctx->access_log,\r
+           "%s - %s [%s %+05d] \"%s %s HTTP/%d.%d\" %d %llu",\r
+           inet_ntoa(conn->rsa.u.sin.sin_addr),\r
+           ri->remote_user == NULL ? "-" : ri->remote_user,\r
+           date, tz_offset,\r
+           ri->request_method ? ri->request_method : "-",\r
+           ri->uri ? ri->uri : "-",\r
+           ri->http_version_major, ri->http_version_minor,\r
+           conn->request_info.status_code,\r
+           (unsigned long long) conn->num_bytes_sent);\r
+       log_header(conn, "Referer", conn->ctx->access_log);\r
+       log_header(conn, "User-Agent", conn->ctx->access_log);\r
+       (void) fputc('\n', conn->ctx->access_log);\r
+       (void) fflush(conn->ctx->access_log);\r
+}\r
+\r
+static bool_t\r
+isbyte(int n) {\r
+       return (n >= 0 && n <= 255);\r
+}\r
+\r
+static bool_t\r
+check_acl(struct mg_context *ctx, const struct usa *usa)\r
+{\r
+       int             a, b, c, d, n, mask, allowed;\r
+       char            flag, *s;\r
+       size_t          len;\r
+       uint32_t        acl_subnet, acl_mask, remote_ip;\r
+\r
+       (void) memcpy(&remote_ip, &usa->u.sin.sin_addr, sizeof(remote_ip));\r
+\r
+       mg_lock(ctx);\r
+       s = ctx->options[OPT_ACL];\r
+\r
+       /* Allow by default, if no ACL is set */\r
+       if (s == NULL) {\r
+               mg_unlock(ctx);\r
+               return (TRUE);\r
+       }\r
+\r
+       /* If any ACL is set, deny by default */\r
+       allowed = '-';\r
+       FOR_EACH_WORD_IN_LIST(s, len) {\r
+\r
+               mask = 32;\r
+\r
+               if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {\r
+                       cry("[%s]: subnet must be [+|-]x.x.x.x[/x]", s);\r
+               } else if (flag != '+' && flag != '-') {\r
+                       cry("flag must be + or -: [%s]", s);\r
+               } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {\r
+                       cry("bad ip address: [%s]", s);\r
+               } else if (sscanf(s + n, "/%d", &mask) == 0) {\r
+                       /* Do nothing, no mask specified */\r
+               } else if (mask < 0 || mask > 32) {\r
+                       cry("bad subnet mask: %d [%s]", n, s);\r
+               }\r
+\r
+               acl_subnet = (a << 24) | (b << 16) | (c << 8) | d;\r
+               acl_mask = mask ? 0xffffffffU << (32 - mask) : 0;\r
+\r
+               if (acl_subnet == (ntohl(remote_ip) & acl_mask))\r
+                       allowed = flag;\r
+       }\r
+       mg_unlock(ctx);\r
+\r
+       return (allowed == '+');\r
+}\r
+\r
+static void\r
+add_to_set(SOCKET fd, fd_set *set, int *max_fd)\r
+{\r
+       FD_SET(fd, set);\r
+       if (fd > (SOCKET) *max_fd)\r
+               *max_fd = (int) fd;\r
+}\r
+\r
+/*\r
+ * Deallocate mongoose context, free up the resources\r
+ */\r
+static void\r
+mg_fini(struct mg_context *ctx)\r
+{\r
+       int     i;\r
+\r
+       close_all_listening_sockets(ctx);\r
+\r
+       for (i = 0; i < ctx->num_callbacks; i++)\r
+               if (ctx->callbacks[i].uri_regex != NULL)\r
+                       free(ctx->callbacks[i].uri_regex);\r
+\r
+       for (i = 0; i < NUM_OPTIONS; i++)\r
+               if (ctx->options[i] != NULL)\r
+                       free(ctx->options[i]);\r
+\r
+       if (ctx->access_log)\r
+               (void) fclose(ctx->access_log);\r
+       if (ctx->error_log)\r
+               (void) fclose(ctx->error_log);\r
+\r
+       /* TODO: free SSL context */\r
+       (void) pthread_mutex_destroy(&ctx->mutex);\r
+       destroy_socket_pool(&ctx->socket_pool);\r
+\r
+       free(ctx);\r
+}\r
+\r
+#if !defined(_WIN32)\r
+static bool_t\r
+set_uid_option(struct mg_context *ctx, const char *uid)\r
+{\r
+       struct passwd   *pw;\r
+       int             retval = FALSE;\r
+\r
+       ctx = NULL; /* Unused */\r
+\r
+       if ((pw = getpwnam(uid)) == NULL)\r
+               cry("%s: unknown user [%s]", __func__, uid);\r
+       else if (setgid(pw->pw_gid) == -1)\r
+               cry("%s: setgid(%s): %s", __func__, uid, strerror(errno));\r
+       else if (setuid(pw->pw_uid) == -1)\r
+               cry("%s: setuid(%s): %s", __func__, uid, strerror(errno));\r
+       else\r
+               retval = TRUE;\r
+\r
+       return (retval);\r
+}\r
+#endif /* !_WIN32 */\r
+\r
+#if !defined(NO_SSL)\r
+void\r
+mg_set_ssl_password_callback(struct mg_context *ctx, mg_spcb_t func)\r
+{\r
+       ctx->ssl_password_callback = func;\r
+}\r
+\r
+/*\r
+ * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.\r
+ */\r
+static bool_t\r
+set_ssl_option(struct mg_context *ctx, const char *pem)\r
+{\r
+       SSL_CTX         *CTX;\r
+       void            *lib;\r
+       union {void *p; void (*fp)(void);} u;\r
+       struct ssl_func *fp;\r
+       int             retval = FALSE;\r
+\r
+       /* Load SSL library dynamically */\r
+       if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {\r
+               cry("set_ssl_option: cannot load %s", SSL_LIB);\r
+               return (FALSE);\r
+       }\r
+\r
+       for (fp = ssl_sw; fp->name != NULL; fp++) {\r
+#ifdef _WIN32\r
+               /* GetProcAddress() returns pointer to function */\r
+               u.fp = (void (*)(void)) dlsym(lib, fp->name);\r
+#else\r
+               /*\r
+                * dlsym() on UNIX returns void *.\r
+                * ISO C forbids casts of data pointers to function\r
+                * pointers. We need to use a union to make a cast.\r
+                */\r
+               u.p = dlsym(lib, fp->name);\r
+#endif /* _WIN32 */\r
+               if (u.fp == NULL) {\r
+                       cry("set_ssl_option: cannot find %s", fp->name);\r
+                       return (FALSE);\r
+               } else {\r
+                       fp->ptr = u.fp;\r
+               }\r
+       }\r
+\r
+       /* Initialize SSL crap */\r
+       SSL_library_init();\r
+\r
+       if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)\r
+               cry("SSL_CTX_new error");\r
+       else if (ctx->ssl_password_callback != NULL)\r
+               SSL_CTX_set_default_passwd_cb(CTX, ctx->ssl_password_callback);\r
+\r
+       if (CTX != NULL && SSL_CTX_use_certificate_file(\r
+           CTX, pem, SSL_FILETYPE_PEM) == 0)\r
+               cry("cannot open %s", pem);\r
+       else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(\r
+           CTX, pem, SSL_FILETYPE_PEM) == 0)\r
+               cry("cannot open %s", pem);\r
+       else\r
+               retval = TRUE;\r
+\r
+       ctx->ssl_ctx = CTX;\r
+\r
+       return (retval);\r
+}\r
+#endif /* !NO_SSL */\r
+\r
+static bool_t\r
+open_log_file(FILE **fpp, const char *path)\r
+{\r
+       bool_t  retval = TRUE;\r
+\r
+       if (*fpp != NULL)\r
+               (void) fclose(*fpp);\r
+\r
+       if (path == NULL) {\r
+               *fpp = NULL;\r
+       } else if ((*fpp = fopen(path, "a")) == NULL) {\r
+               cry("cannot open log file %s: %s",\r
+                   path, strerror(errno));\r
+               retval = FALSE;\r
+       } else {\r
+               set_close_on_exec(fileno(*fpp));\r
+       }\r
+\r
+       return (retval);\r
+}\r
+\r
+static bool_t\r
+set_alog_option(struct mg_context *ctx, const char *path)\r
+{\r
+       return (open_log_file(&ctx->access_log, path));\r
+}\r
+\r
+static bool_t\r
+set_elog_option(struct mg_context *ctx, const char *path)\r
+{\r
+       return (open_log_file(&ctx->error_log, path));\r
+}\r
+\r
+#if !defined(NO_AUTH)\r
+static bool_t\r
+set_gpass_option(struct mg_context *ctx, const char *path)\r
+{\r
+       ctx = NULL;\r
+       return (access(path, R_OK) == 0);\r
+}\r
+#endif /* !NO_AUTH */\r
+\r
+static void\r
+admin_page(struct mg_connection *conn, const struct mg_request_info *ri,\r
+               void *user_data)\r
+{\r
+       const struct mg_option  *list;\r
+       const char              *option_name, *option_value;\r
+       int                     i;\r
+\r
+       user_data = NULL; /* Unused */\r
+\r
+       (void) mg_printf(conn,\r
+           "HTTP/1.1 200 OK\r\n"\r
+           "Content-Type: text/html\r\n\r\n"\r
+           "<html><body><h1>Mongoose v. %s</h1>", mg_version());\r
+\r
+       if (!strcmp(ri->request_method, "POST")) {\r
+               option_name = mg_get_var(conn, "o");\r
+               option_value = mg_get_var(conn, "v");\r
+               if (mg_set_option(conn->ctx,\r
+                   option_name, option_value) == -1) {\r
+                       (void) mg_printf(conn,\r
+                           "<p style=\"background: red\">Error setting "\r
+                           "option \"%s\"</p>",\r
+                           option_name ? option_name : "(null)");\r
+               } else {\r
+                       (void) mg_printf(conn,\r
+                           "<p style=\"color: green\">Saved: %s=%s</p>",\r
+                           option_name, option_value ? option_value : "NULL");\r
+               }\r
+       }\r
+\r
+       /* Print table with all options */\r
+       list = mg_get_option_list();\r
+       (void) mg_printf(conn, "%s", "<table border=\"1\""\r
+           "<tr><th>Option</th><th>Description</th>"\r
+           "<th colspan=2>Value</th></tr>");\r
+\r
+       for (i = 0; list[i].name != NULL; i++) {\r
+               option_value = mg_get_option(conn->ctx, list[i].name);\r
+               if (option_value == NULL)\r
+                       option_value = "";\r
+               (void) mg_printf(conn,\r
+                   "<form method=post><tr><td>%s</td><td>%s</td>"\r
+                   "<input type=hidden name=o value='%s'>"\r
+                   "<td><input type=text name=v value='%s'></td>"\r
+                   "<td><input type=submit value=save></td></form></tr>",\r
+                   list[i].name, list[i].description, list[i].name,\r
+                   option_value);\r
+       }\r
+\r
+       (void) mg_printf(conn, "%s", "</table></body></html>");\r
+}\r
+\r
+static void\r
+terminate_one_thread(struct mg_context *ctx)\r
+{\r
+       struct socket fake;\r
+\r
+       fake.flags = FLAG_TERMINATE;\r
+       put_socket(&ctx->socket_pool, &fake);\r
+}\r
+\r
+static void worker_loop(struct mg_context *ctx);\r
+static bool_t\r
+set_threads_option(struct mg_context *ctx, const char *str)\r
+{\r
+       int     i, old_count, new_count;\r
+\r
+       new_count = atoi(str);\r
+       old_count = atoi(ctx->options[OPT_THREADS]);\r
+\r
+       if (new_count > old_count) {\r
+               for (i = 0; i < new_count - old_count; i++)\r
+                       start_thread((mg_thread_func_t) worker_loop, ctx);\r
+       } else {\r
+               for (i = 0; i < old_count - new_count; i++)\r
+                       terminate_one_thread(ctx);\r
+       }\r
+\r
+       return (TRUE);\r
+}\r
+\r
+static bool_t\r
+set_admin_uri_option(struct mg_context *ctx, const char *uri)\r
+{\r
+       mg_bind_to_uri(ctx, uri, &admin_page, NULL);\r
+       return (TRUE);\r
+}\r
+\r
+static const struct mg_option known_options[] = {\r
+       {"root", "\tWeb root directory", NULL},\r
+       {"index_files", "Index files", "index.html,index.htm,index.cgi"},\r
+#if !defined(NO_SSL)\r
+       {"ssl_cert", "SSL certificate file", NULL},\r
+#endif /* !NO_SSL */\r
+       {"ports", "Listening ports", NULL},\r
+       {"dir_list", "Directory listing", "yes"},\r
+       {"protect", "URI to htpasswd mapping", NULL},\r
+#if !defined(NO_CGI)\r
+       {"cgi_ext", "CGI extensions", "cgi,pl,php"},\r
+       {"cgi_interp", "CGI interpreter to use with all CGI scripts", NULL},\r
+#endif /* NO_CGI */\r
+       {"ssi_ext", "SSI extensions", "shtml,shtm"},\r
+#if !defined(NO_AUTH)\r
+       {"auth_realm", "Authentication domain name", "mydomain.com"},\r
+       {"auth_gpass", "Global passwords file", NULL},\r
+       {"auth_PUT", "PUT,DELETE auth file", NULL},\r
+#endif /* !NO_AUTH */\r
+#if !defined(_WIN32)\r
+       {"uid", "\tRun as user", NULL},\r
+#endif /* !_WIN32 */\r
+       {"access_log", "Access log file", NULL},\r
+       {"error_log", "Error log file", NULL},\r
+       {"aliases", "Path=URI mappings", NULL},\r
+       {"admin_uri", "Administration page URI", NULL},\r
+       {"acl", "\tAllow/deny IP addresses/subnets", NULL},\r
+       {"threads", "Thread pool size", "23"},\r
+       {NULL, NULL, NULL}\r
+};\r
+\r
+static const struct option_setter {\r
+       int     context_index;\r
+       bool_t (*setter)(struct mg_context *, const char *);\r
+} setters[] = {\r
+       {OPT_ROOT,              NULL},\r
+       {OPT_INDEX_FILES,       NULL},\r
+#if !defined(NO_SSL)\r
+       {OPT_SSL_CERTIFICATE,   &set_ssl_option},\r
+#endif /* !NO_SSL */\r
+       {OPT_PORTS,             &set_ports_option},\r
+       {OPT_DIR_LIST,          NULL},\r
+       {OPT_PROTECT,           NULL},\r
+#if !defined(NO_CGI)\r
+       {OPT_CGI_EXTENSIONS,    NULL},\r
+       {OPT_CGI_INTERPRETER,   NULL},\r
+#endif /* NO_CGI */\r
+       {OPT_SSI_EXTENSIONS,    NULL},\r
+#if !defined(NO_AUTH)\r
+       {OPT_AUTH_DOMAIN,       NULL},\r
+       {OPT_AUTH_GPASSWD,      &set_gpass_option},\r
+       {OPT_AUTH_PUT,          NULL},\r
+#endif /* !NO_AUTH */\r
+#if !defined(_WIN32)\r
+       {OPT_UID,               &set_uid_option},\r
+#endif /* !_WIN32 */\r
+       {OPT_ACCESS_LOG,        &set_alog_option},\r
+       {OPT_ERROR_LOG,         &set_elog_option},\r
+       {OPT_ALIASES,           NULL},\r
+       {OPT_ADMIN_URI,         &set_admin_uri_option},\r
+       {OPT_ACL,               NULL},\r
+       {OPT_THREADS,           set_threads_option},\r
+       {-1,                    NULL}\r
+};\r
+\r
+static const struct mg_option *\r
+find_opt(const char *opt_name)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0; known_options[i].name != NULL; i++)\r
+               if (!strcmp(opt_name, known_options[i].name))\r
+                       return (known_options + i);\r
+\r
+       return (NULL);\r
+}\r
+\r
+int\r
+mg_set_option(struct mg_context *ctx, const char *opt, const char *val)\r
+{\r
+       const struct mg_option  *option;\r
+       int                     i, ctx_index, retval;\r
+\r
+       mg_lock(ctx);\r
+       if (opt != NULL && (option = find_opt(opt)) != NULL) {\r
+               i = (int) (option - known_options);\r
+\r
+               if (setters[i].setter != NULL)\r
+                       retval = setters[i].setter(ctx, val);\r
+               else\r
+                       retval = TRUE;\r
+\r
+               /* Free old value if any */\r
+               ctx_index = setters[i].context_index;\r
+               if (ctx->options[ctx_index] != NULL)\r
+                       free(ctx->options[ctx_index]);\r
+\r
+               /* Set new option value */\r
+               ctx->options[ctx_index] = val ? mg_strdup(val) : NULL;\r
+       } else {\r
+               retval = -1;\r
+       }\r
+       mg_unlock(ctx);\r
+\r
+       return (retval);\r
+}\r
+\r
+const struct mg_option *\r
+mg_get_option_list(void)\r
+{\r
+       return (known_options);\r
+}\r
+\r
+const char *\r
+mg_get_option(struct mg_context *ctx, const char *option_name)\r
+{\r
+       const struct mg_option  *o;\r
+       const char                      *value = NULL;\r
+\r
+       mg_lock(ctx);\r
+       value = NULL;\r
+       if ((o = find_opt(option_name)) != NULL)\r
+               value = ctx->options[setters[o - known_options].context_index];\r
+       mg_unlock(ctx);\r
+\r
+       return (value);\r
+}\r
+\r
+static void\r
+reset_per_request_attributes(struct mg_connection *conn)\r
+{\r
+       if (conn->request_info.remote_user != NULL)\r
+               free(conn->request_info.remote_user);\r
+       if (conn->free_post_data && conn->request_info.post_data != NULL)\r
+               free((void *)conn->request_info.post_data);\r
+}\r
+\r
+static void\r
+close_socket_gracefully(SOCKET sock)\r
+{\r
+       char    buf[BUFSIZ];\r
+       int     n;\r
+\r
+       /* Send FIN to the client */\r
+       (void) shutdown(sock, SHUT_WR);\r
+\r
+       /*\r
+        * Read and discard pending data. If we do not do that and close the\r
+        * socket, the data in the send buffer may be discarded. This\r
+        * behaviour is seen on Windows, when client keeps sending data\r
+        * when server decide to close the connection; then when client\r
+        * does recv() it gets no data back.\r
+        */\r
+       do {\r
+               n = pull(-1, sock, NULL, buf, sizeof(buf));\r
+       } while (n > 0);\r
+\r
+       /* Now we know that our FIN is ACK-ed, safe to close */\r
+       (void) closesocket(sock);\r
+}\r
+\r
+static void\r
+close_connection(struct mg_connection *conn)\r
+{\r
+       reset_per_request_attributes(conn);\r
+       if (conn->ssl)\r
+               SSL_free(conn->ssl);\r
+       if (conn->sock != INVALID_SOCKET) {\r
+               close_socket_gracefully(conn->sock);\r
+       }\r
+}\r
+\r
+static void\r
+reset_connection_attributes(struct mg_connection *conn)\r
+{\r
+       reset_per_request_attributes(conn);\r
+       conn->free_post_data = FALSE;\r
+       conn->request_info.status_code = -1;\r
+       conn->keep_alive = FALSE;\r
+       conn->num_bytes_sent = 0;\r
+       (void) memset(&conn->request_info, 0, sizeof(conn->request_info));\r
+}\r
+\r
+static void\r
+shift_to_next(struct mg_connection *conn, char *buf, int req_len, int *nread)\r
+{\r
+       uint64_t        cl;\r
+       int             over_len, body_len;\r
+\r
+       cl = get_content_length(conn);\r
+       over_len = *nread - req_len;\r
+       assert(over_len >= 0);\r
+\r
+       if (cl == UNKNOWN_CONTENT_LENGTH) {\r
+               body_len = 0;\r
+       } else if (cl < (uint64_t) over_len) {\r
+               body_len = (int) cl;\r
+       } else {\r
+               body_len = over_len;\r
+       }\r
+\r
+       *nread -= req_len + body_len;\r
+       (void) memmove(buf, buf + req_len + body_len, *nread);\r
+}\r
+\r
+static void\r
+process_new_connection(struct mg_connection *conn)\r
+{\r
+       struct mg_request_info *ri = &conn->request_info;\r
+       char    buf[MAX_REQUEST_SIZE];\r
+       int     request_len, nread;\r
+\r
+       nread = 0;\r
+       do {\r
+               /* If next request is not pipelined, read it in */\r
+               if ((request_len = get_request_len(buf, (size_t) nread)) == 0)\r
+                       request_len = read_request(-1, conn->sock, conn->ssl,\r
+                           buf, sizeof(buf), &nread);\r
+               assert(nread >= request_len);\r
+\r
+               if (request_len == 0)\r
+                       break;  /* Remote end closed the connection */\r
+\r
+               /*\r
+                * This sets conn->keep_alive to FALSE, so by default\r
+                * we break the loop.\r
+                */\r
+               reset_connection_attributes(conn);\r
+\r
+               /* 0-terminate the request: parse_request uses sscanf */\r
+               buf[request_len - 1] = '\0';\r
+\r
+               if (parse_http_request(buf, ri, &conn->rsa)) {\r
+                       if (ri->http_version_major != 1 ||\r
+                            (ri->http_version_major == 1 &&\r
+                            (ri->http_version_minor < 0 ||\r
+                            ri->http_version_minor > 1))) {\r
+                               send_error(conn, 505,\r
+                                   "HTTP version not supported",\r
+                                   "%s", "Weird HTTP version");\r
+                               log_access(conn);\r
+                       } else {\r
+                               ri->post_data = buf + request_len;\r
+                               ri->post_data_len = nread - request_len;\r
+                               analyze_request(conn);\r
+                               log_access(conn);\r
+                               shift_to_next(conn, buf, request_len, &nread);\r
+                       }\r
+               } else {\r
+                       /* Do not put garbage in the access log */\r
+                       send_error(conn, 400, "Bad Request",\r
+                           "Can not parse request: [%.*s]", nread, buf);\r
+               }\r
+\r
+       } while (conn->keep_alive);\r
+}\r
+\r
+static void\r
+accept_new_connection(const struct socket *listener, struct mg_context *ctx)\r
+{\r
+       struct socket   rem;\r
+\r
+       rem.usa.len = sizeof(rem.usa.u.sin);\r
+\r
+       if ((rem.sock = accept(listener->sock,\r
+           &rem.usa.u.sa, &rem.usa.len)) == -1) {\r
+               cry("accept: %d", ERRNO);\r
+       } else if (!check_acl(ctx, &rem.usa)) {\r
+               (void) closesocket(rem.sock);\r
+               cry("%s: denied by ACL", inet_ntoa(rem.usa.u.sin.sin_addr));\r
+       } else {\r
+               rem.flags = listener->flags;\r
+               put_socket(&ctx->socket_pool, &rem);\r
+       }\r
+}\r
+\r
+static void\r
+worker_loop(struct mg_context *ctx)\r
+{\r
+       struct mg_connection    conn;\r
+       struct socket           rem;\r
+       bool_t                  ssl;\r
+\r
+       for (;;) {\r
+               get_socket(&ctx->socket_pool, &rem);\r
+\r
+               if (rem.flags & FLAG_TERMINATE)\r
+                       break;\r
+\r
+               conn.sock = rem.sock;\r
+               conn.rsa = rem.usa;\r
+               conn.ctx = ctx;\r
+               conn.birth_time = time(NULL);\r
+               conn.ssl = NULL;\r
+               conn.free_post_data = conn.keep_alive = FALSE;\r
+\r
+               ssl = rem.flags & FLAG_SSL;\r
+               if (ssl && (conn.ssl = SSL_new(ctx->ssl_ctx)) == NULL) {\r
+                       cry("%s: SSL_new: %s", __func__, strerror(ERRNO));\r
+               } else if (ssl && SSL_set_fd(conn.ssl, conn.sock) != 1) {\r
+                       cry("%s: SSL_set_fd: %s", __func__, strerror(ERRNO));\r
+               } else if (ssl && SSL_accept(conn.ssl) != 1) {\r
+                       cry("%s: SSL handshake failed", __func__);\r
+               } else {\r
+                       process_new_connection(&conn);\r
+               }\r
+               close_connection(&conn);\r
+       }\r
+}\r
+\r
+static void\r
+listening_loop(struct mg_context *ctx)\r
+{\r
+       fd_set          read_set;\r
+       struct timeval  tv;\r
+       int             i, max_fd;\r
+\r
+       while (ctx->stop_flag == 0) {\r
+               FD_ZERO(&read_set);\r
+               max_fd = -1;\r
+\r
+               /* Add listening sockets to the read set */\r
+               for (i = 0; i < ctx->num_listeners; i++)\r
+                       add_to_set(ctx->listeners[i].sock, &read_set, &max_fd);\r
+\r
+               tv.tv_sec = 1;\r
+               tv.tv_usec = 0;\r
+\r
+               if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {\r
+#ifdef _WIN32\r
+                       /*\r
+                        * On windows, if read_set and write_set are empty,\r
+                        * select() returns "Invalid parameter" error\r
+                        * (at least on my Windows XP Pro). So in this case,\r
+                        * we sleep here.\r
+                        */\r
+                       Sleep(1000);\r
+#endif /* _WIN32 */\r
+               } else {\r
+                       for (i = 0; i < ctx->num_listeners; i++)\r
+                               if (FD_ISSET(ctx->listeners[i].sock, &read_set))\r
+                                       accept_new_connection(\r
+                                           ctx->listeners + i, ctx);\r
+               }\r
+       }\r
+\r
+       /* Stop signal received: somebody called mg_stop. Quit. */\r
+       mg_fini(ctx);\r
+}\r
+\r
+void\r
+mg_stop(struct mg_context *ctx)\r
+{\r
+       ctx->stop_flag = 1;\r
+}\r
+\r
+struct mg_context *\r
+mg_start(void)\r
+{\r
+       struct mg_context       *ctx;\r
+       int                     i;\r
+\r
+       if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {\r
+               cry("cannot allocate mongoose context");\r
+               return (NULL);\r
+       }\r
+\r
+       /* Initialize options. First pass: set default option values */\r
+       for (i = 0; known_options[i].name != NULL; i++)\r
+               ctx->options[setters[i].context_index] =\r
+                       known_options[i].default_value  == NULL ?\r
+                       NULL : mg_strdup(known_options[i].default_value);\r
+\r
+       /* Call setter functions */\r
+       for (i = 0; known_options[i].name != NULL; i++)\r
+               if (setters[i].setter &&\r
+                   ctx->options[setters[i].context_index] != NULL)\r
+                       if (setters[i].setter(ctx,\r
+                           ctx->options[setters[i].context_index]) == FALSE) {\r
+                               mg_fini(ctx);\r
+                               return (NULL);\r
+                       }\r
+\r
+       /* Initial document root is set to current working directory */\r
+       if (ctx->options[OPT_ROOT] == NULL)\r
+               ctx->options[OPT_ROOT] = getcwd(NULL, 0);\r
+\r
+#if 0\r
+       tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);\r
+#endif\r
+\r
+#ifdef _WIN32\r
+       {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}\r
+#endif /* _WIN32 */\r
+\r
+       /* Start worker threads */\r
+       init_socket_pool(&ctx->socket_pool);\r
+       for (i = 0; i < atoi(ctx->options[OPT_THREADS]); i++)\r
+               start_thread((mg_thread_func_t) worker_loop, ctx);\r
+\r
+       /* Start master (listening) thread */\r
+       (void) pthread_mutex_init(&ctx->mutex, NULL);\r
+       start_thread((mg_thread_func_t) listening_loop, ctx);\r
+\r
+       return (ctx);\r
+}\r
diff --git a/vendor/mongoose/mongoose.h b/vendor/mongoose/mongoose.h
new file mode 100644 (file)
index 0000000..4b4b21f
--- /dev/null
@@ -0,0 +1,139 @@
+/*\r
+ * Copyright (c) 2004-2009 Sergey Lyubka\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in\r
+ * all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+ * THE SOFTWARE.\r
+ *\r
+ * $Id: mongoose.h 154 2008-12-22 20:14:28Z valenok $\r
+ */\r
+\r
+#ifndef MONGOOSE_HEADER_INCLUDED\r
+#define        MONGOOSE_HEADER_INCLUDED\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif /* __cplusplus */\r
+\r
+struct mg_context;     /* Handle for the HTTP service itself   */\r
+struct mg_connection;  /* Handle for the individual connection */\r
+\r
+/*\r
+ * This structure contains full information about the HTTP request.\r
+ * It is passed to the user-specified callback function as a parameter.\r
+ */\r
+struct mg_request_info {\r
+       char    *request_method;        /* "GET", "POST", etc   */\r
+       char    *uri;                   /* Normalized URI       */\r
+       char    *query_string;          /* \0 - terminated      */\r
+       char    *post_data;             /* POST data buffer     */\r
+       char    *remote_user;           /* Authenticated user   */\r
+       long    remote_ip;              /* Client's IP address  */\r
+       int     remote_port;            /* Client's port        */\r
+       int     post_data_len;          /* POST buffer length   */\r
+       int     http_version_major;\r
+       int     http_version_minor;\r
+       int     status_code;            /* HTTP status code     */\r
+       int     num_headers;            /* Number of headers    */\r
+#define        MAX_HTTP_HEADERS        64\r
+       struct mg_header {\r
+               char    *name;          /* HTTP header name     */\r
+               char    *value;         /* HTTP header value    */\r
+       } http_headers[MAX_HTTP_HEADERS];\r
+};\r
+\r
+/*\r
+ * Mongoose configuration option.\r
+ * Array of those is returned by mg_get_option_list().\r
+ */\r
+struct mg_option {\r
+       char    *name;\r
+       char    *description;\r
+       char    *default_value;\r
+};\r
+\r
+/*\r
+ * Functions dealing with initialization, starting and stopping Mongoose\r
+ *\r
+ * mg_start            Start serving thread. Return server context.\r
+ * mg_stop             Stop server thread, and release the context.\r
+ * mg_set_option       Set an option for the running context.\r
+ * mg_get_option       Get an option for the running context.\r
+ * mg_get_option_list  Get a list of all known options.\r
+ * mg_handle_uri       Associate user function with paticular URI.\r
+ *                     '*' in regex matches zero or more characters.\r
+ * mg_handle_error_code        Associate user function with HTTP error code.\r
+ *                     Passing 0 as error code binds function to all codes.\r
+ *                     Error code is passed as status_code in request info.\r
+ * mg_protect_uri      Similar to "protect" option, but uses a user\r
+ *                     specified function instead of the passwords file.\r
+ *                     User specified function is usual callback, which\r
+ *                     does use its third argument to pass the result back.\r
+ */\r
+\r
+struct mg_context *mg_start(void);\r
+void mg_stop(struct mg_context *);\r
+const struct mg_option *mg_get_option_list(void);\r
+const char *mg_get_option(struct mg_context *, const char *);\r
+int mg_set_option(struct mg_context *, const char *, const char *);\r
+\r
+typedef void (*mg_callback_t)(struct mg_connection *,\r
+               const struct mg_request_info *info, void *user_data);\r
+\r
+void mg_bind_to_uri(struct mg_context *ctx, const char *uri_regex,\r
+               mg_callback_t func, void *user_data);\r
+void mg_bind_to_error_code(struct mg_context *ctx, int error_code,\r
+               mg_callback_t func, void *user_data);\r
+void mg_protect_uri(struct mg_context *ctx, const char *uri_regex,\r
+               mg_callback_t func, void *user_data);\r
+\r
+/*\r
+ * Needed only if SSL certificate asks for a password.\r
+ * Instead of prompting for a password, specified function will be called.\r
+ */\r
+typedef int (*mg_spcb_t)(char *buf, int num, int w, void *key);\r
+void mg_set_ssl_password_callback(struct mg_context *ctx, mg_spcb_t func);\r
+\r
+/*\r
+ * Functions that can be used within the user URI callback\r
+ *\r
+ * mg_write    Send data to the remote end.\r
+ * mg_printf   Send data, using printf() semantics.\r
+ * mg_get_header Helper function to get HTTP header value\r
+ * mg_get_var  Helper function to get form variable value.\r
+ *             Returned value must be free-d by the caller.\r
+ */\r
+int mg_write(struct mg_connection *, const void *buf, int len);\r
+int mg_printf(struct mg_connection *, const char *fmt, ...);\r
+const char *mg_get_header(const struct mg_connection *, const char *hdr_name);\r
+char *mg_get_var(const struct mg_connection *, const char *var_name);\r
+\r
+/*\r
+ * General helper functions\r
+ * mg_version  Return current version.\r
+ * mg_md5      Helper function. buf must be 33 bytes in size. Expects\r
+ *             a NULL terminated list of asciz strings.\r
+ *             Fills buf with stringified \0 terminated MD5 hash.\r
+ */\r
+const char *mg_version(void);\r
+void mg_md5(char *buf, ...);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+\r
+#endif /* MONGOOSE_HEADER_INCLUDED */\r
diff --git a/zypp-history.lr b/zypp-history.lr
new file mode 100644 (file)
index 0000000..86002d8
--- /dev/null
@@ -0,0 +1,11 @@
+/var/log/zypp/history {
+    compress
+    dateext
+    notifempty
+    missingok
+    nocreate
+
+    maxage 1827
+    rotate 99
+    size 10M
+}
diff --git a/zypp.conf b/zypp.conf
new file mode 100644 (file)
index 0000000..58a411e
--- /dev/null
+++ b/zypp.conf
@@ -0,0 +1,485 @@
+## Configuration file for software management
+## /etc/zypp/zypp.conf
+##
+## Boolean values are 0 1 yes no on off true false
+
+
+[main]
+
+
+##
+## Override the detected architecture
+##
+## Valid values:  i586, i686, x86_64, ppc, ppc64, ia64, s390, s390x, ..
+## Default value: Autodetected
+##
+## ** CAUTION: Only set if you know what you're doing !
+## ** Changing this needs a full refresh (incl. download)
+## ** of all repository data.
+##
+# arch = s390
+
+
+##
+## Path where the caches are kept.
+##
+## Valid values: A directory
+## Default value: /var/cache/zypp
+##
+# cachedir = /var/cache/zypp
+
+
+##
+## Path where the repo metadata is downloaded and kept.
+##
+## Valid values: A directory
+## Default value: {cachedir}/raw
+##
+## Changing this needs a full refresh (incl. download) of all repository data
+##
+# metadatadir = /var/cache/zypp/raw
+
+
+##
+## Path where the repo solv files are created and kept.
+##
+## Valid values: A directory
+## Default value: {cachedir}/solv
+##
+# solvfilesdir = /var/cache/zypp/solv
+
+
+##
+## Path where the repo packages are downloaded and kept.
+##
+## Valid values: A directory
+## Default value: {cachedir}/packages
+##
+# packagesdir = /var/cache/zypp/packages
+
+
+##
+## Path where the configuration files are kept.
+##
+## Valid values: A directory
+## Default value: /etc/zypp
+##
+# configdir = /etc/zypp
+
+##
+## Path where the known repositories .repo files are kept
+##
+## Valid values: A directory
+## Default value: {configdir}/repos.d
+##
+## Changing this invalidates all known repositories
+##
+# reposdir = /etc/zypp/repos.d
+
+##
+## Path where the known services .service files are kept
+##
+## Valid values: A directory
+## Default value: {configdir}/services.d
+##
+## Changing this invalidates all known services
+##
+# servicesdir = /etc/zypp/services.d
+
+
+##
+## Whether repository urls should be probed when added
+##
+## Valid values: boolean
+## Default value: false
+##
+## If true, accessability of repositories is checked immediately (when added)
+##   (e.g. 'zypper ar' will check immediately)
+## If false, accessability of repositories is checked when refreshed
+##   (e.g. 'zypper ar' will delay the check until the next refresh)
+##
+# repo.add.probe = false
+
+
+##
+## Amount of time in minutes that must pass before another refresh.
+##
+## Valid values: Integer
+## Default value: 10
+##
+## If you have autorefresh enabled for a repository, it is checked for
+## up-to-date metadata not more often than every <repo.refresh.delay>
+## minutes. If an automatic request for refresh comes before <repo.refresh.delay>
+## minutes passed since the last check, the request is ignored.
+##
+## A value of 0 means the repository will always be checked. To get the oposite
+## effect, disable autorefresh for your repositories.
+##
+## This option has no effect for repositories with autorefresh disabled, nor for
+## user-requested refresh.
+##
+# repo.refresh.delay = 10
+
+##
+## Maximum number of concurrent connections to use per transfer
+##
+## Valid values: Integer
+## Default value: 5
+##
+## This setting is only used if more than one is possible
+## Setting it to a reasonable number avoids flooding servers
+##
+# download.max_concurrent_connections = 5
+
+##
+## Sets the minimum download speed (bytes per second)
+## until the connection is dropped
+## This can be useful to prevent security attacks on hosts by
+## providing updates at very low speeds.
+##
+## 0 means no limit
+##
+# download.min_download_speed = 0
+
+## Maximum download speed (bytes per second)
+## 0 means no limit
+# download.max_download_speed = 0
+
+## Number of tries per download which will be
+## done without user interaction
+## 0 means no limit (use with caution)
+# download.max_silent_tries = 5
+
+##
+## Whether to consider using a .delta.rpm when downloading a package
+##
+## Valid values: boolean
+## Default value: true
+##
+## Using a delta rpm will decrease the download size for package updates
+## since it does not contain all files of the package but only the binary
+## diff of changed ones. Recreating the rpm package on the local machine
+## is an expensive operation (memory,CPU). If your network connection is
+## not too slow, you benefit from disabling .delta.rpm.
+##
+# download.use_deltarpm = true
+
+##
+## Whether to consider using a deltarpm even when rpm is local
+##
+## Valid values: boolean
+## Default value: false
+##
+## This option has no effect unless download.use_deltarpm is set true.
+##
+#  download.use_deltarpm.always = false
+
+##
+## Hint which media to prefer when installing packages (download vs. CD).
+##
+## Valid values:       download, volatile
+## Default value:      download
+##
+## Note that this just a hint. First of all the solver will choose the 'best'
+## package according to its repos priority, version and architecture. But if
+## there is a choice, we will prefer packages from the desired media.
+##
+## Packages available locally are always preferred. The question is whether
+## you prefer packages being downloaded via FTP/HTTP/HTTPS (download), rather
+## than being prompted to insert a CD/DVD (volatile), in case they are available
+## on both media.
+##
+##   Name             | Priority | URI
+##   openSUSE-11.1     99         dvd:///
+##   openSUSE-11.1-Oss 99         http://download.opensuse.org/distribution/11.1/repo/oss
+##
+## In the above example 2 repositories with similar content are used. Rather
+## than raising the priority of one of them to 'prefer' a certain media, you
+## should use the same priority for both and set download.media_preference
+## instead.
+##
+## download.media_preference = download
+
+##
+## Commit download policy to use as default.
+##
+##  DownloadOnly,      Just download all packages to the local cache.
+##                     Do not install. Implies a dry-run.
+##
+##  DownloadInAdvance, First download all packages to the local cache.
+##                     Then start to install.
+##
+##  DownloadInHeaps,   Similar to DownloadInAdvance, but try to split
+##                     the transaction into heaps, where at the end of
+##                     each heap a consistent system state is reached.
+##
+##  DownloadAsNeeded   Alternating download and install. Packages are
+##                     cached just to avid CD/DVD hopping. This is the
+##                     traditional behaviour.
+##
+##  <UNSET>            If a value is not set, empty or unknown, we pick
+##                     some sane default.
+##
+## commit.downloadMode =
+
+##
+## Defining directory which contains vendor description files.
+##
+## One file in this directory reflects a group of equivalent vendors. e.G.:
+## (filename is "nvidia" but could be any other name):
+## ------------------------- file contains begin -----------------------
+## [main]
+##
+## vendors = nvidia,suse,opensuse
+##
+## ------------------------- file contains end -----------------------
+## Libzypp makes an string comparision (like strncmp, case-insensitive)
+## whereas the beginning of the strings are compared only.
+## e.G. vendor "opensuse11.0" is compatible to "openSuSE".
+##
+## Valid values: A directory
+## Default value: {configdir}/vendors.d
+##
+# vendordir = /etc/zypp/vendors.d
+
+
+##
+## Whether required packages are installed ONLY
+## So recommended packages, language packages and packages which depend
+## on hardware (modalias) will not be regarded.
+##
+## Valid values: boolean
+## Default value: false
+##
+# solver.onlyRequires = false
+
+##
+## EXPERTS ONLY: Per default the solver will not replace packages of
+## different vendors, unless you explicitly ask to do so. Setting this
+## option to TRUE will disable this vendor check (unless the application
+## explicitly re-enables it). Packages will then be considered based on
+## repository priority and version only. This may easily damage your system.
+##
+## CHANGING THE DEFAULT IS NOT RECOMMENDED.
+##
+## Valid values:  boolean
+## Default value: false
+##
+# solver.allowVendorChange = false
+
+##
+## EXPERTS ONLY: Cleanup when deleting packages. Whether the solver should
+## per default try to remove packages exclusively required by the ones he's
+## asked to delete.
+##
+## This option should be used on a case by case basis, enabled via
+## command line options or switches the applications offer. Changing
+## the global default on a system where unattended actions are performed,
+## may easily damage your system.
+##
+## CHANGING THE DEFAULT IS NOT RECOMMENDED.
+##
+## Valid values:  boolean
+## Default value: false
+##
+# solver.cleandepsOnRemove = false
+
+##
+## This file contains requirements/conflicts which fulfill the
+## needs of a running system.
+## For example the system would be broken if not glibc or kernel is
+## installed.
+## So the user will be informed if these packages will be deleted.
+##
+## Format: Each line represents one dependency:
+##         e.g.
+##         requires:kernel
+##         requires:glibc
+## Default value: {configdir}/systemCheck
+##
+# solver.checkSystemFile = /etc/zypp/systemCheck
+
+##
+## When committing a dist upgrade (e.g. 'zypper dup') a solver testcase
+## is written to /var/log/updateTestcase-<date>. It is needed in bugreports.
+## This optin returns the number of testcases to keep on the system. Old
+## cases will be deleted, as new ones are created.
+##
+## Use 0 to write no testcase at all, or -1 to keep all testcases.
+##
+## Valid values:       Integer
+## Default value:      2
+##
+# solver.upgradeTestcasesToKeep = 2
+
+##
+## Whether dist upgrade should remove a products dropped packages.
+##
+## A new product may suggest a list of old and no longer supported
+## packages (dropped packages). Performing a dist upgrade the solver
+## may try to delete them, even if they do not cause any dependency
+## problem.
+##
+## Turning this option off, the solver will not try to remove those
+## packages unless they actually do cause dependency trouble. You may
+## do the cleanup manually, or simply leave them installed as long
+## as you don't need the disk space.
+##
+## Valid values:       Boolean
+## Default value:      true
+##
+# solver.upgradeRemoveDroppedPackages = true
+
+##
+## Packages which can be installed in different versions at the same time.
+##
+## Packages are selected either by name, or by provides. In the later case
+## the string must start with "provides:" immediately followed by the capability.
+##
+## Example:
+##     kernel                          - just packages whith name 'kernel'
+##     provides:multiversion(kernel)   - all packages providing 'multiversion(kernel)'
+##                                       (kenel and kmp packages should do this)
+## Valid values:
+##     Comma separated list of packages.
+##
+## Default value:
+##     empty
+##
+# multiversion = provides:multiversion(kernel)
+
+## Comma separated list of kernel packages to keep installed in parallel, if the
+## above multiversion variable is set. Packages can be specified as
+## 2.6.32.12-0.7 - Exact version to keep
+## latest        - Keep kernel with the highest version number
+## latest-N      - Keep kernel with the Nth highest version number
+## running       - Keep the running kernel
+## oldest        - Keep kernel with the lowest version number (the GA kernel)
+## oldest+N      - Keep kernel with the Nth lowest version number
+##
+## Default: Do not delete any kernels if multiversion = provides:multiversion(kernel) is set
+# multiversion.kernels = latest,running
+
+##
+## Path to locks file. If not exist then is create.
+## In this file is saved also UI locks.
+##
+## valid value: path to file or place where file can be created
+## default value: {configdir}/locks
+##
+# locksfile.path = /etc/zypp/locks
+
+##
+## Whetever to apply locks in locks file after zypp start.
+##
+## Valid values: boolean
+## Default value: true
+##
+# locksfile.apply = true
+
+##
+## Where update items are stored
+## (example: scripts, messages)
+##
+## Valid values: path to directory
+## Default value: /var/adm
+##
+# update.datadir = /var/adm
+
+##
+## Where update messages are stored
+##
+## Valid values: path to directory
+## Default value: {update.datadir}/update-messages
+##
+# update.messagesdir = /var/adm/update-messages
+
+##
+## Where update scripts are stored
+##
+## Valid values: path to directory
+## Default value: {update.datadir}/update-scripts
+##
+# update.scriptsdir = /var/adm/update-scripts
+
+##
+## Command to be invoked to send update messages.
+##
+## Packages may leave an update message file in {update.messagesdir}.
+## At the end of each commit, zypp collects those messages and may send
+## a notification to the user.
+##
+## zypp will prepare the update messages according to the selected
+## content format and pipe the content to the command.
+##
+## Format:
+##     single - For each update message invoke the command and send
+##              the message.
+##     none   - For each update message invoke the command but don't
+##              use a pipe to send any data. You probably want to pass
+##              the message file on the commandline using %P (see
+##              Substitutions).
+##     digest - Single invocation of the command, sending the path
+##              names of all update message. One per line.
+##     bulk   - Single invocation of the command, sending the
+##              concatenated content of all update messages, separated
+##              by Ctrl-L.
+##
+## Substitutions:
+##     %p     - package identification (name-version-release.arch)
+##     %P     - full path to the update message file
+##
+## Valid values: The value is specified as "format | command".
+##               An empty value will turn off any notification.
+##
+## Examples:     single | mail -s 'Update message from %p' root
+##               none   | my-send-script -f %P
+##
+## Default value: single | /usr/lib/zypp/notify-message -p %p
+##
+# update.messages.notify = single | /usr/lib/zypp/notify-message -p %p
+
+##
+## Options for package installation: excludedocs
+##
+## Don't install any files which are marked as documentation.
+##
+## Valid values:  boolean
+## Default value: no
+##
+# rpm.install.excludedocs = no
+
+##
+## Location of history log file.
+##
+## The history log is described at
+## http://en.opensuse.org/Libzypp/Package_History
+##
+## Valid values: absolute path to a file
+## Default value: /var/log/zypp/history
+##
+# history.logfile = /var/log/zypp/history
+
+##
+## Global credentials directory path.
+##
+## If a URL contains ?credentials=<filename> parameter, the credentials will
+## be stored and looked for in a file named <filename> in this directory.
+##
+## Valid values: absolute path to a directory
+## Default value: /etc/zypp/credentials.d
+##
+# credentials.global.dir = /etc/zypp/credentials.d
+
+##
+## Global credentials catalog file path.
+##
+## This file contains a catalog of all known user credentials which were
+## not stored via the ?credentials=<filename> URL parameter, i.e. passed
+## in URL as username:password component, or entered by user in
+## an authentication dialog.
+##
+## Valid values: absolute path to a file
+## Default value: /etc/zypp/credentials.cat
+##
+# credentials.global.file = /etc/zypp/credentials.cat
diff --git a/zypp/Arch.cc b/zypp/Arch.cc
new file mode 100644 (file)
index 0000000..6b5eeb0
--- /dev/null
@@ -0,0 +1,574 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Arch.cc
+ *
+*/
+#include <iostream>
+#include <list>
+#include <inttypes.h>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Tr1hash.h"
+#include "zypp/Arch.h"
+#include "zypp/Bit.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Arch::CompatEntry
+  //
+  /** Holds an architecture ID and it's compatible relation.
+   * An architecture is compatibleWith, if it's _idBit is set in
+   * _compatBits. noarch has ID 0, non builtin archs ID 1 and
+   * have to be treated specialy.
+  */
+  struct Arch::CompatEntry
+  {
+    /** Bitfield for architecture IDs and compatBits relation.
+     * \note Need one bit for each builtin Arch.
+     * \todo Migrate to some infinite BitField
+    */
+    typedef bit::BitField<uint64_t> CompatBits;
+
+    CompatEntry( const std::string & archStr_r,
+                 CompatBits::IntT idBit_r = 1 )
+    : _idStr( archStr_r )
+    , _archStr( archStr_r )
+    , _idBit( idBit_r )
+    , _compatBits( idBit_r )
+    {}
+
+    CompatEntry( IdString archStr_r,
+                 CompatBits::IntT idBit_r = 1 )
+    : _idStr( archStr_r )
+    , _archStr( archStr_r.asString() )
+    , _idBit( idBit_r )
+    , _compatBits( idBit_r )
+    {}
+
+    void addCompatBit( const CompatBits & idBit_r ) const
+    {
+      if ( idBit_r && ! (_compatBits & idBit_r) )
+        {
+          _compatBits |= idBit_r;
+        }
+    }
+
+    /** Return whether \c this is compatible with \a targetEntry_r.*/
+    bool compatibleWith( const CompatEntry & targetEntry_r ) const
+    {
+      switch ( _idBit.value() )
+        {
+        case 0:
+          // this is noarch and always comatible
+          return true;
+          break;
+        case 1:
+          // this is a non builtin: self compatible only
+          return _archStr == targetEntry_r._archStr;
+          break;
+        }
+      // This is a builtin: compatible if mentioned in targetEntry_r
+      return targetEntry_r._compatBits & _idBit;
+    }
+
+    /** compare by score, then archStr. */
+    int compare( const CompatEntry & rhs ) const
+    {
+      if ( _idBit.value() != rhs. _idBit.value() )
+       return( _idBit.value() < rhs. _idBit.value() ? -1 : 1 );
+      return _archStr.compare( rhs._archStr ); // Id 1: non builtin
+    }
+
+    bool isBuiltIn() const
+    { return( _idBit != CompatBits(1) ); }
+
+    IdString::IdType id() const
+    { return _idStr.id(); }
+
+    IdString            _idStr;
+    std::string         _archStr; // frequently used by the UI so we keep a reference
+    CompatBits          _idBit;
+    mutable CompatBits  _compatBits;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Arch::CompatEntry Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Arch::CompatEntry & obj )
+  {
+    Arch::CompatEntry::CompatBits bit( obj._idBit );
+    unsigned bitnum = 0;
+    while ( bit )
+    {
+      ++bitnum;
+      bit >>= 1;
+    }
+    return str << str::form( "%-15s ", obj._archStr.c_str() ) << str::numstring(bitnum,2) << ' '
+               << obj._compatBits << ' ' << obj._compatBits.value();
+  }
+
+  /** \relates Arch::CompatEntry */
+  inline bool operator==( const Arch::CompatEntry & lhs, const Arch::CompatEntry & rhs )
+  { return lhs._idStr == rhs._idStr; }
+  /** \relates Arch::CompatEntry */
+  inline bool operator!=( const Arch::CompatEntry & lhs, const Arch::CompatEntry & rhs )
+  { return ! ( lhs == rhs ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+ZYPP_DEFINE_ID_HASHABLE( zypp::Arch::CompatEntry );
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    // Builtin architecture STRING VALUES to be
+    // used in defCompatibleWith below!
+    //
+    // const IdString  _foo( "foo" );
+    //
+    // NOTE: Builtin CLASS Arch CONSTANTS are defined below.
+    //       You have to change them accordingly.
+    //
+    // NOTE: Thake care CompatBits::IntT is able to provide one
+    //       bit for each architecture.
+    //
+#define DEF_BUILTIN(A) const IdString  _##A( #A );
+    DEF_BUILTIN( noarch );
+
+    DEF_BUILTIN( i386 );
+    DEF_BUILTIN( i486 );
+    DEF_BUILTIN( i586 );
+    DEF_BUILTIN( i686 );
+    DEF_BUILTIN( athlon );
+    DEF_BUILTIN( x86_64 );
+
+    DEF_BUILTIN( pentium3 );
+    DEF_BUILTIN( pentium4 );
+
+    DEF_BUILTIN( s390 );
+    DEF_BUILTIN( s390x );
+
+    DEF_BUILTIN( ppc );
+    DEF_BUILTIN( ppc64 );
+
+    DEF_BUILTIN( ia64 );
+
+    DEF_BUILTIN( alphaev67 );
+    DEF_BUILTIN( alphaev6 );
+    DEF_BUILTIN( alphapca56 );
+    DEF_BUILTIN( alphaev56 );
+    DEF_BUILTIN( alphaev5 );
+    DEF_BUILTIN( alpha );
+
+    DEF_BUILTIN( sparc64v );
+    DEF_BUILTIN( sparcv9v );
+    DEF_BUILTIN( sparc64 );
+    DEF_BUILTIN( sparcv9 );
+    DEF_BUILTIN( sparcv8 );
+    DEF_BUILTIN( sparc );
+
+    DEF_BUILTIN( armv7nhl );
+    DEF_BUILTIN( armv7hl );
+    DEF_BUILTIN( armv7l );
+    DEF_BUILTIN( armv6l );
+    DEF_BUILTIN( armv5tejl );
+    DEF_BUILTIN( armv5tel );
+    DEF_BUILTIN( armv5l );
+    DEF_BUILTIN( armv4tl );
+    DEF_BUILTIN( armv4l );
+    DEF_BUILTIN( armv3l );
+
+    DEF_BUILTIN( sh3 );
+
+    DEF_BUILTIN( sh4 );
+    DEF_BUILTIN( sh4a );
+#undef DEF_BUILTIN
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CompatSet
+    //
+    /** Maintain architecture compatibility (Singleton by the way it is used).
+     *
+     * Povides \ref Arch::CompatEntry for \ref Arch. Defines the
+     * compatibleWith relation.
+     * \li \c noarch has _idBit 0
+     * \li \c nonbuiltin archs have _idBit 1
+    */
+    struct ArchCompatSet : private base::NonCopyable
+    {
+      typedef Arch::CompatEntry       CompatEntry;
+      typedef CompatEntry::CompatBits CompatBits;
+
+      typedef std::tr1::unordered_set<CompatEntry> Set;
+      typedef Set::iterator           iterator;
+      typedef Set::const_iterator     const_iterator;
+
+      /** Singleton access. */
+      static ArchCompatSet & instance()
+      {
+        static ArchCompatSet _instance;
+        return _instance;
+      }
+
+      /** Return the entry related to \a archStr_r.
+       * Creates an entry for nonbuiltin archs.
+      */
+      const Arch::CompatEntry & assertDef( const std::string & archStr_r )
+      { return *_compatSet.insert( Arch::CompatEntry( archStr_r ) ).first; }
+      /** \overload */
+      const Arch::CompatEntry & assertDef( IdString archStr_r )
+      { return *_compatSet.insert( Arch::CompatEntry( archStr_r ) ).first; }
+
+      const_iterator begin() const
+      { return _compatSet.begin(); }
+
+      const_iterator end() const
+      { return _compatSet.end(); }
+
+      struct DumpOnCompare
+      {
+        int operator()( const CompatEntry & lhs,  const CompatEntry & rhs ) const
+        { return lhs._idBit.value() < rhs._idBit.value(); }
+      };
+
+      std::ostream & dumpOn( std::ostream & str ) const
+      {
+        str << "ArchCompatSet:";
+        std::list<CompatEntry> ov( _compatSet.begin(), _compatSet.end() );
+        ov.sort( DumpOnCompare() );
+        for_( it, ov.begin(), ov.end() )
+          {
+            str << endl << ' ' << *it;
+          }
+        return str;
+      }
+
+    private:
+      /** Singleton ctor. */
+      ArchCompatSet()
+      {
+        // _noarch must have _idBit 0.
+        // Other builtins have 1-bit set
+        // and are initialized done on the fly.
+        _compatSet.insert( Arch::CompatEntry( _noarch, 0 ) );
+        ///////////////////////////////////////////////////////////////////
+        // Define the CompatibleWith relation:
+        //
+        // NOTE: Order of definition is significant! (Arch::compare)
+       //       - define compatible (less) architectures first!
+        //
+        defCompatibleWith( _i386,      _noarch );
+        defCompatibleWith( _i486,      _noarch,_i386 );
+        defCompatibleWith( _i586,      _noarch,_i386,_i486 );
+        defCompatibleWith( _i686,      _noarch,_i386,_i486,_i586 );
+        defCompatibleWith( _athlon,    _noarch,_i386,_i486,_i586,_i686 );
+        defCompatibleWith( _x86_64,    _noarch,_i386,_i486,_i586,_i686,_athlon );
+
+        defCompatibleWith( _pentium3,  _noarch,_i386,_i486,_i586,_i686 );
+        defCompatibleWith( _pentium4,  _noarch,_i386,_i486,_i586,_i686,_pentium3 );
+
+        defCompatibleWith( _ia64,      _noarch,_i386,_i486,_i586,_i686 );
+        //
+        defCompatibleWith( _s390,      _noarch );
+        defCompatibleWith( _s390x,     _noarch,_s390 );
+        //
+        defCompatibleWith( _ppc,       _noarch );
+        defCompatibleWith( _ppc64,     _noarch,_ppc );
+        //
+        defCompatibleWith( _alpha,     _noarch );
+        defCompatibleWith( _alphaev5,  _noarch,_alpha );
+        defCompatibleWith( _alphaev56, _noarch,_alpha,_alphaev5 );
+        defCompatibleWith( _alphapca56,        _noarch,_alpha,_alphaev5,_alphaev56 );
+        defCompatibleWith( _alphaev6,  _noarch,_alpha,_alphaev5,_alphaev56,_alphapca56 );
+        defCompatibleWith( _alphaev67, _noarch,_alpha,_alphaev5,_alphaev56,_alphapca56,_alphaev6 );
+        //
+        defCompatibleWith( _sparc,     _noarch );
+        defCompatibleWith( _sparcv8,   _noarch,_sparc );
+        defCompatibleWith( _sparcv9,   _noarch,_sparc,_sparcv8 );
+       defCompatibleWith( _sparcv9v,   _noarch,_sparc,_sparcv8,_sparcv9 );
+       //
+        defCompatibleWith( _sparc64,   _noarch,_sparc,_sparcv8,_sparcv9 );
+       defCompatibleWith( _sparc64v,   _noarch,_sparc,_sparcv8,_sparcv9,_sparcv9v,_sparc64 );
+        //
+        defCompatibleWith( _armv3l,    _noarch );
+        defCompatibleWith( _armv4l,    _noarch,_armv3l );
+        defCompatibleWith( _armv4tl,   _noarch,_armv3l,_armv4l );
+        defCompatibleWith( _armv5l,    _noarch,_armv3l,_armv4l,_armv4tl );
+        defCompatibleWith( _armv5tel,  _noarch,_armv3l,_armv4l,_armv4tl,_armv5l );
+        defCompatibleWith( _armv5tejl, _noarch,_armv3l,_armv4l,_armv4tl,_armv5l,_armv5tel );
+        defCompatibleWith( _armv6l,    _noarch,_armv3l,_armv4l,_armv4tl,_armv5l,_armv5tel,_armv5tejl );
+        defCompatibleWith( _armv7l,    _noarch,_armv3l,_armv4l,_armv4tl,_armv5l,_armv5tel,_armv5tejl,_armv6l );
+        defCompatibleWith( _armv7hl,    _noarch );
+        defCompatibleWith( _armv7nhl,   _noarch, _armv7hl );
+        //
+        defCompatibleWith( _sh3,       _noarch );
+        //
+        defCompatibleWith( _sh4,       _noarch );
+        defCompatibleWith( _sh4a,      _noarch,_sh4 );
+        //
+        ///////////////////////////////////////////////////////////////////
+        // dumpOn( USR ) << endl;
+      }
+
+    private:
+      /** Return the next avialable _idBit.
+       * Ctor injects _noarch into the _compatSet, 1 is for
+       * nonbuiltin archs, so we can use <tt>size</tt> for
+       * buitin archs.
+      */
+      CompatBits::IntT nextIdBit() const
+      {
+        if ( CompatBits::size == _compatSet.size() )
+        {
+          // Provide more bits in CompatBits::IntT
+          INT << "Need more than " << CompatBits::size << " bits to encode architectures." << endl;
+          ZYPP_THROW( Exception("Need more bits to encode architectures.") );
+        }
+        CompatBits::IntT nextBit = CompatBits::IntT(1) << (_compatSet.size());
+        return nextBit;
+      }
+
+      /** Assert each builtin Arch gets an unique _idBit when
+       *  inserted into the _compatSet.
+      */
+      const CompatEntry & assertCompatSetEntry( IdString archStr_r )
+      { return *_compatSet.insert( Arch::CompatEntry( archStr_r, nextIdBit() ) ).first; }
+
+      /** Initialize builtin Archs and set _compatBits.
+      */
+      void defCompatibleWith( IdString targetArch_r,
+                              IdString arch0_r,
+                              IdString arch1_r = IdString(),
+                              IdString arch2_r = IdString(),
+                              IdString arch3_r = IdString(),
+                              IdString arch4_r = IdString(),
+                              IdString arch5_r = IdString(),
+                              IdString arch6_r = IdString(),
+                              IdString arch7_r = IdString(),
+                              IdString arch8_r = IdString(),
+                              IdString arch9_r = IdString() )
+      {
+        const CompatEntry & target( assertCompatSetEntry( targetArch_r ) );
+        target.addCompatBit( assertCompatSetEntry( arch0_r )._idBit );
+#define _SETARG(N) if ( arch##N##_r.empty() ) return; target.addCompatBit( assertCompatSetEntry( arch##N##_r )._idBit )
+        _SETARG(1); _SETARG(2); _SETARG(3); _SETARG(4);
+        _SETARG(5); _SETARG(6); _SETARG(7); _SETARG(8); _SETARG(9);
+#undef _SETARG
+      }
+
+    private:
+      Set _compatSet;
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Arch
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const Arch Arch_empty ( IdString::Empty );
+  const Arch Arch_noarch( _noarch );
+
+  const Arch Arch_i386( _i386 );
+  const Arch Arch_i486( _i486 );
+  const Arch Arch_i586( _i586 );
+  const Arch Arch_i686( _i686 );
+  const Arch Arch_athlon( _athlon );
+  const Arch Arch_x86_64( _x86_64 );
+
+  const Arch Arch_pentium3( _pentium3 );
+  const Arch Arch_pentium4( _pentium4 );
+
+  const Arch Arch_s390( _s390 );
+  const Arch Arch_s390x( _s390x );
+
+  const Arch Arch_ppc( _ppc );
+  const Arch Arch_ppc64( _ppc64 );
+
+  const Arch Arch_ia64( _ia64 );
+
+  const Arch Arch_alphaev67( _alphaev67 );
+  const Arch Arch_alphaev6( _alphaev6 );
+  const Arch Arch_alphapca56( _alphapca56 );
+  const Arch Arch_alphaev56( _alphaev56 );
+  const Arch Arch_alphaev5( _alphaev5 );
+  const Arch Arch_alpha( _alpha );
+
+  const Arch Arch_sparc64v( _sparc64v );
+  const Arch Arch_sparc64( _sparc64 );
+  const Arch Arch_sparcv9v( _sparcv9v );
+  const Arch Arch_sparcv9( _sparcv9 );
+  const Arch Arch_sparcv8( _sparcv8 );
+  const Arch Arch_sparc( _sparc );
+
+  const Arch Arch_armv7nhl ( _armv7nhl );
+  const Arch Arch_armv7hl ( _armv7hl );
+  const Arch Arch_armv7l( _armv7l );
+  const Arch Arch_armv6l( _armv6l );
+  const Arch Arch_armv5tejl( _armv5tejl );
+  const Arch Arch_armv5tel( _armv5tel );
+  const Arch Arch_armv5l( _armv5l );
+  const Arch Arch_armv4tl( _armv4tl );
+  const Arch Arch_armv4l( _armv4l );
+  const Arch Arch_armv3l( _armv3l );
+
+  const Arch Arch_sh3( _sh3 );
+
+  const Arch Arch_sh4( _sh4 );
+  const Arch Arch_sh4a( _sh4a );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::Arch
+  //   METHOD TYPE : Ctor
+  //
+  Arch::Arch()
+  : _entry( &ArchCompatSet::instance().assertDef( _noarch ) )
+  {}
+
+  Arch::Arch( IdString::IdType id_r )
+  : _entry( &ArchCompatSet::instance().assertDef( IdString(id_r) ) )
+  {}
+
+  Arch::Arch( const IdString & idstr_r )
+  : _entry( &ArchCompatSet::instance().assertDef( idstr_r ) )
+  {}
+
+  Arch::Arch( const std::string & str_r )
+  : _entry( &ArchCompatSet::instance().assertDef( str_r ) )
+  {}
+
+  Arch::Arch( const char * cstr_r )
+  : _entry( &ArchCompatSet::instance().assertDef( cstr_r ) )
+  {}
+
+  Arch::Arch( const CompatEntry & rhs )
+  : _entry( &rhs )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::idStr
+  //   METHOD TYPE : IdString
+  //
+  IdString Arch::idStr() const
+  { return _entry->_idStr; }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::asString
+  //   METHOD TYPE : const std::string &
+  //
+  const std::string & Arch::asString() const
+  { return _entry->_archStr; }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::isBuiltIn
+  //   METHOD TYPE : bool
+  //
+  bool Arch::isBuiltIn() const
+  { return _entry->isBuiltIn(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::compatibleWith
+  //   METHOD TYPE : bool
+  //
+  bool Arch::compatibleWith( const Arch & targetArch_r ) const
+  { return _entry->compatibleWith( *targetArch_r._entry ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::baseArch
+  //   METHOD TYPE : Arch
+  //
+  Arch Arch::baseArch( ) const
+  {
+    // check the multilib archs:
+    if (Arch_x86_64.compatibleWith(*this))
+    {
+      return Arch_x86_64;
+    }
+    if (Arch_sparc64v.compatibleWith(*this))
+    {
+      return Arch_sparc64v;
+    }
+    if (Arch_sparc64.compatibleWith(*this))
+    {
+      return Arch_sparc64;
+    }
+    if (Arch_ppc64.compatibleWith(*this))
+    {
+      return Arch_ppc64;
+    }
+    if (Arch_s390x.compatibleWith(*this))
+    {
+      return Arch_s390x;
+    }
+    // Here: no multilib; return arch before noarch
+    CompatSet cset( compatSet( *this ) );
+    if ( cset.size() > 2 )     // systemArchitecture, ..., basearch, noarch
+    {
+      return *(++cset.rbegin());
+    }
+    return *this;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::compare
+  //   METHOD TYPE : bool
+  //
+  int Arch::compare( const Arch & rhs ) const
+  { return _entry->compare( *rhs._entry ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Arch::compatSet
+  //   METHOD TYPE : Arch::CompatSet
+  //
+  Arch::CompatSet Arch::compatSet( const Arch & targetArch_r )
+  {
+    Arch::CompatSet ret;
+
+    for ( ArchCompatSet::const_iterator it = ArchCompatSet::instance().begin();
+          it != ArchCompatSet::instance().end(); ++it )
+      {
+        if ( it->compatibleWith( *targetArch_r._entry ) )
+          {
+            ret.insert( Arch(*it) );
+          }
+      }
+
+    return ret;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Arch.h b/zypp/Arch.h
new file mode 100644 (file)
index 0000000..6b18a6e
--- /dev/null
@@ -0,0 +1,319 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Arch.h
+ *
+*/
+#ifndef ZYPP_ARCH_H
+#define ZYPP_ARCH_H
+
+#include <iosfwd>
+#include <functional>
+#include <set>
+#include <string>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Iterator.h"
+
+#include "zypp/IdStringType.h"
+#include "zypp/RelCompare.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Arch
+  //
+  /** Architecture.
+  */
+  class Arch
+  {
+  public:
+    /** Default ctor \ref Arc_noarch. */
+    Arch();
+
+    /** Ctor taking Arch as string. */
+    explicit Arch( IdString::IdType id_r );
+    explicit Arch( const IdString & idstr_r );
+    explicit Arch( const std::string & str_r );
+    explicit Arch( const char * cstr_r );
+
+  public:
+    /** \name IdStringType like interface.
+     * We can't use the complete \ref IdStringType mixin until
+     * _doCompare can be redefined on any level, not just as char*.
+    */
+    //@{
+    /** String representation of Arch. */
+    IdString idStr() const;
+     /** \overload */
+    const std::string & asString() const;
+    /** \overload */
+    const char * c_str() const
+    { return asString().c_str(); }
+
+    /** Test for an empty Arch (this is \ref Arch_epmty, not \ref Arch_noarch ). */
+    bool empty() const
+    { return asString().empty(); }
+
+    /** Size of the string representation. */
+    unsigned size() const
+    { return asString().size(); }
+
+    /** Expert backdoor. */
+    IdString::IdType id() const
+    { return idStr().id(); }
+    //@}
+
+  public:
+    /** Whether this is a buitin (or known) architecture.
+     * Used e.g. in \ref Capability to determine whether
+     * some trailing \c ".string" is part ot the name or
+     * restriction to an architecture.
+    */
+    bool isBuiltIn() const;
+
+  public:
+    /** Compatibility relation.
+     * \return \c True iff \c this is compatible with \a targetArch_r.
+     * \code
+     * Arch_noarch.compatibleWith( ... )       ==> always true;
+     * Arch_i686.compatibleWith( Arch_x86_64 ) ==> true;
+     * Arch_x86_64.compatibleWith( Arch_i686 ) ==> false;
+     * \endcode
+    */
+    bool compatibleWith( const Arch & targetArch_r ) const;
+
+    /**
+     * \return the arch before noarch if it's not a multilib arch
+     * (e.g. x86_64,sparc64v,sparc64,ppc64,s390x).
+    */
+    Arch baseArch() const;
+
+    /** \overload static version. */
+    static Arch baseArch( const Arch & targetArch_r )
+    { return targetArch_r.baseArch(); }
+
+    /** Arch comparison.
+     * Compatible architectures are treated as \c less (i.e. <tt>i686>i386>noarch</tt>).
+     * So \c Arch_noarch is the least Arch. Equivalent architectures
+     * (compatible in both directions) are ordered arbitrary.
+     */
+    int compare( const Arch & rhs ) const;
+
+    /** Arch comparison (static version). */
+    static int compare( const Arch & lhs, const Arch & rhs )
+    { return lhs.compare( rhs ); }
+
+  public:
+    /** Reversed arch order, best Arch first. */
+    typedef std::set<Arch,CompareByGT<Arch> > CompatSet;
+
+    /** Return a set of all Arch's \ref compatibleWith a \a targetArch_r.
+     * \note The set is ordered according to compare, thus iterating
+     * will start at \a targetArch_r and end with \c Arch_noarch.
+     * \code
+     * Arch::CompatSet cset( Arch::compatSet( Arch_x86_64 ) );
+     *
+     * cout << str::join( make_transform_iterator( cset.begin(), std::mem_fun_ref(&Arch::asString) ),
+     *                    make_transform_iterator( cset.end(), std::mem_fun_ref(&Arch::asString) ) )
+     *      << endl;
+     *
+     * // Prints: x86_64 athlon i686 i586 i486 i386 noarch
+     * \endcode
+    */
+    static CompatSet compatSet( const Arch & targetArch_r );
+
+    /** */
+    static std::string asString( const CompatSet & cset )
+    {
+      return str::join( make_transform_iterator( cset.begin(), std::mem_fun_ref(&Arch::asString) ),
+                        make_transform_iterator( cset.end(), std::mem_fun_ref(&Arch::asString) ) );
+    }
+
+  public:
+    struct CompatEntry;
+  private:
+    Arch( const CompatEntry & );
+    const CompatEntry * _entry;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \name Builtin architecture constants.
+   *
+   * Defined outside Arch as e.g. \c Arch_i386, because some names,
+   * like \c i388, are used as \c #define, thus unusable as identifier
+   * like \c Arch::i386.
+  */
+  //@{
+  /** \relates Arch
+   * This is an empty \ref Arch represented by an empty string.
+   * Sometimes used to indicate an any or an unknown Arch. Don't
+   * confuse this with \ref Arch_noarch, which is in fact an
+   * architecture.
+  */
+  extern const Arch Arch_empty;
+
+  /** \relates Arch */
+  extern const Arch Arch_noarch;
+
+  /** \relates Arch */
+  extern const Arch Arch_pentium4;
+  /** \relates Arch */
+  extern const Arch Arch_pentium3;
+
+  /** \relates Arch */
+  extern const Arch Arch_x86_64;
+  /** \relates Arch */
+  extern const Arch Arch_athlon;
+  /** \relates Arch */
+  extern const Arch Arch_i686;
+  /** \relates Arch */
+  extern const Arch Arch_i586;
+  /** \relates Arch */
+  extern const Arch Arch_i486;
+  /** \relates Arch */
+  extern const Arch Arch_i386;
+
+  /** \relates Arch */
+  extern const Arch Arch_s390x;
+  /** \relates Arch */
+  extern const Arch Arch_s390;
+
+  /** \relates Arch */
+  extern const Arch Arch_ppc64;
+  /** \relates Arch */
+  extern const Arch Arch_ppc;
+
+  /** \relates Arch */
+  extern const Arch Arch_ia64;
+
+  /** \relates Arch */
+  extern const Arch Arch_alphaev67;
+  /** \relates Arch */
+  extern const Arch Arch_alphaev6;
+  /** \relates Arch */
+  extern const Arch Arch_alphapca56;
+  /** \relates Arch */
+  extern const Arch Arch_alphaev56;
+  /** \relates Arch */
+  extern const Arch Arch_alphaev5;
+  /** \relates Arch */
+  extern const Arch Arch_alpha;
+
+   /** \relates Arch */
+  extern const Arch Arch_sparc64v;
+  /** \relates Arch */
+  extern const Arch Arch_sparc64;
+  /** \relates Arch */
+  extern const Arch Arch_sparcv9v;
+  /** \relates Arch */
+  extern const Arch Arch_sparcv9;
+  /** \relates Arch */
+  extern const Arch Arch_sparcv8;
+  /** \relates Arch */
+  extern const Arch Arch_sparc;
+
+  /** \relates Arch */
+  extern const Arch Arch_armv7nhl;
+  /** \relates Arch */
+  extern const Arch Arch_armv7hl;
+  /** \relates Arch */
+  extern const Arch Arch_armv7l;
+  /** \relates Arch */
+  extern const Arch Arch_armv6l;
+  /** \relates Arch */
+  extern const Arch Arch_armv5tejl;
+  /** \relates Arch */
+  extern const Arch Arch_armv5tel;
+  /** \relates Arch */
+  extern const Arch Arch_armv5l;
+  /** \relates Arch */
+  extern const Arch Arch_armv4tl;
+  /** \relates Arch */
+  extern const Arch Arch_armv4l;
+  /** \relates Arch */
+  extern const Arch Arch_armv3l;
+
+   /** \relates Arch */
+  extern const Arch Arch_sh3;
+
+  /** \relates Arch */
+  extern const Arch Arch_sh4;
+  /** \relates Arch */
+  extern const Arch Arch_sh4a;
+  //@}
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Arch stream output. */
+  inline std::ostream & operator<<( std::ostream & str, const Arch & obj )
+  { return str << obj.asString(); }
+
+  /** \name Equality based on string value. */
+  //@{
+  /** \relates Arch */
+  inline bool operator==( const Arch & lhs, const Arch & rhs )
+  { return lhs.asString() == rhs.asString(); }
+
+  /** \relates Arch */
+  inline bool operator==( const Arch & lhs, const std::string & rhs )
+  { return lhs.asString() == rhs; }
+
+  /** \relates Arch */
+  inline bool operator==( const std::string & lhs, const Arch & rhs )
+  { return lhs == rhs.asString(); }
+
+  /** \relates Arch */
+  inline bool operator!=( const Arch & lhs, const Arch & rhs )
+  { return !( lhs == rhs ); }
+
+  /** \relates Arch */
+  inline bool operator!=( const Arch & lhs, const std::string & rhs )
+  { return !( lhs == rhs ); }
+
+  /** \relates Arch */
+  inline bool operator!=( const std::string & lhs, const Arch & rhs )
+  { return !( lhs == rhs ); }
+  //@}
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** Functor finding compatible architectures.
+   * \see Arch::compatibleWith
+  */
+  struct ArchCompatibleWith : public std::unary_function<Arch,bool>
+  {
+    /** The target architecture */
+    Arch _targetArch;
+    /** Ctor taking the target architecture */
+    ArchCompatibleWith( const Arch & targetArch_r )
+    : _targetArch( targetArch_r )
+    {}
+    /** Call Arch::compatibleWith ( \c _targetArch ) on \a rhs. */
+    bool operator()( const Arch & rhs ) const
+    { return rhs.compatibleWith( _targetArch ); }
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+  /** \relates zypp::Arch Default order for std::container based Arch::compare.*/
+  template<>
+    inline bool less<zypp::Arch>::operator()( const zypp::Arch & lhs, const zypp::Arch & rhs ) const
+    { return lhs.compare( rhs ) < 0; }
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ARCH_H
diff --git a/zypp/AutoDispose.h b/zypp/AutoDispose.h
new file mode 100644 (file)
index 0000000..bb01549
--- /dev/null
@@ -0,0 +1,200 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/AutoDispose.h
+ *
+*/
+#ifndef ZYPP_AUTODISPOSE_H
+#define ZYPP_AUTODISPOSE_H
+
+#include <iosfwd>
+#include <boost/call_traits.hpp>
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : AutoDispose<_Tp>
+  //
+  /** Reference counted access to a \c _Tp object calling a custom
+   *  \c Dispose function when the last AutoDispose handle to it is
+   *  destroyed or reset.
+   *
+   * \note As with pointers, constness of an \c AutoDispose object does
+   * \b not apply to the stored \c _Tp object. If the stored \c _Tp object
+   * should be immutable, you should use <tt>AutoDispose\<const _Tp\></tt>.
+   *
+   * Pass a filename to the application and provide the appropriate
+   * code to be executed when the file is no longer needed:
+   * \code
+   * struct FileCache
+   * {
+   *   Pathname getFile();
+   *   void     releaseFile( const Pathname & );
+   * };
+   *
+   * static FileCache cache;
+   *
+   * void unlink( const Pathname & file_r );
+   *
+   * AutoDispose<const Pathname> provideFile( ... )
+   * {
+   *   if ( file_is_in_cache )
+   *     {
+   *       // will call 'cache.releaseFile( file )'
+   *       return AutoDispose<const Pathname>( cache.getFile(),
+   *                                           bind( &FileCache::releaseFile, ref(cache), _1 ) );
+   *     }
+   *   else if ( file_is_temporary )
+   *     {
+   *       // will call 'unlink( file )'
+   *       return AutoDispose<const Pathname>( file, unlink );
+   *     }
+   *   else if ( file_is_permanent )
+   *     {
+   *       // will do nothing.
+   *       return AutoDispose<const Pathname>( file );
+   *     }
+   *   else
+   *     {
+   *       // will do nothing.
+   *       return AutoDispose<const Pathname>();
+   *     }
+   * }
+   * \endcode
+   *
+   * Exception safe handling of temporary files:
+   * \code
+   * void provideFileAt( const Pathname & destination )
+   * {
+   *   AutoDispose<const Pathname> guard( destination, unlink );
+   *
+   *   // Any exception here will lead to 'unlink( destination )'
+   *   // ...
+   *
+   *   // On success: reset the dispose function to NOOP.
+   *   guard.resetDispose();
+   * }
+   * \endcode
+  */
+  template<class _Tp>
+    class AutoDispose
+    {
+    public:
+      typedef typename boost::call_traits<_Tp>::param_type       param_type;
+      typedef typename boost::call_traits<_Tp>::reference        reference;
+      typedef typename boost::call_traits<_Tp>::const_reference  const_reference;
+      typedef _Tp                                                value_type;
+      typedef typename boost::call_traits<_Tp>::value_type       result_type;
+
+    public:
+      /** Dispose function signatue. */
+      typedef function<void ( param_type )> Dispose;
+
+    public:
+      /** Default Ctor using default constructed value and no dispose function. */
+      AutoDispose()
+      : _pimpl( new Impl( value_type() ) )
+      {}
+
+      /** Ctor taking dispose function and using default constructed value. */
+      explicit AutoDispose( const Dispose & dispose_r )
+      : _pimpl( new Impl( value_type(), dispose_r ) )
+      {}
+
+      /** Ctor taking value and no dispose function. */
+      explicit AutoDispose( param_type value_r )
+      : _pimpl( new Impl( value_r ) )
+      {}
+
+      /** Ctor taking value and dispose function. */
+      AutoDispose( param_type value_r, const Dispose & dispose_r )
+      : _pimpl( new Impl( value_r, dispose_r ) )
+      {}
+
+    public:
+
+      /** Provide implicit conversion to \c _Tp\&. */
+      operator reference() const
+      { return _pimpl->_value; }
+
+      /** Reference to the \c _Tp object. */
+      reference value() const
+      { return _pimpl->_value; }
+
+      /** Reference to the \c _Tp object. */
+      reference operator*() const
+      { return _pimpl->_value; }
+
+      /** Pointer to the \c _Tp object (asserted to be <tt>!= NULL</tt>). */
+      value_type * operator->() const
+      { return & _pimpl->_value; }
+
+      /** Reset to default Ctor values. */
+      void reset()
+      { AutoDispose().swap( *this ); }
+
+      /** Exchange the contents of two AutoDispose objects. */
+      void swap( AutoDispose & rhs )
+      { _pimpl.swap( rhs._pimpl ); }
+
+    public:
+      /** Return the current dispose function. */
+      const Dispose & getDispose() const
+      { return _pimpl->_dispose; }
+
+      /** Set a new dispose function. */
+      void setDispose( const Dispose & dispose_r )
+      { _pimpl->_dispose = dispose_r; }
+
+      /** Set no dispose function. */
+      void resetDispose()
+      { setDispose( Dispose() ); }
+
+      /** Exchange the dispose function. +*/
+      void swapDispose( Dispose & dispose_r )
+      { _pimpl->_dispose.swap( dispose_r ); }
+
+    private:
+      struct Impl : private base::NonCopyable
+      {
+        Impl( param_type value_r )
+        : _value( value_r )
+        {}
+        Impl( param_type value_r, const Dispose & dispose_r )
+        : _value( value_r )
+        , _dispose( dispose_r )
+        {}
+        ~Impl()
+        {
+          if ( _dispose )
+            try { _dispose( _value ); } catch(...) {}
+        }
+        value_type _value;
+        Dispose    _dispose;
+      };
+
+      shared_ptr<Impl> _pimpl;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates AutoDispose<_Tp> Stream output of the \c _Tp object. */
+  template<class _Tp>
+    inline std::ostream & operator<<( std::ostream & str, const AutoDispose<_Tp> & obj )
+    { return str << obj.value(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_AUTODISPOSE_H
diff --git a/zypp/Bit.h b/zypp/Bit.h
new file mode 100644 (file)
index 0000000..c12d0a1
--- /dev/null
@@ -0,0 +1,354 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Bit.h
+ *
+*/
+#ifndef ZYPP_BIT_H
+#define ZYPP_BIT_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/SafeBool.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+  /**
+   * \todo Use boost::mpl library to assert constraints
+   * at compiletime! There various like (_IntT is an integral type)
+   * (begin+size < maxbits) or ( field dependent
+   * constants must be within the range defined by size ).
+  */
+  namespace bit
+  { /////////////////////////////////////////////////////////////////
+
+    namespace bit_detail
+    {
+      /** Generate constants with \a _size trailing '1'-bits */
+      template<class _IntT, unsigned _size>
+        struct Gen1Bits
+        {
+          static const _IntT value = (Gen1Bits<_IntT,_size-1>::value << 1)+1;
+        };
+      /** Specialization for \a _length 0 */
+      template<class _IntT>
+        struct Gen1Bits<_IntT, 0>
+        {
+          static const _IntT value = 0;
+        };
+    }
+
+    /** Number of bits available in \a _IntT. */
+    template<class _IntT>
+      struct MaxBits
+      {
+        typedef _IntT IntT;
+        static const unsigned value = (sizeof(IntT)*8);
+      };
+
+    /** For printing bits. */
+    template<class _IntT>
+      inline std::string asString( _IntT val, char zero = '0', char one = '1' )
+      {
+        std::string s( MaxBits<_IntT>::value, zero );
+        for( unsigned i = MaxBits<_IntT>::value; i; )
+          {
+            --i;
+            if ( val & (_IntT)1 )
+              s[i] = one;
+            val = val >> 1;
+          };
+        return s;
+      }
+
+    /** A bitmaks of \a _size 1-bits starting at bit \a _begin. */
+    template<class _IntT, unsigned _begin, unsigned _size>
+      struct Mask
+      {
+        typedef _IntT IntT;
+        static const IntT value    = bit_detail::Gen1Bits<IntT,_size>::value << _begin;
+        static const IntT inverted = ~value;
+      };
+
+    /** Range of bits starting at bit \a _begin with length \a _size. */
+    template<class _IntT, unsigned _begin, unsigned _size>
+      struct Range
+      {
+        typedef _IntT IntT;
+        typedef zypp::bit::MaxBits<IntT>           MaxBits;
+        typedef zypp::bit::Mask<IntT,_begin,_size> Mask;
+
+        static const unsigned begin  = _begin;
+        static const unsigned size   = _size;
+        static const unsigned end    = _begin + _size;
+      };
+    /** Range specialisation for (illegal) zero \a _size.
+     * Force error at compiletime. Currently because types
+     * and values are undefined
+    */
+    template<class _IntT, unsigned _begin>
+      struct Range<_IntT, _begin, 0>
+      {};
+
+    /** A value with in a Range.
+     * \code
+     * typedef Range<char,2,3> SubField; // bits 2,3,4 in a char field
+     * SubField::Mask::value;            // 00011100
+     * RangeValue<SubField,0>::value;    // 00000000
+     * RangeValue<SubField,1>::value;    // 00000100
+     * RangeValue<SubField,2>::value;    // 00001000
+     * RangeValue<SubField,3>::value;    // 00001100
+     * \endcode
+    */
+    template<class _Range, typename _Range::IntT _value>
+      struct RangeValue
+      {
+        typedef _Range                RangeT;
+        typedef typename _Range::IntT IntT;
+
+        static const IntT value = _value << RangeT::begin;
+      };
+
+    /** A single 1-bit within a Range.
+     * \code
+     * typedef Range<char,2,3> SubField; // bits 2,3,4 in a char field
+     * SubField::Mask::value;            // 00011100
+     * RangeBit<SubField,0>::value;      // 00000100
+     * RangeBit<SubField,1>::value;      // 00001000
+     * RangeBit<SubField,2>::value;      // 00010000
+     * \endcode
+    */
+    template<class _Range, unsigned _pos>
+      struct RangeBit
+      {
+        typedef _Range                RangeT;
+        typedef typename _Range::IntT IntT;
+
+        static const IntT value = IntT(1) << (RangeT::begin + _pos);
+      };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : BitField
+    //
+    /** An integral type used as BitField.
+     *
+     * Most methods exist as templated and nontemplated
+     * version. The nontemplated operates on the complete
+     * BitField, while the tamplated ones are restricted
+     * to the given Range.
+     * \code
+     * BitField<char> bf;                // 00000000
+     * typedef Range<char,2,3> SubField; // bits 2,3,4 in a char field
+     *
+     * bf<SubField>.assign( -1 );        // assign SubField in -1
+     *                                   // to SubField in bf.
+     *                                   // 00011100
+     * bf.assign( -1 );                  // assign -1 to bf
+     *                                   // 11111111
+     * bf<SubField>.assign( 0 );         // 11100011
+     * \endcode
+    */
+    template<class _IntT>
+      class BitField  : public Range<_IntT, 0, MaxBits<_IntT>::value>
+                      , private base::SafeBool<BitField<_IntT> >
+      {
+        typedef typename base::SafeBool<BitField<_IntT> >::bool_type bool_type;
+
+      public:
+        /** Default ctor: zero. */
+        BitField()
+        : _value( (_IntT)0 )
+        {}
+        /** Ctor taking an \a _IntT. */
+        BitField( const _IntT & value_r )
+        : _value( value_r )
+        {}
+
+      public:
+        /** Validate in a boolean context. */
+        using base::SafeBool<BitField<_IntT> >::operator bool_type;
+
+      public:
+        /** Return the value. */
+        template<class _Range>
+          _IntT value() const
+          {
+            return _value & _Range::Mask::value;
+          }
+        _IntT value() const
+        {
+          return _value;
+        }
+
+        /** Value as bit string. */
+        template<class _Range>
+          std::string asString() const
+          {
+            return bit::asString( _value & _Range::Mask::value, '_' );
+          }
+        std::string asString() const
+        {
+          return bit::asString( _value, '_' );
+        }
+
+        /** Assign Range in \a rhs to \c this. */
+        template<class _Range>
+          BitField & assign( _IntT rhs )
+          {
+            _value = (_value & _Range::Mask::inverted)
+                   | (rhs & _Range::Mask::value);
+            return *this;
+          }
+        BitField & assign( _IntT rhs )
+        {
+          _value = rhs;
+          return *this;
+        }
+
+        /** Test for equal value within a Range. */
+        template<class _Range>
+          bool isEqual( _IntT rhs ) const
+          {
+            return (_value & _Range::Mask::value)
+                == (rhs & _Range::Mask::value);
+          }
+        bool isEqual( _IntT rhs ) const
+        {
+          return _value == rhs;
+        }
+
+       public:
+
+         /** Set or unset bits of \a rhs. */
+        template<class _Range>
+            BitField & set( _IntT rhs, bool doset_r )
+            { return set( (rhs & _Range::Mask::value), doset_r ); }
+
+        BitField & set( _IntT rhs, bool doset_r )
+        { return doset_r ? set( rhs ) : unset( rhs ); }
+
+        /** Set bits of \a rhs. */
+        template<class _Range>
+            BitField & set( _IntT rhs )
+            { return set( rhs & _Range::Mask::value ); }
+
+        BitField & set( _IntT rhs )
+        { _value |= rhs; return *this; }
+
+        /** Unset bits of \a rhs. */
+        template<class _Range>
+            BitField & unset( _IntT rhs )
+            { return unset( rhs & _Range::Mask::value ); }
+
+        BitField & unset( _IntT rhs )
+        { _value &= ~rhs; return *this; }
+
+        /** Test whether \b all bits of \a rhs are set. */
+        template<class _Range>
+            bool test( _IntT rhs )
+            { return test( rhs & _Range::Mask::value ); }
+
+        bool test( _IntT rhs ) const
+        { return (_value & rhs) == rhs; }
+
+        /** Test whether \b at \b least \b one bit of \a rhs is set. */
+        template<class _Range>
+            bool testAnyOf( _IntT rhs )
+            { return testAnyOf( rhs & _Range::Mask::value ); }
+
+        bool testAnyOf( _IntT rhs ) const
+        { return (_value & rhs); }
+
+      public:
+
+        BitField & operator=( const BitField & rhs )
+        { _value = rhs._value; return *this; }
+
+        BitField & operator&=( const BitField & rhs )
+        { _value &= rhs._value; return *this; }
+
+        BitField & operator|=( const BitField & rhs )
+        { _value |= rhs._value; return *this; }
+
+        BitField & operator^=( const BitField & rhs )
+        { _value ^= rhs._value; return *this; }
+
+        BitField & operator<<=( unsigned num )
+        { _value <<= num; return *this; }
+
+        BitField & operator>>=( unsigned num )
+        { _value >>= num; return *this; }
+
+        BitField operator~() const
+        { return ~_value; }
+
+      private:
+        friend base::SafeBool<BitField<_IntT> >::operator bool_type() const;
+        /** \ref base::SafeBool test. */
+        bool boolTest() const
+        { return _value; }
+
+      private:
+        _IntT _value;
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates BitField Stream output */
+    template<class _IntT>
+      std::ostream & operator<<( std::ostream & str, const BitField<_IntT> & obj )
+      {
+        return str << obj.asString();
+      }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline bool operator==( const BitField<_IntT> & lhs, const BitField<_IntT> & rhs )
+      { return lhs.value() == rhs.value(); }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline bool operator!=( const BitField<_IntT> & lhs, const BitField<_IntT> & rhs )
+      { return ! (lhs == rhs); }
+
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline BitField<_IntT> operator&( const BitField<_IntT> & lhs, const BitField<_IntT> & rhs )
+      { return BitField<_IntT>(lhs) &= rhs; }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline BitField<_IntT> operator|( const BitField<_IntT> & lhs, const BitField<_IntT> & rhs )
+      { return BitField<_IntT>(lhs) |= rhs; }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline BitField<_IntT> operator^( const BitField<_IntT> & lhs, const BitField<_IntT> & rhs )
+      { return BitField<_IntT>(lhs) ^= rhs; }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline BitField<_IntT> operator<<( const BitField<_IntT> & lhs, unsigned num )
+      { return BitField<_IntT>(lhs) <<= num; }
+
+    /** \relates BitField */
+    template<class _IntT>
+      inline BitField<_IntT> operator>>( const BitField<_IntT> & lhs, unsigned num )
+      { return BitField<_IntT>(lhs) >>= num; }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace bit
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BIT_H
diff --git a/zypp/ByteCount.cc b/zypp/ByteCount.cc
new file mode 100644 (file)
index 0000000..bae1e7d
--- /dev/null
@@ -0,0 +1,105 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ByteCount.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/ByteCount.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const ByteCount::Unit ByteCount::B( 1LL, "B", 0 );
+
+  const ByteCount::Unit ByteCount::K( 1024LL, "KiB", 1 );
+  const ByteCount::Unit ByteCount::KiB( K );
+  const ByteCount::Unit ByteCount::M( 1048576LL, "MiB", 1 );
+  const ByteCount::Unit ByteCount::MiB( M );
+  const ByteCount::Unit ByteCount::G( 1073741824LL, "GiB", 2 );
+  const ByteCount::Unit ByteCount::GiB( G );
+  const ByteCount::Unit ByteCount::T( 1099511627776LL, "TiB", 3 );
+  const ByteCount::Unit ByteCount::TiB( T );
+
+  const ByteCount::Unit ByteCount::kB( 1000LL, "kB", 1 );
+  const ByteCount::Unit ByteCount::MB( 1000000LL, "MB", 1 );
+  const ByteCount::Unit ByteCount::GB( 1000000000LL, "GB", 2 );
+  const ByteCount::Unit ByteCount::TB( 1000000000000LL, "TB", 3 );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ByteCount::fillBlock
+  //   METHOD TYPE : ByteCount &
+  //
+  ByteCount & ByteCount::fillBlock( ByteCount blocksize_r )
+  {
+    if ( _count && blocksize_r )
+      {
+        SizeType diff = _count % blocksize_r;
+        if ( diff )
+          {
+            if ( _count > 0 )
+              {
+                _count += blocksize_r;
+                _count -= diff;
+              }
+            else
+              {
+                _count -= blocksize_r;
+                _count += diff;
+              }
+          }
+      }
+    return *this;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ByteCount::bestUnit
+  //   METHOD TYPE : ByteCount::Unit
+  //
+  const ByteCount::Unit & ByteCount::bestUnit() const
+  {
+    SizeType usize( _count < 0 ? -_count : _count );
+    if ( usize < K.factor() )
+      return B;
+    if ( usize < M.factor() )
+      return K;
+    if ( usize < G.factor() )
+      return M;
+    if ( usize < T.factor() )
+      return G;
+    return T;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ByteCount::bestUnit1000
+  //   METHOD TYPE : ByteCount::Unit
+  //
+  const ByteCount::Unit & ByteCount::bestUnit1000() const
+  {
+    SizeType usize( _count < 0 ? -_count : _count );
+    if ( usize < kB.factor() )
+      return B;
+    if ( usize < MB.factor() )
+      return kB;
+    if ( usize < GB.factor() )
+      return MB;
+    if ( usize < TB.factor() )
+      return GB;
+    return TB;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ByteCount.h b/zypp/ByteCount.h
new file mode 100644 (file)
index 0000000..657c5f5
--- /dev/null
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ByteCount.h
+ *
+*/
+#ifndef ZYPP_BYTECOUNT_H
+#define ZYPP_BYTECOUNT_H
+
+#include <iosfwd>
+
+#include "zypp/base/Unit.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ByteCount
+  //
+  /** Store and operate with byte count.
+   *
+  */
+  class ByteCount
+  {
+    friend std::ostream & operator<<( std::ostream & str, const ByteCount & obj );
+
+  public:
+
+    typedef base::Unit      Unit;
+    typedef Unit::ValueType SizeType;
+
+    /** \name Byte unit constants. */
+    //@{
+    /** 1 Byte */
+    static const Unit B;
+
+    /** 1024 Byte */
+    static const Unit K;
+    static const Unit KiB;
+    /** 1024^2 Byte */
+    static const Unit M;
+    static const Unit MiB;
+    /** 1024^3 Byte */
+    static const Unit G;
+    static const Unit GiB;
+    /** 1024^4 Byte */
+    static const Unit T;
+    static const Unit TiB;
+
+    /** 1000 Byte */
+    static const Unit kB;
+    /** 1000^2 Byte */
+    static const Unit MB;
+    /** 1000^3 Byte */
+    static const Unit GB;
+    /** 1000^4 Byte */
+    static const Unit TB;
+    //@}
+
+  public:
+
+    /** Default ctor */
+    ByteCount()
+    : _count( 0 )
+    {}
+    /** Ctor taking 1 Unit. */
+    ByteCount( const Unit & unit_r )
+    : _count( unit_r.factor() )
+    {}
+    /** Ctor taking a count and optinal Unit. */
+    ByteCount( const SizeType count_r, const Unit & unit_r = B )
+    : _count( count_r * unit_r.factor() )
+    {}
+
+ public:
+
+    /** Conversion to SizeType. */
+    operator SizeType() const
+    { return _count; }
+
+    /** \name Arithmetic operations.
+     * \c + \c - \c * \c / are provided via conversion to SizeType.
+    */
+    //@{
+    ByteCount & operator+=( const SizeType rhs ) { _count += rhs; return *this; }
+    ByteCount & operator-=( const SizeType rhs ) { _count -= rhs; return *this; }
+    ByteCount & operator*=( const SizeType rhs ) { _count *= rhs; return *this; }
+    ByteCount & operator/=( const SizeType rhs ) { _count /= rhs; return *this; }
+
+    ByteCount & operator++(/*prefix*/) { _count += 1; return *this; }
+    ByteCount & operator--(/*prefix*/) { _count -= 1; return *this; }
+
+    ByteCount operator++(int/*postfix*/) { return _count++; }
+    ByteCount operator--(int/*postfix*/) { return _count--; }
+    //@}
+
+    /** Adjust count to multiple of \a blocksize_r (default 1K).
+     * Zero blocksize_r is treated as 1B.
+    */
+    ByteCount & fillBlock( ByteCount blocksize_r = K );
+
+    /** Return count adjusted to multiple of \a blocksize_r (default 1K). */
+    ByteCount fullBlocks( ByteCount blocksize_r = K ) const
+    { return ByteCount(*this).fillBlock( blocksize_r ); }
+
+    /** Return number of blocks of size \a blocksize_r (default 1K). */
+    SizeType blocks( ByteCount blocksize_r = K ) const
+    { return fullBlocks( blocksize_r ) / blocksize_r; }
+
+  public:
+
+    /** Return the best Unit (B,K,M,G,T) for count. */
+    const Unit & bestUnit() const;
+
+    /** Return the best Unit (B,kB,MB,GB,TB) for count. */
+    const Unit & bestUnit1000() const;
+
+    /** \name Conversion to string.
+     * \li \a field_width_r Width for the number part (incl. precision)
+     * \li \a unit_width_r With for the unit symbol (without symbol if zero)
+     * \li \a prec_r Precision to use.
+     * \see zypp::base::Unit
+    */
+    //@{
+    /** Auto selected Unit and precision. */
+    std::string asString( unsigned field_width_r = 0,
+                          unsigned unit_width_r  = 1 ) const
+    { return asString( bestUnit(), field_width_r, unit_width_r ); }
+    /** Auto selected Unit. */
+    std::string asString( unsigned field_width_r,
+                          unsigned unit_width_r,
+                          unsigned prec_r ) const
+    { return asString( bestUnit(), field_width_r, unit_width_r, prec_r ); }
+    /** Auto selected precision. */
+    std::string asString( const Unit & unit_r,
+                          unsigned field_width_r = 0,
+                          unsigned unit_width_r  = 1 ) const
+    { return asString( unit_r, field_width_r, unit_width_r, unit_r.prec() ); }
+    /** Nothing auto selected. */
+    std::string asString( const Unit & unit_r,
+                          unsigned field_width_r,
+                          unsigned unit_width_r,
+                          unsigned prec_r ) const
+    { return unit_r.form( _count, field_width_r, unit_width_r, prec_r ); }
+    //@}
+
+  private:
+    SizeType _count;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ByteCount Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const ByteCount & obj )
+  { return str << obj.asString(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BYTECOUNT_H
diff --git a/zypp/CMakeLists.txt b/zypp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..61383b7
--- /dev/null
@@ -0,0 +1,951 @@
+####################################################################
+# SOURCES                                                          #
+####################################################################
+
+ADD_DEFINITIONS(-DLOCALEDIR="${CMAKE_INSTALL_PREFIX}/share/locale" -DTEXTDOMAIN="zypp" )
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+#FILE(WRITE filename "message to write"... )
+
+SET( zypp_SRCS
+  ByteCount.cc
+  Capabilities.cc
+  Capability.cc
+  CapMatch.cc
+  Changelog.cc
+  CheckSum.cc
+  Date.cc
+  Dep.cc
+  Digest.cc
+  DiskUsage.cc
+  DiskUsageCounter.cc
+  DownloadMode.cc
+  Edition.cc
+  ExternalProgram.cc
+  PluginFrame.cc
+  PluginFrameException.cc
+  PluginScript.cc
+  PluginScriptException.cc
+  Fetcher.cc
+  FileChecker.cc
+  Glob.cc
+  HistoryLog.cc
+  HistoryLogData.cc
+  IdString.cc
+  InstanceId.cc
+  KeyRing.cc
+  Locks.cc
+  MediaProducts.cc
+  MediaSetAccess.cc
+  OnMediaLocation.cc
+  Package.cc
+  Patch.cc
+  PathInfo.cc
+  Pathname.cc
+  Pattern.cc
+  PoolItem.cc
+  PoolItemBest.cc
+  PoolQuery.cc
+  PoolQueryResult.cc
+  ProblemSolution.cc
+  Product.cc
+  ProgressData.cc
+  ProvideFilePolicy.cc
+  PublicKey.cc
+  Range.cc
+  Rel.cc
+  RepoInfo.cc
+  RepoManager.cc
+  Repository.cc
+  RepoStatus.cc
+  ResKind.cc
+  ResObject.cc
+  Resolvable.cc
+  Resolver.cc
+  ResolverProblem.cc
+  ResPool.cc
+  ResPoolProxy.cc
+  ResStatus.cc
+  ServiceInfo.cc
+  Signature.cc
+  SrcPackage.cc
+  SysContent.cc
+  Target.cc
+  TmpPath.cc
+  Url.cc
+  VendorAttr.cc
+  VendorSupportOptions.cc
+  ZYpp.cc
+  ZYppCommitPolicy.cc
+  ZYppCommitResult.cc
+  ZYppFactory.cc
+)
+SET( zypp_EARLY_SRCS
+  ZConfig.cc
+  Arch.cc
+  Locale.cc
+  CountryCode.cc
+  LanguageCode.cc
+)
+
+SET( zypp_HEADERS
+  Arch.h
+  AutoDispose.h
+  Bit.h
+  ByteCount.h
+  Callback.h
+  Capabilities.h
+  Capability.h
+  CapMatch.h
+  Changelog.h
+  CheckSum.h
+  CountryCode.h
+  Date.h
+  Dep.h
+  Digest.h
+  DiskUsageCounter.h
+  DiskUsage.h
+  DownloadMode.h
+  Edition.h
+  ExternalProgram.h
+  PluginFrame.h
+  PluginFrameException.h
+  PluginScript.h
+  PluginScriptException.h
+  Fetcher.h
+  FileChecker.h
+  Glob.h
+  HistoryLog.h
+  HistoryLogData.h
+  IdString.h
+  IdStringType.h
+  InstanceId.h
+  KeyContext.h
+  KeyRing.h
+  KVMap.h
+  LanguageCode.h
+  Locale.h
+  Locks.h
+  ManagedFile.h
+  MediaProducts.h
+  MediaSetAccess.h
+  Vendor.h
+  OnMediaLocation.h
+  Package.h
+  PackageKeyword.h
+  Patch.h
+  PathInfo.h
+  Pathname.h
+  Pattern.h
+  PoolItem.h
+  PoolItemBest.h
+  PoolQuery.h
+  PoolQueryUtil.tcc
+  PoolQueryResult.h
+  ProblemSolution.h
+  ProblemTypes.h
+  Product.h
+  ProgressData.h
+  ProvideFilePolicy.h
+  PublicKey.h
+  Range.h
+  RelCompare.h
+  Rel.h
+  RepoInfo.h
+  RepoManager.h
+  Repository.h
+  RepoStatus.h
+  Filter.h
+  ResFilters.h
+  ResKind.h
+  ResObject.h
+  ResObjects.h
+  Resolvable.h
+  Resolver.h
+  ResolverProblem.h
+  ResPool.h
+  ResPoolProxy.h
+  ResStatus.h
+  ResTraits.h
+  ServiceInfo.h
+  Signature.h
+  SrcPackage.h
+  SysContent.h
+  Target.h
+  TmpPath.h
+  TriBool.h
+  Url.h
+  VendorAttr.h
+  VendorSupportOptions.h
+  ZConfig.h
+  ZYppCallbacks.h
+  ZYppCommit.h
+  ZYppCommitPolicy.h
+  ZYppCommitResult.h
+  ZYppFactory.h
+  ZYpp.h
+)
+
+INSTALL(  FILES ${zypp_HEADERS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/zypp" )
+
+SET( zypp_base_SRCS
+  base/InterProcessMutex.cc
+  base/SerialNumber.cc
+  base/Random.cc
+  base/Measure.cc
+  base/Fd.cc
+  base/Gettext.cc
+  base/GzStream.cc
+  base/IOStream.cc
+  base/InputStream.cc
+  base/ReferenceCounted.cc
+  base/String.cc
+  base/Regex.cc
+  base/Unit.cc
+  base/ExternalDataSource.cc
+  base/Exception.cc
+  base/UserRequestException.cc
+  base/Sysconfig.cc
+  base/ProfilingFormater.cc
+  base/LogControl.cc
+)
+
+SET( zypp_base_HEADERS
+  base/InterProcessMutex.h
+  base/Collector.h
+  base/SerialNumber.h
+  base/Easy.h
+  base/Errno.h
+  base/Random.h
+  base/Algorithm.h
+  base/Counter.h
+  base/Debug.h
+  base/DefaultIntegral.h
+  base/Deprecated.h
+  base/DtorReset.h
+  base/Exception.h
+  base/UserRequestException.h
+  base/ExternalDataSource.h
+  base/Fd.h
+  base/Flags.h
+  base/Function.h
+  base/Functional.h
+  base/Gettext.h
+  base/GzStream.h
+  base/IOStream.h
+  base/InputStream.h
+  base/Iterator.h
+  base/LogControl.h
+  base/LogTools.h
+  base/Logger.h
+  base/Measure.h
+  base/NonCopyable.h
+  base/ProfilingFormater.h
+  base/ProvideNumericId.h
+  base/PtrTypes.h
+  base/ReferenceCounted.h
+  base/SafeBool.h
+  base/Signal.h
+  base/String.h
+  base/Regex.h
+  base/Sysconfig.h
+  base/Tr1hash.h
+  base/Unit.h
+  base/WatchFile.h
+)
+
+INSTALL(  FILES
+  ${zypp_base_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/base
+)
+
+SET( zypp_media_SRCS
+  media/MediaException.cc
+  media/MediaAccess.cc
+  media/MediaHandler.cc
+  media/Mount.cc
+  media/MediaNFS.cc
+  media/MediaCD.cc
+  media/MediaDIR.cc
+  media/MediaDISK.cc
+  media/MediaCIFS.cc
+  media/ProxyInfo.cc
+  media/MediaCurl.cc
+  media/MediaMultiCurl.cc
+  media/MediaAria2c.cc
+  media/MediaISO.cc
+  media/MediaPlugin.cc
+  media/MediaSource.cc
+  media/MediaManager.cc
+  media/MediaUserAuth.cc
+  media/CredentialFileReader.cc
+  media/CredentialManager.cc
+  media/CurlConfig.cc
+  media/TransferSettings.cc
+  media/MediaPriority.cc
+  media/MetaLinkParser.cc
+  media/ZsyncParser.cc
+  media/MediaBlockList.cc
+  media/UrlResolverPlugin.cc
+)
+
+SET( zypp_media_HEADERS
+  media/MediaAccess.h
+  media/MediaCD.h
+  media/MediaCIFS.h
+  media/MediaCurl.h
+  media/MediaMultiCurl.h
+  media/MediaAria2c.h
+  media/MediaDIR.h
+  media/MediaDISK.h
+  media/MediaException.h
+  media/MediaHandler.h
+  media/MediaISO.h
+  media/MediaPlugin.h
+  media/MediaManager.h
+  media/MediaNFS.h
+  media/MediaSource.h
+  media/MediaUserAuth.h
+  media/Mount.h
+  media/ProxyInfo.h
+  media/CredentialFileReader.h
+  media/CredentialManager.h
+  media/CurlConfig.h
+  media/TransferSettings.h
+  media/MediaPriority.h
+  media/MetaLinkParser.h
+  media/ZsyncParser.h
+  media/MediaBlockList.h
+  media/UrlResolverPlugin.h
+)
+
+INSTALL(  FILES
+  ${zypp_media_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/media
+)
+
+IF ( LIBPROXY_FOUND )
+  SET(zypp_media_proxyinfo_libproxy_SRCS media/proxyinfo/ProxyInfoLibproxy.cc)
+  SET(zypp_media_proxyinfo_libproxy_HEADERS media/proxyinfo/ProxyInfoLibproxy.h)
+ENDIF( LIBPROXY_FOUND )
+
+SET( zypp_media_proxyinfo_SRCS
+  media/proxyinfo/ProxyInfoSysconfig.cc
+  ${zypp_media_proxyinfo_libproxy_SRCS}
+)
+
+SET( zypp_media_proxyinfo_HEADERS
+  media/proxyinfo/ProxyInfoImpl.h
+  media/proxyinfo/ProxyInfoSysconfig.h
+  ${zypp_media_proxyinfo_libproxy_HEADERS}
+  media/proxyinfo/ProxyInfos.h
+)
+
+INSTALL(  FILES
+  ${zypp_media_proxyinfo_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/media/proxyinfo
+)
+
+SET( zypp_parser_SRCS
+  parser/ParseException.cc
+  parser/IniParser.cc
+  parser/IniDict.cc
+  parser/HistoryLogReader.cc
+  parser/RepoFileReader.cc
+  parser/RepoindexFileReader.cc
+  parser/ServiceFileReader.cc
+  parser/ProductFileReader.cc
+)
+
+SET( zypp_parser_HEADERS
+  parser/ParseException.h
+  parser/IniParser.h
+  parser/IniDict.h
+  parser/HistoryLogReader.h
+  parser/ParserProgress.h
+  parser/RepoFileReader.h
+  parser/xml_escape_parser.hpp
+  parser/RepoindexFileReader.h
+  parser/ServiceFileReader.h
+  parser/ProductFileReader.h
+)
+
+INSTALL(  FILES
+  ${zypp_parser_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser
+)
+
+SET( zypp_parser_susetags_SRCS
+  parser/susetags/RepoIndex.cc
+  parser/susetags/ContentFileReader.cc
+)
+
+SET( zypp_parser_susetags_HEADERS
+  parser/susetags/RepoIndex.h
+  parser/susetags/ContentFileReader.h
+)
+
+INSTALL(  FILES
+  ${zypp_parser_susetags_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser/susetags
+)
+
+SET( zypp_parser_plaindir_SRCS
+  parser/plaindir/RepoParser.cc
+)
+
+SET( zypp_parser_plaindir_HEADERS
+  parser/plaindir/RepoParser.h
+)
+
+INSTALL(  FILES
+  ${zypp_parser_plaindir_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser/plaindir
+)
+
+SET( zypp_parser_xml_SRCS
+  parser/xml/Node.cc
+  parser/xml/ParseDef.cc
+  parser/xml/ParseDefConsume.cc
+  parser/xml/ParseDefException.cc
+  parser/xml/Reader.cc
+  parser/xml/XmlString.cc
+  parser/xml/libxmlfwd.cc
+  parser/xml/xml_escape_parser.cpp
+)
+
+SET( zypp_parser_xml_HEADERS
+  parser/xml/Parse.h
+  parser/xml/Node.h
+  parser/xml/ParseDef.h
+  parser/xml/ParseDefConsume.h
+  parser/xml/ParseDefException.h
+  parser/xml/ParseDefTraits.h
+  parser/xml/Reader.h
+  parser/xml/XmlEscape.h
+  parser/xml/XmlString.h
+  parser/xml/libxmlfwd.h
+  parser/xml/xml_escape_parser.hpp
+)
+
+INSTALL(  FILES
+  ${zypp_parser_xml_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser/xml
+)
+
+SET( zypp_parser_yum_SRCS
+  parser/yum/RepomdFileReader.cc
+  parser/yum/PatchesFileReader.cc
+)
+
+SET( zypp_parser_yum_HEADERS
+  parser/yum/RepomdFileReader.h
+  parser/yum/PatchesFileReader.h
+)
+
+INSTALL(  FILES
+  ${zypp_parser_yum_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser/yum
+)
+
+SET( zypp_parser_ws_SRCS
+  parser/ws/WebpinResultFileReader.cc
+)
+
+SET( zypp_parser_ws_HEADERS
+  parser/ws/WebpinResultFileReader.h
+)
+
+INSTALL(  FILES
+  ${zypp_parser_ws_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/parser/ws
+)
+
+
+SET( zypp_pool_SRCS
+  pool/GetResolvablesToInsDel.cc
+  pool/PoolImpl.cc
+  pool/PoolStats.cc
+)
+
+SET( zypp_pool_HEADERS
+  pool/GetResolvablesToInsDel.h
+  pool/PoolImpl.h
+  pool/PoolStats.h
+  pool/PoolTraits.h
+  pool/ByIdent.h
+)
+
+INSTALL(  FILES
+  ${zypp_pool_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/pool
+)
+
+SET( zypp_solver_detail_SRCS
+  solver/detail/Helper.cc
+  solver/detail/InstallOrder.cc
+  solver/detail/ProblemSolutionIgnore.cc
+  solver/detail/ProblemSolutionCombi.cc
+  solver/detail/Resolver.cc
+  solver/detail/ResolverUpgrade.cc
+  solver/detail/Resolver_problems.cc
+  solver/detail/SolutionAction.cc
+  solver/detail/Testcase.cc
+  solver/detail/SolverQueueItem.cc
+  solver/detail/SolverQueueItemInstall.cc
+  solver/detail/SolverQueueItemDelete.cc
+  solver/detail/SolverQueueItemUpdate.cc
+  solver/detail/SolverQueueItemInstallOneOf.cc
+  solver/detail/SolverQueueItemLock.cc
+  solver/detail/SATResolver.cc
+  solver/detail/SystemCheck.cc
+)
+
+SET( zypp_solver_detail_HEADERS
+  solver/detail/Helper.h
+  solver/detail/InstallOrder.h
+  solver/detail/ProblemSolutionIgnore.h
+  solver/detail/ProblemSolutionCombi.h
+  solver/detail/Resolver.h
+  solver/detail/SolutionAction.h
+  solver/detail/Testcase.h
+  solver/detail/Types.h
+  solver/detail/SolverQueueItem.h
+  solver/detail/SolverQueueItemInstall.h
+  solver/detail/SolverQueueItemDelete.h
+  solver/detail/SolverQueueItemUpdate.h
+  solver/detail/SolverQueueItemInstallOneOf.h
+  solver/detail/SolverQueueItemLock.h
+  solver/detail/SATResolver.h
+  solver/detail/SystemCheck.h
+)
+
+INSTALL(  FILES
+  ${zypp_solver_detail_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/solver/detail
+)
+
+SET( zypp_sat_SRCS
+  sat/Pool.cc
+  sat/Solvable.cc
+  sat/SolvableSet.cc
+  sat/SolvIterMixin.cc
+  sat/Queue.cc
+  sat/Transaction.cc
+  sat/WhatProvides.cc
+  sat/WhatObsoletes.cc
+  sat/LocaleSupport.cc
+  sat/LookupAttr.cc
+  sat/SolvAttr.cc
+  sat/AttrMatcher.cc
+)
+
+SET( zypp_sat_HEADERS
+  sat/Pool.h
+  sat/Solvable.h
+  sat/SolvableSet.h
+  sat/SolvIterMixin.h
+  sat/Queue.cc
+  sat/Transaction.h
+  sat/WhatProvides.h
+  sat/WhatObsoletes.h
+  sat/LocaleSupport.h
+  sat/LookupAttr.h
+  sat/LookupAttrTools.h
+  sat/SolvAttr.h
+  sat/AttrMatcher.h
+)
+
+INSTALL(  FILES
+  ${zypp_sat_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/sat
+)
+
+SET( zypp_sat_detail_SRCS
+  sat/detail/PoolImpl.cc
+)
+
+SET( zypp_sat_detail_HEADERS
+  sat/detail/PoolMember.h
+  sat/detail/PoolImpl.h
+)
+
+INSTALL(  FILES
+  ${zypp_sat_detail_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/sat/detail
+)
+
+
+SET( zypp_target_SRCS
+  target/RequestedLocalesFile.cc
+  target/SoftLocksFile.cc
+  target/HardLocksFile.cc
+  target/CommitPackageCache.cc
+  target/CommitPackageCacheImpl.cc
+  target/CommitPackageCacheReadAhead.cc
+  target/TargetCallbackReceiver.cc
+  target/TargetException.cc
+  target/TargetImpl.cc
+)
+
+SET( zypp_target_HEADERS
+  target/RequestedLocalesFile.h
+  target/SoftLocksFile.h
+  target/HardLocksFile.h
+  target/CommitPackageCache.h
+  target/CommitPackageCacheImpl.h
+  target/CommitPackageCacheReadAhead.h
+  target/TargetCallbackReceiver.h
+  target/TargetException.h
+  target/TargetImpl.h
+)
+
+INSTALL(  FILES
+  ${zypp_target_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/target
+)
+
+IF(NOT UDEV_FOUND)
+IF(HAL_FOUND)
+SET( zypp_target_hal_SRCS
+  target/hal/HalContext.cc
+)
+SET( zypp_target_hal_HEADERS
+  target/hal/HalContext.h
+  target/hal/HalException.h
+)
+ENDIF(HAL_FOUND)
+ENDIF(NOT UDEV_FOUND)
+# as HAL is deprecated, we don't install headers
+
+SET( zypp_target_modalias_SRCS
+  target/modalias/Modalias.cc
+)
+
+SET( zypp_target_modalias_HEADERS
+  target/modalias/Modalias.h
+)
+
+INSTALL(  FILES
+  ${zypp_target_modalias_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/target/modalias
+)
+
+SET( zypp_target_rpm_SRCS
+  target/rpm/BinHeader.cc
+  target/rpm/RpmCallbacks.cc
+  target/rpm/RpmDb.cc
+  target/rpm/RpmException.cc
+  target/rpm/RpmHeader.cc
+  target/rpm/librpmDb.cc
+  target/rpm/librpmDb.cv3.cc
+)
+
+SET( zypp_target_rpm_HEADERS
+  target/rpm/BinHeader.h
+  target/rpm/RpmCallbacks.h
+  target/rpm/RpmFlags.h
+  target/rpm/RpmDb.h
+  target/rpm/RpmException.h
+  target/rpm/RpmHeader.h
+  target/rpm/librpm.h
+  target/rpm/librpmDb.h
+)
+
+INSTALL(  FILES
+  ${zypp_target_rpm_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/target/rpm
+)
+
+SET( zypp_thread_SRCS
+  thread/Mutex.cc
+)
+
+SET( zypp_thread_HEADERS
+  thread/Mutex.h
+  thread/MutexException.h
+  thread/MutexLock.h
+  thread/Once.h
+)
+
+INSTALL(  FILES
+  ${zypp_thread_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/thread
+)
+
+SET( zypp_ui_SRCS
+  ui/Selectable.cc
+  ui/SelectableImpl.cc
+  ui/Status.cc
+  ui/UserWantedPackages.cc
+)
+
+SET( zypp_ui_HEADERS
+  ui/SelFilters.h
+  ui/Selectable.h
+  ui/SelectableImpl.h
+  ui/SelectableTraits.h
+  ui/Status.h
+  ui/UserWantedPackages.h
+)
+
+INSTALL(  FILES
+  ${zypp_ui_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/ui
+)
+
+SET( zypp_url_SRCS
+  url/UrlUtils.cc
+  url/UrlBase.cc
+)
+
+SET( zypp_url_HEADERS
+  url/UrlBase.h
+  url/UrlException.h
+  url/UrlUtils.h
+)
+
+INSTALL(  FILES
+  ${zypp_url_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/url
+)
+
+SET( zypp_zypp_detail_SRCS
+  zypp_detail/ZYppImpl.cc
+)
+
+SET( zypp_zypp_detail_HEADERS
+  zypp_detail/ZYppImpl.h
+  zypp_detail/ZYppReadOnlyHack.h
+)
+
+INSTALL(  FILES
+  ${zypp_zypp_detail_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/zypp_detail
+)
+
+SET( zypp_repo_SRCS
+  repo/RepoException.cc
+  repo/RepoMirrorList.cc
+  repo/RepoType.cc
+  repo/ServiceType.cc
+  repo/PackageProvider.cc
+  repo/SrcPackageProvider.cc
+  repo/RepoProvideFile.cc
+  repo/DeltaCandidates.cc
+  repo/Applydeltarpm.cc
+  repo/PackageDelta.cc
+  repo/SUSEMediaVerifier.cc
+  repo/MediaInfoDownloader.cc
+  repo/Downloader.cc
+  repo/RepoVariables.cc
+  repo/RepoInfoBase.cc
+  repo/PluginServices.cc
+  repo/ServiceRepos.cc
+)
+
+SET( zypp_repo_HEADERS
+  repo/RepoException.h
+  repo/RepoMirrorList.h
+  repo/RepoType.h
+  repo/ServiceType.h
+  repo/PackageProvider.h
+  repo/SrcPackageProvider.h
+  repo/RepoProvideFile.h
+  repo/DeltaCandidates.h
+  repo/Applydeltarpm.h
+  repo/PackageDelta.h
+  repo/SUSEMediaVerifier.h
+  repo/MediaInfoDownloader.h
+  repo/Downloader.h
+  repo/RepoVariables.h
+  repo/RepoInfoBase.h
+  repo/RepoInfoBaseImpl.h
+  repo/PluginServices.h
+  repo/ServiceRepos.h
+)
+
+INSTALL( FILES
+  ${zypp_repo_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/repo
+)
+
+SET( zypp_repo_yum_SRCS
+  repo/yum/Downloader.cc
+  repo/yum/ResourceType.cc
+)
+
+SET( zypp_repo_yum_HEADERS
+  repo/yum/Downloader.h
+  repo/yum/ResourceType.h
+)
+
+SET( zypp_repo_susetags_SRCS
+  repo/susetags/Downloader.cc
+)
+
+SET( zypp_repo_susetags_HEADERS
+  repo/susetags/Downloader.h
+)
+
+SET( zypp_ws_SRCS
+  ws/WebpinResult.cc
+)
+
+SET( zypp_ws_HEADERS
+  ws/WebpinResult.h
+)
+
+####################################################################
+
+SET( zypp_misc_HEADERS
+  Misc.h
+  misc/DefaultLoadSystem.h
+  misc/CheckAccessDeleted.h
+)
+
+SET( zypp_misc_SRCS
+  misc/DefaultLoadSystem.cc
+  misc/CheckAccessDeleted.cc
+)
+
+INSTALL( FILES
+  ${zypp_misc_HEADERS}
+  DESTINATION ${CMAKE_INSTALL_PREFIX}/include/zypp/misc
+)
+
+####################################################################
+
+SET( zypp_lib_SRCS
+${zypp_misc_SRCS}
+${zypp_pool_SRCS}
+${zypp_parser_susetags_SRCS}
+${zypp_parser_xml_SRCS}
+${zypp_parser_yum_SRCS}
+${zypp_parser_plaindir_SRCS}
+${zypp_parser_ws_SRCS}
+${zypp_parser_SRCS}
+${zypp_media_proxyinfo_SRCS}
+${zypp_media_SRCS}
+${zypp_url_SRCS}
+${zypp_repo_SRCS}
+${zypp_repo_yum_SRCS}
+${zypp_repo_susetags_SRCS}
+${zypp_repo_data_SRCS}
+${zypp_target_rpm_SRCS}
+${zypp_target_hal_SRCS}
+${zypp_target_modalias_SRCS}
+${zypp_target_SRCS}
+${zypp_solver_detail_SRCS}
+${zypp_ui_SRCS}
+${zypp_thread_SRCS}
+${zypp_ws_SRCS}
+${zypp_SRCS}
+${zypp_zypp_detail_SRCS}
+${zypp_sat_SRCS}
+${zypp_sat_detail_SRCS}
+${zypp_EARLY_SRCS}
+${zypp_base_SRCS}
+)
+
+SET( zypp_lib_HEADERS
+${zypp_target_rpm_HEADERS}
+${zypp_parser_tagfile_HEADERS}
+${zypp_parser_susetags_HEADERS}
+${zypp_parser_yum_HEADERS}
+${zypp_parser_plaindir_HEADERS}
+${zypp_parser_xml_HEADERS}
+${zypp_parser_ws_HEADERS}
+${zypp_parser_HEADERS}
+${zypp_ui_HEADERS}
+${zypp_media_HEADERS}
+${zypp_media_proxyinfo_HEADERS}
+${zypp_base_HEADERS}
+${zypp_solver_detail_HEADERS}
+${zypp_sat_HEADERS}
+${zypp_sat_detail_HEADERS}
+${zypp_url_HEADERS}
+${zypp_ws_HEADERS}
+${zypp_HEADERS}
+${zypp_zypp_detail_HEADERS}
+${zypp_thread_HEADERS}
+${zypp_repo_HEADERS}
+${zypp_source_susetags_HEADERS}
+${zypp_target_modalias_HEADERS}
+${zypp_target_HEADERS}
+${zypp_pool_HEADERS}
+${zypp_misc_HEADERS}
+)
+
+#
+# Macro to set the log group for a list of files
+#
+MACRO( SET_LOGGROUP _group _files  )
+  SET_SOURCE_FILES_PROPERTIES( ${_files} COMPILE_FLAGS -DZYPP_BASE_LOGGER_LOGGROUP=\\"${_group}\\" )
+  FOREACH (_currentFile ${ARGN})
+#MESSAGE( STATUS "setting loggroup to \"${_group}\" for ${_currentFile}" )
+    SET_SOURCE_FILES_PROPERTIES( ${_currentFile} COMPILE_FLAGS -DZYPP_BASE_LOGGER_LOGGROUP=\\"${_group}\\" )
+  ENDFOREACH (_currentFile ${ARGN})
+ENDMACRO( SET_LOGGROUP )
+
+# Default loggroup for all files
+SET_LOGGROUP( "zypp" ${zypp_lib_SRCS} )
+
+# override some defaults
+SET_LOGGROUP( "satsolver" ${zypp_sat_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_target_rpm_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_parser_yum2_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_capability_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_ui_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_media_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_parser_xml_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_media_proxyinfo_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_source_yum_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_base_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_solver_detail_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_sat_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_url_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_source_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_parser_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_zypp_detail_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_thread_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_source_susetags_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_target_modalias_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_target_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_parser_yum_SRCS} )
+#SET_LOGGROUP( "group" ${zypp_pool_SRCS} )
+
+
+ADD_LIBRARY(zypp SHARED ${zypp_lib_SRCS})
+SET_TARGET_PROPERTIES( zypp PROPERTIES VERSION "${LIBZYPP_VERSION_INFO}" )
+SET_TARGET_PROPERTIES( zypp PROPERTIES SOVERSION "${LIBZYPP_SOVERSION_INFO}" )
+ADD_DEPENDENCIES(zypp schema_header)
+# System libraries
+SET(UTIL_LIBRARY util)
+TARGET_LINK_LIBRARIES(zypp ${UTIL_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${RPM_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${GETTEXT_LIBRARIES} )
+TARGET_LINK_LIBRARIES(zypp ${CURL_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${LIBXML_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${ZLIB_LIBRARY} )
+TARGET_LINK_LIBRARIES(zypp ${SATSOLVER_LIBRARY} ${SATSOLVER_EXT_LIBRARY} ${EXPAT_LIBRARY})
+TARGET_LINK_LIBRARIES(zypp ${OPENSSL_LIBRARIES} )
+TARGET_LINK_LIBRARIES(zypp ${CRYPTO_LIBRARIES} )
+TARGET_LINK_LIBRARIES(zypp ${SIGNALS_LIBRARY} )
+
+IF ( UDEV_FOUND )
+  TARGET_LINK_LIBRARIES(zypp ${UDEV_LIBRARY} )
+ELSE ( UDEV_FOUND )
+  IF ( HAL_FOUND )
+    TARGET_LINK_LIBRARIES(zypp ${HAL_LIBRARY} ${HAL_STORAGE_LIBRARY} ${DBUS_LIBRARY} )
+  ENDIF ( HAL_FOUND )
+ENDIF ( UDEV_FOUND )
+
+TARGET_LINK_LIBRARIES(zypp ${LIBPROXY_LIBRARIES} )
+
+INSTALL(TARGETS zypp LIBRARY DESTINATION ${LIB_INSTALL_DIR} )
+
+# install XML schemas
+FILE( GLOB YUM_SCHEMA_FILES ${LIBZYPP_SOURCE_DIR}/zypp/parser/yum/schema/*.rng )
+INSTALL(FILES ${YUM_SCHEMA_FILES} DESTINATION "${CMAKE_INSTALL_PREFIX}/share/zypp/schema/yum" )
diff --git a/zypp/Callback.h b/zypp/Callback.h
new file mode 100644 (file)
index 0000000..b3eb516
--- /dev/null
@@ -0,0 +1,290 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Callback.h
+ *
+*/
+#ifndef ZYPP_CALLBACK_H
+#define ZYPP_CALLBACK_H
+
+#include "zypp/base/NonCopyable.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** \todo Eliminate this! */
+  namespace HACK {
+    class Callback
+    {
+    };
+  } // namespace HACK
+
+  ///////////////////////////////////////////////////////////////////
+  /** Callbacks light.
+   *
+   * \par The task report structure (SENDER SIDE).
+   *
+   * A default constructible struct derived from callback::ReportBase.
+   * It \b must \b not conatin any data, just virtual methods.
+   *
+   * These are the functions the sender invokes, and which will be forwarded
+   * to some receiver. If no receiver is present, the defined default
+   * implementations are invoked.
+   *
+   * For methods returning non-void, define a reasonable return value,
+   * because this is what you get back in case no receiver is listening.
+   *
+   * That way the sending side does not need to know whether some receiver
+   * is listening. And it enables the receiver to return a reasonable value,
+   * in case he's got no idea, what else to return.
+   *
+   * \code
+   *   struct Foo : public callback::ReportBase
+   *   {
+   *     virtual void ping( int i )
+   *     {}
+   *     virtual int pong()
+   *     { return -1; }
+   *
+   *   };
+   * \endcode
+   *
+   * \par Sending a Task report (SENDER SIDE).
+   *
+   * Simply create a callback::SendReport<_Report>, where _Report
+   * is your task report structure. Invoke the callback functions
+   * as needed. That's it.
+   *
+   * \note Even creation and destruction of a callback::SendReport
+   * are indicated to a receiver. So even in case of an Exception,
+   * the receiver is able to recognize, that the task ended.
+   * So don't create it without need.
+   *
+   * \code
+   * {
+   *   callback::SendReport<Foo> report;
+   *   report->ping( 13 );
+   *   int response = report->pong();
+   * }
+   * \endcode
+   *
+   * \par Receiving Task reports (RECEIVER SIDE).
+   *
+   * To receive task reports of type \c Foo the recipient class
+   * derives from callback::ReceiveReport\<Foo\>. callback::ReceiveReport
+   * inherits \c Foo and provides two additional virtual methods:
+   *
+   * \code
+   *   virtual void reportbegin() {}
+   *   virtual void reportend() {}
+   * \endcode
+   *
+   * These two are automatically invoked, whenever the sender
+   * creates a callback::SendReport instance, and when it gets
+   * destructed. So even if the sending task is aborted without
+   * sending an explicit notification, the reciever may notice it,
+   * by overloading \c reportend.
+   *
+   * Overload the methods you're interested in.
+   *
+   * \note In case you must return some value and don't know which,
+   * return the task structures default. The author of the task
+   * structure had to provide this value, so it's probabely better
+   * than anything you \e invent.
+   * \code
+   *   int somefunction()
+   *   {
+   *     ...// don't know what to return?
+   *     return Foo::somefunction();
+   *   }
+   * \endcode
+   *
+   * \par Connecting the Receiver
+   *
+   * For this callback::ReceiveReport provides 4 methods:
+   * \code
+   *  void connect();
+   *  void disconnect();
+   *  bool connected() const;
+   *  ReceiveReport * whoIsConnected() const;
+   * \endcode
+   *
+   * \li \c connect Connect this ReceiveReport (in case some other
+   * ReceiveReport is connected, it get disconnected. Remember its
+   * a Callback light).
+   * \li \c disconnect Disconnect this ReceiveReport in case it is
+   * connected. If not connected nothing happens.
+   * \li \c connected Test wheter this ReceiveReport is currently
+   * connected.
+   * \li \c whoIsConnected Return a 'ReceiveReport*' to the currently
+   * connected ReceiveReport, or \c NULL if none is connected.
+   *
+  */
+  namespace callback
+  { /////////////////////////////////////////////////////////////////
+
+    /**  */
+    struct ReportBase
+    {
+      virtual ~ReportBase()
+      {}
+    };
+
+    /**  */
+    template<class _Report>
+      class DistributeReport;
+
+    /**  */
+    template<class _Report>
+      struct ReceiveReport : public _Report
+      {
+       typedef _Report                   ReportType;
+       typedef ReceiveReport<_Report>    Receiver;
+        typedef DistributeReport<_Report> Distributor;
+
+        virtual ~ReceiveReport()
+        { disconnect(); }
+
+        ReceiveReport * whoIsConnected() const
+        { return Distributor::instance().getReceiver(); }
+
+        bool connected() const
+        { return whoIsConnected() == this; }
+
+        void connect()
+        { Distributor::instance().setReceiver( *this ); }
+
+        void disconnect()
+        { Distributor::instance().unsetReceiver( *this ); }
+
+        virtual void reportbegin()
+        {}
+        virtual void reportend()
+        {}
+      };
+
+    /**  */
+    template<class _Report>
+      struct DistributeReport
+      {
+       public:
+       typedef _Report                   ReportType;
+       typedef ReceiveReport<_Report>    Receiver;
+       typedef DistributeReport<_Report> Distributor;
+
+         static DistributeReport & instance()
+         {
+           static DistributeReport _self;
+           return _self;
+         }
+
+         Receiver * getReceiver() const
+         { return _receiver == &_noReceiver ? 0 : _receiver; }
+
+         void setReceiver( Receiver & rec_r )
+         { _receiver = &rec_r; }
+
+         void unsetReceiver( Receiver & rec_r )
+         { if ( _receiver == &rec_r ) noReceiver(); }
+
+         void noReceiver()
+         { _receiver = &_noReceiver; }
+
+      public:
+         Receiver * operator->()
+         { return _receiver; }
+
+      private:
+        DistributeReport()
+        : _receiver( &_noReceiver )
+        {}
+        Receiver _noReceiver;
+        Receiver * _receiver;
+      };
+
+    /**  */
+    template<class _Report>
+      struct SendReport : private zypp::base::NonCopyable
+      {
+       typedef _Report                   ReportType;
+        typedef ReceiveReport<_Report>    Receiver;
+        typedef DistributeReport<_Report> Distributor;
+
+        SendReport()
+        { Distributor::instance()->reportbegin(); }
+
+        ~SendReport()
+        { Distributor::instance()->reportend(); }
+
+        Distributor & operator->()
+        { return Distributor::instance(); }
+      };
+
+    /** Temporarily connect a ReceiveReport then restore the previous one.
+     *
+     * Pass the ReceiveReport you want to connect temporarily
+     * to the ctor. The ReceiveReport is connected, a previously
+     * connected ReceiveReport is remembered and re-connected in
+     * the dtor.
+     * Use the default ctpr to temporarily disconnect any connected report.
+     * \code
+     *  struct FooReceive : public callback::ReceiveReport<Foo>
+     *  {..};
+     *  struct FooReceive2 : public callback::ReceiveReport<Foo>
+     *  {..};
+     *
+     *  FooReceive  r;
+     *  FooReceive2 r2;
+     *
+     *  r.connect();
+     *  ... // r receiving the report
+     *  {
+     *    callback::TempConnect<Foo> temp( r2 );
+     *    ...// r2 receiving the report
+     *  }
+     *  ...// r receiving the report
+     * \endcode
+    */
+    template<class _Report>
+      struct TempConnect
+      {
+       typedef _Report                   ReportType;
+        typedef ReceiveReport<_Report>    Receiver;
+        typedef DistributeReport<_Report> Distributor;
+
+        TempConnect()
+        : _oldRec( Distributor::instance().getReceiver() )
+        {
+          Distributor::instance().noReceiver();
+        }
+
+        TempConnect( Receiver & rec_r )
+        : _oldRec( Distributor::instance().getReceiver() )
+        {
+          rec_r.connect();
+        }
+
+        ~TempConnect()
+        {
+          if ( _oldRec )
+            Distributor::instance().setReceiver( *_oldRec );
+          else
+            Distributor::instance().noReceiver();
+        }
+      private:
+        Receiver * _oldRec;
+      };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace callback
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_CALLBACK_H
diff --git a/zypp/CapMatch.cc b/zypp/CapMatch.cc
new file mode 100644 (file)
index 0000000..e3a77e5
--- /dev/null
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CapMatch.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/CapMatch.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const CapMatch CapMatch::yes( true );
+  const CapMatch CapMatch::no( false );
+  const CapMatch CapMatch::irrelevant;
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const CapMatch & obj )
+  {
+    if ( obj._result == CapMatch::IRRELEVANT )
+      return str << "IRRELEVANT";
+    return str << ( obj._result == CapMatch::MATCH ? "MATCH" : "NOMATCH" );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/CapMatch.h b/zypp/CapMatch.h
new file mode 100644 (file)
index 0000000..5cbcf5a
--- /dev/null
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CapMatch.h
+ *
+*/
+#ifndef ZYPP_CAPMATCH_H
+#define ZYPP_CAPMATCH_H
+
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CapMatch
+  //
+  /** Tri state Capability match result.
+   *
+   * CapMatch::irrelevant denotes a result value that should be ignored.
+   * Therfore it behaves neutral when used in <tt>! && ||</tt> expressions.
+   *
+   * \code
+   *   CapMatch any
+   *   (CapMatch::irrelevant && any) == any                  // true
+   *   (CapMatch::irrelevant || any) == any                  // true
+   *   ( !CapMatch::irrelevant )     == CapMatch::irrelevant // true
+   * \endcode
+  */
+  class CapMatch
+  {
+    enum Result { NOMATCH, MATCH, IRRELEVANT };
+
+  public:
+
+    CapMatch()
+    : _result( IRRELEVANT )
+    {}
+
+    CapMatch( bool val_r )
+    : _result( val_r ? MATCH : NOMATCH )
+    {}
+
+    static const CapMatch yes;
+    static const CapMatch no;
+    static const CapMatch irrelevant;
+
+    friend bool operator==( const CapMatch & lhs, const CapMatch & rhs )
+    { return lhs._result == rhs._result; }
+
+    friend bool operator!=( const CapMatch & lhs, const CapMatch & rhs )
+    { return lhs._result != rhs._result; }
+
+    friend CapMatch operator!( const CapMatch & lhs )
+    {
+      if ( lhs._result == CapMatch::IRRELEVANT )
+        return lhs;
+      return !(lhs._result == CapMatch::MATCH);
+    }
+
+    friend CapMatch operator&&( const CapMatch & lhs, const CapMatch & rhs )
+    {
+      if ( lhs._result == CapMatch::IRRELEVANT )
+        return rhs;
+      if ( rhs._result == CapMatch::IRRELEVANT )
+        return lhs;
+      return    (lhs._result == CapMatch::MATCH)
+             && (rhs._result == CapMatch::MATCH);
+    }
+
+    friend CapMatch operator||( const CapMatch & lhs, const CapMatch & rhs )
+    {
+      if ( lhs._result == CapMatch::IRRELEVANT )
+        return rhs;
+      if ( rhs._result == CapMatch::IRRELEVANT )
+        return lhs;
+      return    (lhs._result == CapMatch::MATCH)
+             || (rhs._result == CapMatch::MATCH);
+    }
+
+    friend std::ostream & operator<<( std::ostream & str, const CapMatch & obj );
+
+  private:
+    Result _result;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates CapMatch Stream output */
+  std::ostream & operator<<( std::ostream & str, const CapMatch & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_CAPMATCH_H
diff --git a/zypp/Capabilities.cc b/zypp/Capabilities.cc
new file mode 100644 (file)
index 0000000..dc53979
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Capabilities.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/Capabilities.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  Capabilities:: Capabilities( const sat::detail::IdType * base_r, sat::detail::IdType skip_r )
+  : _begin( base_r )
+  {
+    if ( ! _begin )
+      return;
+
+    if ( skip_r )
+    {
+      for ( const sat::detail::IdType * end = _begin; *end; ++end )
+      {
+        if ( *end == skip_r )
+        {
+          _begin = end+1;
+          return;
+        }
+      }
+    }
+    // skipp all ==> empty
+    _begin = 0;
+  }
+
+
+  Capabilities::size_type Capabilities::size() const
+  {
+    if ( ! _begin )
+      return 0;
+
+    // jump over satsolvers internal ids.
+    Capabilities::size_type ret = 0;
+    for ( const sat::detail::IdType * end = _begin; *end; ++end )
+    {
+      if ( ! sat::detail::isDepMarkerId( *end ) )
+        ++ret;
+    }
+    return ret;
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const Capabilities & obj )
+  {
+    return dumpRange( str << "(" << obj.size() << ")", obj.begin(), obj.end() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Capabilities.h b/zypp/Capabilities.h
new file mode 100644 (file)
index 0000000..b0cd922
--- /dev/null
@@ -0,0 +1,171 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Capabilities.h
+ *
+*/
+#ifndef ZYPP_SAT_CAPABILITIES_H
+#define ZYPP_SAT_CAPABILITIES_H
+
+#include <iosfwd>
+
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/Capability.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Capabilities
+  //
+  /** Container of \ref Capability (currently read only).
+   *
+   * \note satsolver dependency lists may include internal ids
+   * which must be skipped on iteration or size calculation
+   * (\see \ref detail::isDepMarkerId).
+   */
+  class Capabilities
+  {
+    public:
+      typedef Capability value_type;
+      typedef unsigned   size_type;
+
+      enum Mode { SKIP_TO_INTERNAL };
+
+    public:
+      /** Default ctor */
+      Capabilities()
+      : _begin( 0 )
+      {}
+
+      /** Ctor from Id pointer (friend \ref Solvable). */
+      explicit
+      Capabilities( const sat::detail::IdType * base_r )
+      : _begin( base_r )
+      {}
+
+      /** Ctor from Id pointer (friend \ref Solvable).
+       * Jump behind skip_r (e.g. behind prereqMarker).
+       */
+      Capabilities( const sat::detail::IdType * base_r, sat::detail::IdType skip_r );
+
+    public:
+      /** Whether the container is empty. */
+      bool empty() const
+      { return ! ( _begin && *_begin ); }
+
+      /** Number of capabilities inside. */
+      size_type size() const;
+
+    public:
+      class const_iterator;
+
+      /** Iterator pointing to the first \ref Capability. */
+      const_iterator begin() const;
+
+      /** Iterator pointing behind the last \ref Capability. */
+      const_iterator end() const;
+
+    private:
+      const sat::detail::IdType * _begin;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Capabilities Stream output */
+  std::ostream & operator<<( std::ostream & str, const Capabilities & obj );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Capabilities::const_iterator
+  //
+  /** \ref Capabilities iterator.
+   */
+  class Capabilities::const_iterator : public boost::iterator_adaptor<
+        const_iterator                   // Derived
+      , const sat::detail::IdType *      // Base
+      , const Capability                 // Value
+      , boost::forward_traversal_tag     // CategoryOrTraversal
+      , const Capability                 // Reference
+      >
+  {
+    public:
+      const_iterator()
+      : const_iterator::iterator_adaptor_( 0 )
+      {}
+
+      explicit const_iterator( const sat::detail::IdType * _idx )
+      : const_iterator::iterator_adaptor_( _idx )
+      {
+        if ( base_reference() && sat::detail::isDepMarkerId( *base_reference() ) )
+        {
+          _tagged = true;
+          ++base_reference();
+        }
+      }
+
+    public:
+      /** Return \c true if the \ref Capability is \c tagged.
+       * The meaning of \c tagged depends on the kind of dependency you
+       * are processing. It is a hint that the iteratir skipped some
+       * internal marker, indicating that subsequent cabailities have
+       * a special property. Within a \ref Solvables requirements e.g.
+       * the pre-requirements are tagged.
+       * \code
+       * Capabilities req( solvable.requires() );
+       * for_( it, req.begin(), req.end() )
+       * {
+       *   if ( it.tagged() )
+       *     cout << *it << " (is prereq)" << endl;
+       *   else
+       *     cout << *it << endl;
+       * }
+       * \endcode
+      */
+      bool tagged() const { return _tagged; }
+
+    private:
+      friend class boost::iterator_core_access;
+
+      reference dereference() const
+      { return ( base() ) ? Capability( *base() ) : Capability::Null; }
+
+      template <class OtherDerived, class OtherIterator, class V, class C, class R, class D>
+      bool equal( const boost::iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> & rhs ) const
+      { // NULL pointer is eqal pointer to Id 0
+        return ( base() == rhs.base() // includes both NULL...
+                 || ( !rhs.base() && !*base()     )
+                 || ( !base()     && !*rhs.base() ) );
+      }
+
+      void increment()
+      { // jump over satsolvers internal ids.
+        if ( sat::detail::isDepMarkerId( *(++base_reference()) ) )
+        {
+          _tagged = true;
+          ++base_reference();
+        }
+      }
+
+    private:
+      DefaultIntegral<bool,false> _tagged;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  inline Capabilities::const_iterator Capabilities::begin() const
+  { return const_iterator( _begin ); }
+
+  inline Capabilities::const_iterator Capabilities::end() const
+  { return const_iterator( 0 ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_CAPABILITIES_H
diff --git a/zypp/Capability.cc b/zypp/Capability.cc
new file mode 100644 (file)
index 0000000..62086d5
--- /dev/null
@@ -0,0 +1,569 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Capability.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/Arch.h"
+#include "zypp/Rel.h"
+#include "zypp/Edition.h"
+#include "zypp/Capability.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/ResPool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /** backward skip whitespace starting at pos_r */
+    inline std::string::size_type backskipWs( const std::string & str_r, std::string::size_type pos_r )
+    {
+      for ( ; pos_r != std::string::npos; --pos_r )
+      {
+        char ch = str_r[pos_r];
+        if ( ch != ' ' && ch != '\t' )
+          break;
+      }
+      return pos_r;
+    }
+
+    /** backward skip non-whitespace starting at pos_r */
+    inline std::string::size_type backskipNWs( const std::string & str_r, std::string::size_type pos_r )
+    {
+      for ( ; pos_r != std::string::npos; --pos_r )
+      {
+        char ch = str_r[pos_r];
+        if ( ch == ' ' || ch == '\t' )
+          break;
+      }
+      return pos_r;
+    }
+
+    /** Split any 'op edition' from str_r */
+    void splitOpEdition( std::string & str_r, Rel & op_r, Edition & ed_r )
+    {
+      if ( str_r.empty() )
+        return;
+      std::string::size_type ch( str_r.size()-1 );
+
+      // check whether the one but last word is a valid Rel:
+      if ( (ch = backskipWs( str_r, ch )) != std::string::npos )
+      {
+        std::string::size_type ee( ch );
+        if ( (ch = backskipNWs( str_r, ch )) != std::string::npos )
+        {
+          std::string::size_type eb( ch );
+          if ( (ch = backskipWs( str_r, ch )) != std::string::npos )
+          {
+            std::string::size_type oe( ch );
+            ch = backskipNWs( str_r, ch ); // now before 'op'? begin
+            if ( op_r.parseFrom( str_r.substr( ch+1, oe-ch ) ) )
+            {
+              // found a legal 'op'
+              ed_r = Edition( str_r.substr( eb+1, ee-eb ) );
+              if ( ch != std::string::npos ) // 'op' is not at str_r begin, so skip WS
+                ch = backskipWs( str_r, ch );
+              str_r.erase( ch+1 );
+              return;
+            }
+          }
+        }
+      }
+      // HERE: Didn't find 'name op edition'
+      // As a convenience we check for an embeded 'op' (not surounded by WS).
+      // But just '[<=>]=?|!=' and not inside '()'.
+      ch = str_r.find_last_of( "<=>)" );
+      if ( ch != std::string::npos && str_r[ch] != ')' )
+      {
+        std::string::size_type oe( ch );
+
+        // do edition first:
+        ch = str_r.find_first_not_of( " \t", oe+1 );
+        if ( ch != std::string::npos )
+          ed_r = Edition( str_r.substr( ch ) );
+
+        // now finish op:
+        ch = oe-1;
+        if ( str_r[oe] != '=' )        // '[<>]'
+        {
+          op_r = ( str_r[oe] == '<' ) ? Rel::LT : Rel::GT;
+        }
+        else
+        { // '?='
+          if ( ch != std::string::npos )
+          {
+            switch ( str_r[ch] )
+            {
+              case '<': --ch; op_r = Rel::LE; break;
+              case '>': --ch; op_r = Rel::GE; break;
+              case '!': --ch; op_r = Rel::NE; break;
+              case '=': --ch; // fall through
+              default:        op_r = Rel::EQ; break;
+            }
+          }
+        }
+
+        // finally name:
+        if ( ch != std::string::npos ) // 'op' is not at str_r begin, so skip WS
+          ch = backskipWs( str_r, ch );
+        str_r.erase( ch+1 );
+        return;
+      }
+      // HERE: It's a plain 'name'
+    }
+
+    /** Build \ref Capability from data. No parsing required.
+    */
+    sat::detail::IdType relFromStr( ::_Pool * pool_r,
+                                    const Arch & arch_r,
+                                    const std::string & name_r,
+                                    Rel op_r,
+                                    const Edition & ed_r,
+                                    const ResKind & kind_r )
+    {
+      // First build the name, non-packages prefixed by kind
+      sat::Solvable::SplitIdent split( kind_r, name_r );
+      sat::detail::IdType nid( split.ident().id() );
+
+      if ( split.kind() == ResKind::srcpackage )
+      {
+        // map 'kind srcpackage' to 'arch src', the pseudo architecture
+        // satsolver uses.
+        nid = ::rel2id( pool_r, nid, IdString(ARCH_SRC).id(), REL_ARCH, /*create*/true );
+      }
+
+      // Extend name by architecture, if provided and not a srcpackage
+      if ( ! arch_r.empty() && kind_r != ResKind::srcpackage )
+      {
+        nid = ::rel2id( pool_r, nid, arch_r.id(), REL_ARCH, /*create*/true );
+      }
+
+      // Extend 'op edition', if provided
+      if ( op_r != Rel::ANY && ed_r != Edition::noedition )
+      {
+        nid = ::rel2id( pool_r, nid, ed_r.id(), op_r.bits(), /*create*/true );
+      }
+
+      return nid;
+    }
+
+   /** Build \ref Capability from data, just parsing name for '[.arch]' and detect
+    * 'kind srcpackage' (will be mapped to arch \c src).
+    */
+    sat::detail::IdType relFromStr( ::_Pool * pool_r,
+                                    const std::string & name_r, Rel op_r, const Edition & ed_r,
+                                    const ResKind & kind_r )
+    {
+      static const Arch srcArch( IdString(ARCH_SRC).asString() );
+      static const std::string srcKindPrefix( ResKind::srcpackage.asString() + ':' );
+
+      // check for an embedded 'srcpackage:foo' to be mapped to 'foo' and 'ResKind::srcpackage'.
+      if ( kind_r.empty() && str::hasPrefix( name_r, srcKindPrefix ) )
+      {
+        return relFromStr( pool_r, Arch_empty, name_r.substr( srcKindPrefix.size() ), op_r, ed_r, ResKind::srcpackage );
+      }
+
+      Arch arch( Arch_empty );
+      std::string name( name_r );
+
+      std::string::size_type asep( name_r.rfind( "." ) );
+      if ( asep != std::string::npos )
+      {
+        Arch ext( name_r.substr( asep+1 ) );
+        if ( ext.isBuiltIn() || ext == srcArch )
+        {
+          arch = ext;
+          name.erase( asep );
+        }
+      }
+
+      return relFromStr( pool_r, arch, name, op_r, ed_r, kind_r );
+    }
+
+    /** Full parse from string, unless Capability::PARSED.
+    */
+    sat::detail::IdType relFromStr( ::_Pool * pool_r,
+                                    const Arch & arch_r, // parse from name if empty
+                                    const std::string & str_r, const ResKind & kind_r,
+                                    Capability::CtorFlag flag_r )
+    {
+      std::string name( str_r );
+      Rel         op;
+      Edition     ed;
+      if ( flag_r == Capability::UNPARSED )
+      {
+        splitOpEdition( name, op, ed );
+      }
+
+      if ( arch_r.empty() )
+        return relFromStr( pool_r, name, op, ed, kind_r ); // parses for name[.arch]
+      // else
+      return relFromStr( pool_r, arch_r, name, op, ed, kind_r );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  const Capability Capability::Null( STRID_NULL );
+  const Capability Capability::Empty( STRID_EMPTY );
+
+  /////////////////////////////////////////////////////////////////
+
+  Capability::Capability( const char * str_r, const ResKind & prefix_r, CtorFlag flag_r )
+  : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const std::string & str_r, const ResKind & prefix_r, CtorFlag flag_r )
+  : _id( relFromStr( myPool().getPool(), Arch_empty, str_r.c_str(), prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const Arch & arch_r, const char * str_r, const ResKind & prefix_r, CtorFlag flag_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const Arch & arch_r, const std::string & str_r, const ResKind & prefix_r, CtorFlag flag_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, str_r.c_str(), prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const char * str_r, CtorFlag flag_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const Arch & arch_r, const char * str_r, CtorFlag flag_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
+  {}
+
+  Capability::Capability( const Arch & arch_r, const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  // Ctor from <name[.arch] op edition>.
+  ///////////////////////////////////////////////////////////////////
+
+  Capability::Capability( const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), name_r, op_r, Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), name_r, op_r, ed_r, prefix_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  // Ctor from <arch name op edition>.
+  ///////////////////////////////////////////////////////////////////
+
+  Capability::Capability( const std::string & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, op_r, Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, op_r, ed_r, prefix_r ) )
+  {}
+  Capability::Capability( const Arch & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, name_r, op_r, Edition(ed_r), prefix_r ) )
+  {}
+  Capability::Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+  : _id( relFromStr( myPool().getPool(), arch_r, name_r, op_r, ed_r, prefix_r ) )
+  {}
+
+  const char * Capability::c_str() const
+  { return( _id ? ::dep2str( myPool().getPool(), _id ) : "" ); }
+
+  CapMatch Capability::_doMatch( sat::detail::IdType lhs,  sat::detail::IdType rhs )
+  {
+#warning MIGRATE TO SAT
+#warning TESTCASE
+    if ( lhs == rhs )
+      return CapMatch::yes;
+
+    CapDetail l( lhs );
+    CapDetail r( rhs );
+
+    switch ( l.kind() )
+    {
+      case CapDetail::NOCAP:
+        return( r.kind() == CapDetail::NOCAP ); // NOCAP matches NOCAP only
+        break;
+      case CapDetail::EXPRESSION:
+        return CapMatch::irrelevant;
+        break;
+      case CapDetail::NAMED:
+      case CapDetail::VERSIONED:
+        break;
+    }
+
+    switch ( r.kind() )
+    {
+      case CapDetail::NOCAP:
+        return CapMatch::no; // match case handled above
+        break;
+      case CapDetail::EXPRESSION:
+        return CapMatch::irrelevant;
+        break;
+      case CapDetail::NAMED:
+      case CapDetail::VERSIONED:
+        break;
+    }
+    // comparing two simple caps:
+    if ( l.name() != r.name() )
+      return CapMatch::no;
+
+    // if both are arch restricted they must match
+    if ( l.arch() != r.arch()
+         && ! ( l.arch().empty() || r.arch().empty() ) )
+      return CapMatch::no;
+
+    // isNamed matches ANY edition:
+    if ( l.isNamed() || r.isNamed() )
+      return CapMatch::yes;
+
+    // both are versioned:
+    return overlaps( Edition::MatchRange( l.op(), l.ed() ),
+                     Edition::MatchRange( r.op(), r.ed() ) );
+  }
+
+  bool Capability::isInterestingFileSpec( const char * name_r )
+  {
+    static       str::smatch what;
+    static const str::regex  filenameRegex(
+                 "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
+                 str::regex::optimize|str::regex::nosubs );
+
+    return str::regex_match( name_r, what, filenameRegex );
+  }
+
+  Capability Capability::guessPackageSpec( const std::string & str_r, bool & rewrote_r )
+  {
+    Capability cap( str_r );
+    CapDetail detail( cap.detail() );
+
+    // str_r might be the form "libzypp-1.2.3-4.5(.arch)'
+    // correctly parsed as name capability by the ctor.
+    if ( detail.isNamed() && ::strrchr( detail.name().c_str(), '-' ) && sat::WhatProvides( cap ).empty() )
+    {
+      Arch origArch( detail.arch() ); // to support a trailing .arch
+
+      std::string guess( detail.name().asString() );
+      std::string::size_type pos( guess.rfind( '-' ) );
+      guess[pos] = '=';
+
+      Capability guesscap( origArch, guess );
+      detail = guesscap.detail();
+
+      ResPool pool( ResPool::instance() );
+      // require name part matching a pool items name (not just provides!)
+      if ( pool.byIdentBegin( detail.name() ) != pool.byIdentEnd( detail.name() ) )
+      {
+       rewrote_r = true;
+       return guesscap;
+      }
+
+      // try the one but last '-'
+      if ( pos )
+      {
+        guess[pos] = '-';
+        if ( (pos = guess.rfind( '-', pos-1 )) != std::string::npos )
+        {
+          guess[pos] = '=';
+
+          guesscap = Capability( origArch, guess );
+          detail = guesscap.detail();
+
+          // require name part matching a pool items name (not just provides!)
+          if ( pool.byIdentBegin( detail.name() ) != pool.byIdentEnd( detail.name() ) )
+         {
+           rewrote_r = true;
+           return guesscap;
+         }
+        }
+      }
+    }
+
+    rewrote_r = false;
+    return cap;
+  }
+
+  Capability Capability::guessPackageSpec( const std::string & str_r )
+  {
+    bool dummy;
+    return guessPackageSpec( str_r, dummy );
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const Capability & obj )
+  {
+    return str << obj.detail();
+  }
+
+  std::ostream & dumpOn( std::ostream & str, const Capability & obj )
+  {
+    return str << obj.detail();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CapDetail
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  void CapDetail::_init()
+  {
+    // : _kind( NOCAP ), _lhs( id_r ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
+
+    if ( _lhs == sat::detail::emptyId || _lhs == sat::detail::noId )
+      return; // NOCAP
+
+    if ( ! ISRELDEP(_lhs) )
+    {
+      // this is name without arch!
+      _kind = NAMED;
+      return;
+    }
+
+    ::Reldep * rd = GETRELDEP( myPool().getPool(), _lhs );
+    _lhs  = rd->name;
+    _rhs  = rd->evr;
+    _flag = rd->flags;
+
+    if ( Rel::isRel( _flag ) )
+    {
+      _kind = VERSIONED;
+      // Check for name.arch...
+      if ( ! ISRELDEP(_lhs) )
+        return; // this is name without arch!
+      rd = GETRELDEP( myPool().getPool(), _lhs );
+      if ( rd->flags != CAP_ARCH )
+        return; // this is not name.arch
+      // This is name.arch:
+      _lhs = rd->name;
+      _archIfSimple = rd->evr;
+    }
+    else if ( rd->flags == CAP_ARCH )
+    {
+      _kind = NAMED;
+      // This is name.arch:
+      _lhs = rd->name;
+      _archIfSimple = rd->evr;
+    }
+    else
+    {
+      _kind = EXPRESSION;
+      return;
+    }
+    // map back satsolvers pseudo arch 'src' to kind srcpackage
+    if ( _archIfSimple == ARCH_SRC )
+    {
+      _lhs = IdString( (ResKind::srcpackage.asString() + ":" + IdString(_lhs).c_str()).c_str() ).id();
+      _archIfSimple = 0;
+    }
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const CapDetail & obj )
+  {
+    static const char archsep = '.';
+    switch ( obj.kind() )
+    {
+      case CapDetail::NOCAP:
+        return str << "<NoCap>";
+        break;
+      case CapDetail::NAMED:
+        str << obj.name();
+        if ( obj.hasArch() )
+          str << archsep << obj.arch();
+        return str;
+        break;
+      case CapDetail::VERSIONED:
+        str << obj.name();
+        if ( obj.hasArch() )
+          str << archsep << obj.arch();
+        return str << " " << obj.op() << " " << obj.ed();
+        break;
+      case CapDetail::EXPRESSION:
+        switch ( obj.capRel() )
+        {
+          case CapDetail::REL_NONE:
+          case CapDetail::CAP_AND:
+          case CapDetail::CAP_OR:
+          case CapDetail::CAP_WITH:
+          case CapDetail::CAP_ARCH:
+            return str << obj.lhs().detail() << " " << obj.capRel() << " " << obj.rhs().detail();
+            break;
+          case CapDetail::CAP_NAMESPACE:
+            return str << obj.lhs().detail() << "(" << obj.rhs().detail() << ")";
+        }
+        break;
+    }
+    return str <<  "<UnknownCap>";
+  }
+
+  std::ostream & operator<<( std::ostream & str, CapDetail::Kind obj )
+  {
+    switch ( obj )
+    {
+      case CapDetail::NOCAP:      return str << "NoCap"; break;
+      case CapDetail::NAMED:      return str << "NamedCap"; break;
+      case CapDetail::VERSIONED:  return str << "VersionedCap"; break;
+      case CapDetail::EXPRESSION: return str << "CapExpression"; break;
+    }
+    return str << "UnknownCap";
+  }
+
+  std::ostream & operator<<( std::ostream & str, CapDetail::CapRel obj )
+  {
+    switch ( obj )
+    {
+      case CapDetail::REL_NONE:      return str << "NoCapRel"; break;
+      case CapDetail::CAP_AND:       return str << "&"; break; // AND
+      case CapDetail::CAP_OR:        return str << "|"; break; // OR
+      case CapDetail::CAP_WITH:      return str << "+"; break; // WITH
+      case CapDetail::CAP_NAMESPACE: return str << "NAMESPACE"; break;
+      case CapDetail::CAP_ARCH:      return str << "ARCH"; break;
+   }
+    return str << "UnknownCapRel";
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Capability.h b/zypp/Capability.h
new file mode 100644 (file)
index 0000000..8f76c2c
--- /dev/null
@@ -0,0 +1,386 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Capability.h
+ *
+*/
+#ifndef ZYPP_CAPABILITY_H
+#define ZYPP_CAPABILITY_H
+
+#include <iosfwd>
+
+#include "zypp/base/SafeBool.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+
+#include "zypp/IdString.h"
+#include "zypp/Edition.h"
+#include "zypp/Rel.h"
+#include "zypp/ResTraits.h"
+
+#include "zypp/CapMatch.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class Capability;
+  class CapDetail;
+  class Arch;
+
+  typedef std::tr1::unordered_set<Capability> CapabilitySet;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Capability
+  //
+  /** A sat capability.
+   *
+   * A Capability: <tt>"name[.arch] [op edition]"</tt>
+   *
+   * If a certain \ref ResKind is specified upon construction, the
+   * capabilities name part is prefixed, unless it already conatins a
+   * well known kind spec. If no \ref ResKind is specified, it's assumed
+   * you refer to a package or the name is already prefixed:
+   * \code
+   * Capability( "foo" )                   ==> 'foo'
+   * Capability( "foo", ResKind::package ) ==> 'foo'
+   * Capability( "foo", ResKind::pattern ) ==> 'pattern:foo'
+   * Capability( "pattern:foo" )           ==> 'pattern:foo'
+   * // in doubt an explicit name prefix wins:
+   * Capability( "pattern:foo", ResKind::package ) ==> 'pattern:foo'
+   * Capability( "package:foo", ResKind::pattern ) ==> 'foo'
+   * \endcode
+   */
+  class Capability: protected sat::detail::PoolMember,
+                    private base::SafeBool<Capability>
+  {
+    public:
+      enum CtorFlag { PARSED, UNPARSED };
+
+    public:
+      /** Default ctor, \ref Empty capability. */
+      Capability() : _id( sat::detail::emptyId ) {}
+
+      /** Ctor from id. */
+      explicit Capability( sat::detail::IdType id_r ) : _id( id_r ) {}
+
+      /** \name Ctors parsing a Capability: <tt>"name[.arch] [op edition]"</tt> or <tt>( arch, "name [op edition]")</tt>
+      */
+      //@{
+      /** Ctor from string.
+       * \a str_r is parsed to check whether it contains an <tt>[op edition]</tt> part,
+       * unless the \ref PARSED flag is passed to the ctor. In that case <tt>"name[.arch]"</tt>
+       * is assumed.
+      */
+      explicit Capability( const char * str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
+      /** \overload */
+      explicit Capability( const std::string & str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
+      /** \overload Explicitly specify the \c arch. */
+      Capability( const Arch & arch_r, const char * str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
+      /** \overload Explicitly specify the \c arch. */
+      Capability( const Arch & arch_r, const std::string & str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
+
+      /** \overload Convenience for parsed (name only, no <tt>"[op edition]</tt>) packages: <tt>Capability( "glibc", PARSED ); */
+      Capability( const char * str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
+      /** \overload Explicitly specify the \c arch. */
+      Capability( const Arch & arch_r, const char * str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const Arch & arch_r, const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
+      //@}
+
+
+      /** \name Ctors parsing a broken down Capability: <tt>( "name[.arch]", op, edition )</tt>
+      */
+      //@{
+      /** Ctor from <tt>name[.arch] op edition</tt>. */
+      Capability( const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
+      //@}
+
+      /** \name Ctors taking a broken down Capability: <tt>( arch, name, op, edition )</tt>
+      */
+      //@{
+      /** Ctor from <tt>arch name op edition</tt>. */
+      Capability( const std::string & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const Arch & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
+      /** \overload */
+      Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
+      //@}
+
+    public:
+      /** No or Null \ref Capability ( Id \c 0 ). */
+      static const Capability Null;
+
+      /** Empty Capability. */
+      static const Capability Empty;
+
+    public:
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( ! empty() )</tt>. */
+      using base::SafeBool<Capability>::operator bool_type;
+#endif
+      /** Whether the \ref Capability is empty.
+       * This is true for \ref Null and \ref Empty.
+       */
+      bool empty() const
+      { return( _id == sat::detail::emptyId || _id == sat::detail::noId ); }
+
+    public:
+      /** Conversion to <tt>const char *</tt> */
+      const char * c_str() const;
+
+      /** \overload */
+      std::string asString() const
+      { return c_str(); }
+
+    public:
+      /** Helper providing more detailed information about a \ref Capability. */
+      CapDetail detail() const;
+
+    public:
+      /** \name Match two simple capabilities.
+       *
+       * Two simple capabilities match if they have the same \c name
+       * and their \c edition ranges overlap. Where no edition matches
+       * ANY edition. \see \ref Edition::match.
+       *
+       * If a capability expression is involved, \ref matches returns
+       * \ref CapMatch::irrelevant.
+       */
+      //@{
+      static CapMatch matches( const Capability & lhs,  const Capability & rhs )     { return _doMatch( lhs.id(), rhs.id() ); }
+      static CapMatch matches( const Capability & lhs,  const IdString & rhs )       { return _doMatch( lhs.id(), rhs.id() ); }
+      static CapMatch matches( const Capability & lhs,  const std::string & rhs )    { return _doMatch( lhs.id(), Capability(rhs).id() ); }
+      static CapMatch matches( const Capability & lhs,  const char * rhs )           { return _doMatch( lhs.id(), Capability(rhs).id() );}
+
+      static CapMatch matches( const IdString & lhs,    const Capability & rhs )     { return _doMatch( lhs.id(), rhs.id() ); }
+      static CapMatch matches( const IdString & lhs,    const IdString & rhs )       { return _doMatch( lhs.id(), rhs.id() ); }
+      static CapMatch matches( const IdString & lhs,    const std::string & rhs )    { return _doMatch( lhs.id(), Capability(rhs).id() ); }
+      static CapMatch matches( const IdString & lhs,    const char * rhs )           { return _doMatch( lhs.id(), Capability(rhs).id() ); }
+
+      static CapMatch matches( const std::string & lhs, const Capability & rhs )     { return _doMatch( Capability(lhs).id(), rhs.id() );}
+      static CapMatch matches( const std::string & lhs, const IdString & rhs )       { return _doMatch( Capability(lhs).id(), rhs.id() ); }
+      static CapMatch matches( const std::string & lhs, const std::string & rhs )    { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
+      static CapMatch matches( const std::string & lhs, const char * rhs )           { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
+
+      static CapMatch matches( const char * lhs,        const Capability & rhs )     { return _doMatch( Capability(lhs).id(), rhs.id() );}
+      static CapMatch matches( const char * lhs,        const IdString & rhs )       { return _doMatch( Capability(lhs).id(), rhs.id() ); }
+      static CapMatch matches( const char * lhs,        const std::string & rhs )    { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
+      static CapMatch matches( const char * lhs,        const char * rhs )           { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
+
+      CapMatch matches( const Capability & rhs )  const { return _doMatch( id(), rhs.id() ); }
+      CapMatch matches( const IdString & rhs )    const { return _doMatch( id(), rhs.id() ); }
+      CapMatch matches( const std::string & rhs ) const { return _doMatch( id(), Capability(rhs).id() ); }
+      CapMatch matches( const char * rhs )        const { return _doMatch( id(), Capability(rhs).id() ); }
+      //@}
+
+      /** \ref matches functor.
+       */
+      struct Matches: public std::binary_function<Capability,Capability,CapMatch>
+      {
+        CapMatch operator()( const Capability & lhs, const Capability & rhs ) const
+        { return Capability::matches( lhs, rhs ); }
+      };
+
+    public:
+      /** Test for a filename that is likely being REQUIRED.
+       * Files below \c /bin , \c /sbin ,  \c /lib etc. Scanning a
+       * packages filelist, an \e interesting filename might be worth
+       * being remembered in PROVIDES.
+       */
+      static bool isInterestingFileSpec( const IdString & name_r )    { return isInterestingFileSpec( name_r.c_str() ); }
+      static bool isInterestingFileSpec( const std::string & name_r ) { return isInterestingFileSpec( name_r.c_str() ); }
+      static bool isInterestingFileSpec( const char * name_r );
+
+      /** \ref Capability parser also guessing \c "libzypp-1.2.3-4.5.x86_64" formats.
+       *
+       * The argument might be in the form \c "libzypp-devel-1.2.3.x86_64".
+       * Passed to the Capability ctor, this would correctly be parsed as name
+       * capability, because actually the edition part had to be separated by a
+       * \c '=', and the architecture had to be appended to the name.
+       * So this is how it actually had to look like: \c "libzypp-devel.x86_64=1.2.3"
+       *
+       * Obviously we have to guess if, and where to split name and edition. In
+       * fact \c "devel" could also be the version and \c "1.2.3" would be the
+       * release then.
+       *
+       * Assuming this Capability should be provided by some package in
+       * the \ref ResPool, we check this. If unprovided, we substitute the last,
+       * (or one but last) \c '-' by a \c '='. If the name part (without version)
+       * of the resulting Capability matches a package name (not provides!) in
+       * the \ref ResPool, this Capability is returned.
+       *
+       * Otherwise we return the Capability originally created from
+       * \a str_r.
+       *
+       * \note: As this method will access the global pool, the returned
+       * result depends on the pools content.
+       */
+      static Capability guessPackageSpec( const std::string & str_r );
+      /** \overload Taking an additional bool indicating whether \c str_r made
+       * a valid \ref Capability (\c true) or the result was was guessed by
+       * rewiting a \c '-' to \c '='. (\c false).
+       */
+      static Capability guessPackageSpec( const std::string & str_r, bool & rewrote_r );
+
+    public:
+      /** Expert backdoor. */
+      sat::detail::IdType id() const
+      { return _id; }
+    private:
+      /** Match two Capabilities */
+      static CapMatch _doMatch( sat::detail::IdType lhs,  sat::detail::IdType rhs );
+    private:
+#ifndef SWIG // Swig treats it as syntax error
+      friend base::SafeBool<Capability>::operator bool_type() const;
+#endif
+      bool boolTest() const { return ! empty(); }
+    private:
+      sat::detail::IdType _id;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Capability Stream output */
+  std::ostream & operator<<( std::ostream & str, const Capability & obj );
+
+  /** \relates Capability Detailed stream output */
+  std::ostream & dumpOn( std::ostream & str, const Capability & obj );
+
+  /** \relates Capability */
+  inline bool operator==( const Capability & lhs, const Capability & rhs )
+  { return lhs.id() == rhs.id(); }
+
+  /** \relates Capability */
+  inline bool operator!=( const Capability & lhs, const Capability & rhs )
+  { return lhs.id() != rhs.id(); }
+
+  /** \relates Capability Arbitrary order. */
+  inline bool operator<( const Capability & lhs, const Capability & rhs )
+  { return lhs.id() < rhs.id(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CapDetail
+  //
+  /** Helper providing more detailed information about a \ref Capability.
+   *
+   * Capabilities are classified to be either \c SIMPLE:
+   * \code
+   *   name[.arch] [op edition]
+   *   with op := <|<=|=|>=|>|!=
+   * \endcode
+   * or formed by some \c EXPRESSION:
+   * \code
+   *   left_cap op right_cap
+   *   with op := AND|OR|WITH|NAMESPACE
+   * \endcode
+   */
+  class CapDetail: protected sat::detail::PoolMember
+  {
+    public:
+      enum Kind
+      {
+        NOCAP      = 0x00,
+        NAMED      = 0x01,
+        VERSIONED  = 0x02,
+        EXPRESSION = 0x04
+      };
+
+      /** Enum values corresponding with libsatsolver defines.
+       * \note MPL check in PoolImpl.cc
+      */
+      enum CapRel
+      {
+        REL_NONE      = 0,
+        CAP_AND       = 16,
+        CAP_OR        = 17,
+        CAP_WITH      = 18,
+        CAP_NAMESPACE = 19,
+        CAP_ARCH      = 20
+      };
+
+    public:
+      CapDetail()
+      : _kind( NOCAP ), _lhs( 0 ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
+      {}
+      explicit CapDetail( const Capability & cap_r )
+      : _kind( NOCAP ), _lhs( cap_r.id() ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
+      { _init(); }
+      explicit CapDetail( sat::detail::IdType id_r )
+      : _kind( NOCAP ), _lhs( id_r ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
+      { _init(); }
+
+    public:
+      Kind kind()         const { return _kind; }
+      bool isNull()       const { return _kind == NOCAP; }
+      bool isNamed()      const { return _kind == NAMED; }
+      bool isVersioned()  const { return _kind == VERSIONED; }
+      bool isSimple()     const { return _kind & (NAMED|VERSIONED); }
+      bool isExpression() const { return _kind == EXPRESSION; }
+
+      /** \name Is simple: <tt>name[.arch] [op edition]</tt> */
+      //@{
+      bool     hasArch()  const { return _archIfSimple; }
+      IdString arch()     const { return _archIfSimple ? IdString( _archIfSimple ) : IdString(); }
+      IdString name()     const { return isSimple()    ? IdString( _lhs ) : IdString(); }
+      Rel      op()       const { return isVersioned() ? Rel( _flag )     : Rel::ANY; }
+      Edition  ed()       const { return isVersioned() ? Edition( _rhs )  : Edition(); }
+      //@}
+
+      /** \name Is expression <tt>cap op cap</tt> */
+      //@{
+      Capability lhs()    const { return isExpression() ? Capability( _lhs ) : Capability::Null; }
+      CapRel     capRel() const { return isExpression() ? CapRel(_flag)      : REL_NONE; }
+      Capability rhs()    const { return isExpression() ? Capability( _rhs ) : Capability::Null; }
+     //@}
+
+    private:
+      void _init();
+    private:
+      Kind                _kind;
+      sat::detail::IdType _lhs;
+      sat::detail::IdType _rhs;
+      unsigned            _flag;
+      sat::detail::IdType _archIfSimple;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates CapDetail Stream output */
+  std::ostream & operator<<( std::ostream & str, const CapDetail & obj );
+
+  /** \relates CapDetail Stream output */
+  std::ostream & operator<<( std::ostream & str, CapDetail::Kind obj );
+
+  /** \relates CapDetail Stream output */
+  std::ostream & operator<<( std::ostream & str, CapDetail::CapRel obj );
+
+  ///////////////////////////////////////////////////////////////////
+
+  inline CapDetail Capability::detail() const { return CapDetail( _id ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+ZYPP_DEFINE_ID_HASHABLE( ::zypp::Capability );
+
+#endif // ZYPP_CAPABILITY_H
diff --git a/zypp/Changelog.cc b/zypp/Changelog.cc
new file mode 100644 (file)
index 0000000..3563b4c
--- /dev/null
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Changelog.cc
+ *
+*/
+#include <iostream>
+#include <set>
+
+#include "zypp/Changelog.h"
+
+using namespace std;
+
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** \relates ChangelogEntry */
+  std::ostream & operator<<( std::ostream & out, const ChangelogEntry & obj )
+  { 
+    out << obj.date() << " " << obj.author() << endl << obj.text() << endl;
+    return out;
+  }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Changelog.h b/zypp/Changelog.h
new file mode 100644 (file)
index 0000000..80173cf
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Changelog.h
+ *
+*/
+#ifndef ZYPP_CHANGELOG_H
+#define ZYPP_CHANGELOG_H
+
+#include <string>
+#include <list>
+
+#include "zypp/Date.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ChangelogEntry
+  //
+  /** Single entry in a change log
+  */
+  class ChangelogEntry
+  {
+  public:
+    /** Default ctor */
+    ChangelogEntry( const Date & d,
+                    const std::string & a,
+                    const std::string & t )
+    : _date( d ), _author( a ), _text( t )
+    {};
+    /** Dtor */
+    ~ChangelogEntry()
+    {}
+    Date date() const { return _date; }
+    std::string author() const { return _author; }
+    std::string text() const { return _text; }
+
+  private:
+    Date _date;
+    std::string _author;
+    std::string _text;
+  };
+
+  /** List of ChangelogEntry. */
+  typedef std::list<ChangelogEntry> Changelog;
+
+  /** \relates ChangelogEntry */
+  std::ostream & operator<<( std::ostream & out, const ChangelogEntry & obj );
+
+  ///////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_CHANGELOG_H
diff --git a/zypp/CheckSum.cc b/zypp/CheckSum.cc
new file mode 100644 (file)
index 0000000..ac34c42
--- /dev/null
@@ -0,0 +1,162 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CheckSum.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+
+#include "zypp/CheckSum.h"
+#include "zypp/Digest.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const std::string & CheckSum::md5Type()
+  { static std::string _type( "md5" ); return _type; }
+
+  const std::string & CheckSum::shaType()
+  { static std::string _type( "sha" ); return _type; }
+
+  const std::string & CheckSum::sha1Type()
+  { static std::string _type( "sha1" ); return _type; }
+
+  const std::string & CheckSum::sha256Type()
+  { static std::string _type( "sha256" ); return _type; }
+
+
+  CheckSum::CheckSum()
+  {}
+
+  CheckSum::CheckSum( const std::string & type, const std::string & checksum )
+  : _type( str::toLower( type ) )
+  , _checksum( checksum )
+  {
+    switch ( checksum.size() )
+    {
+      case 64:
+        if ( _type == sha256Type() )
+          return;
+        if ( _type.empty() || _type == shaType() )
+        {
+          _type = sha256Type();
+          return;
+        }
+        // else: dubious
+        break;
+
+      case 40:
+        if ( _type == sha1Type() )
+          return;
+        if ( _type.empty() || _type == shaType() )
+        {
+          _type = sha1Type();
+          return;
+        }
+        // else: dubious
+        break;
+
+      case 32:
+        if (  _type == md5Type() )
+          return;
+        if ( _type.empty() )
+        {
+          _type = md5Type();
+          return;
+        }
+        // else: dubious
+        break;
+
+      case 0:
+        return; // empty checksum is ok
+        break;
+
+      default:
+        if ( _type.empty() )
+        {
+          WAR << "Can't determine type of " << checksum.size() << " byte checksum '" << _checksum << "'" << endl;
+          return;
+        }
+        // else: dubious
+        break;
+    }
+
+    // dubious: Throw on malformed known types, otherwise log a warning.
+    std::string msg = str::form ( _("Dubious type '%s' for %u byte checksum '%s'"),
+                                  _type.c_str(), checksum.size(), _checksum.c_str() );
+    if (    _type == md5Type()
+         || _type == shaType()
+         || _type == sha1Type()
+         || _type == sha256Type() )
+    {
+      ZYPP_THROW( CheckSumException( msg ) );
+    }
+    else
+    {
+      WAR << msg << endl;
+    }
+  }
+
+  CheckSum::CheckSum( const std::string & type_r, std::istream & input_r )
+  {
+    if ( input_r.good() && ! type_r.empty() )
+      {
+        _type = str::toLower( type_r );
+        _checksum = Digest::digest( _type, input_r );
+        if ( ! input_r.eof() || _checksum.empty() )
+          {
+            _type = _checksum = std::string();
+          }
+      }
+  }
+
+  std::string CheckSum::type() const
+  { return _type; }
+
+  std::string CheckSum::checksum() const
+  { return _checksum; }
+
+  bool CheckSum::empty() const
+  { return (checksum().empty() || type().empty()); }
+
+  std::string CheckSum::asString() const
+  {
+    std::ostringstream str;
+    str << *this;
+    return str.str();
+  }
+
+  std::ostream & operator<<( std::ostream & str, const CheckSum & obj )
+  {
+    if ( obj.checksum().empty() )
+      {
+        return str << std::string("NoCheckSum");
+      }
+
+    return str << ( obj.type().empty() ? std::string("UNKNOWN") : obj.type() ) << '-' << obj.checksum();
+  }
+
+   /** \relates CheckSum */
+  bool operator==( const CheckSum & lhs, const CheckSum & rhs )
+  { return lhs.checksum() == rhs.checksum() && lhs.type() == rhs.type(); }
+
+  /** \relates CheckSum */
+  bool operator!=( const CheckSum & lhs, const CheckSum & rhs )
+  { return ! ( lhs == rhs ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/CheckSum.h b/zypp/CheckSum.h
new file mode 100644 (file)
index 0000000..5004ee3
--- /dev/null
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CheckSum.h
+ *
+*/
+#ifndef ZYPP_CHECKSUM_H
+#define ZYPP_CHECKSUM_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  struct CheckSumException : public Exception
+  {
+    CheckSumException( const std::string & msg )
+      : Exception( msg )
+    {}
+  };
+
+  class CheckSum
+  {
+  public:
+    /**
+     * Creates a checksum for algorithm \param type
+     * \throws CheckSumException if the checksum is invalid and can't be constructed
+     */
+    CheckSum( const std::string & type, const std::string & checksum );
+    CheckSum( const std::string & type, std::istream & input_r );
+    CheckSum();
+
+  public:
+    static const std::string & md5Type();
+    static const std::string & shaType();
+    static const std::string & sha1Type();
+    static const std::string & sha256Type();
+
+    static CheckSum md5( const std::string & checksum )
+    { return  CheckSum( md5Type(), checksum); }
+    static CheckSum sha( const std::string & checksum )
+    { return  CheckSum( shaType(), checksum); }
+    static CheckSum sha1( const std::string & checksum )
+    { return  CheckSum( sha1Type(), checksum); }
+    static CheckSum sha256( const std::string & checksum )
+    { return  CheckSum( sha256Type(), checksum); }
+
+    static CheckSum md5( std::istream & input_r )
+    { return  CheckSum( md5Type(), input_r ); }
+    static CheckSum sha( std::istream & input_r )
+    { return  CheckSum( sha1Type(), input_r ); }
+    static CheckSum sha1( std::istream & input_r )
+    { return  CheckSum( sha1Type(), input_r ); }
+    static CheckSum sha256( std::istream & input_r )
+    { return  CheckSum( sha256Type(), input_r ); }
+
+  public:
+    std::string type() const;
+    std::string checksum() const;
+    bool empty() const;
+
+  public:
+    std::string asString() const;
+
+  private:
+    std::string _type;
+    std::string _checksum;
+  };
+
+  /** \relates CheckSum Stream output. */
+  std::ostream & operator<<( std::ostream & str, const CheckSum & obj );
+
+  /** \relates CheckSum */
+  bool operator==( const CheckSum & lhs, const CheckSum & rhs );
+
+  /** \relates CheckSum */
+  bool operator!=( const CheckSum & lhs, const CheckSum & rhs );
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_CHECKSUM_H
diff --git a/zypp/CountryCode.cc b/zypp/CountryCode.cc
new file mode 100644 (file)
index 0000000..d34c2d5
--- /dev/null
@@ -0,0 +1,462 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CountryCode.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/CountryCode.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /** Wrap static codemap data. */
+    struct CodeMaps // singleton
+    {
+      typedef std::tr1::unordered_map<std::string,std::string> CodeMap;
+      typedef CodeMap::const_iterator Index;
+
+      /** Return the CodeMap Index for \a code_r. */
+      static Index getIndex( const std::string & code_r )
+      {
+        static CodeMaps _maps; // the singleton instance
+        return _maps.lookup( code_r );
+      }
+
+    private:
+      /** Ctor initializes the code maps.
+       * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
+      */
+      CodeMaps();
+
+      /** Make shure the code is in the code maps and return it's index. */
+      inline Index lookup( const std::string & code_r );
+
+    private:
+      /** All the codes. */
+      CodeMap codes;
+    };
+
+    inline CodeMaps::Index CodeMaps::lookup( const std::string & code_r )
+    {
+      Index it = codes.find( code_r );
+      if ( it != codes.end() )
+        return it;
+
+      // not found: Remember a new code
+      CodeMap::value_type nval( code_r, std::string() );
+
+      if ( code_r.size() != 2 )
+        WAR << "Malformed CountryCode '" << code_r << "' (expect 2-letter)" << endl;
+
+      std::string lcode( str::toUpper( code_r ) );
+      if ( lcode != code_r )
+        {
+          WAR << "Malformed CountryCode '" << code_r << "' (not upper case)" << endl;
+          // but maybe we're lucky with the upper case code
+          // and find a country name.
+          it = codes.find( lcode );
+          if ( it != codes.end() )
+            nval.second = it->second;
+        }
+
+      MIL << "Remember CountryCode '" << code_r << "': '" << nval.second << "'" << endl;
+      return codes.insert( nval ).first;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CountryCode::Impl
+  //
+  /** CountryCode implementation.
+   * \note CodeMaps contain the untranslated country names.
+   * Translation is done in \ref name.
+  */
+  struct CountryCode::Impl
+  {
+    Impl()
+    : _index( CodeMaps::getIndex( std::string() ) )
+    {}
+
+    Impl( const std::string & code_r )
+    : _index( CodeMaps::getIndex( code_r ) )
+    {}
+
+    std::string code() const
+    { return _index->first; }
+
+    std::string name() const {
+      if ( _index->second.empty() )
+        {
+          std::string ret( _("Unknown country: ") );
+          ret += "'";
+          ret += _index->first;
+          ret += "'";
+          return ret;
+        }
+      return _( _index->second.c_str() );
+    }
+
+  private:
+    /** index into code map. */
+    CodeMaps::Index _index;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CountryCode
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const CountryCode CountryCode::noCode;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : CountryCode::CountryCode
+  //   METHOD TYPE : Ctor
+  //
+  CountryCode::CountryCode()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : CountryCode::CountryCode
+  //   METHOD TYPE : Ctor
+  //
+  CountryCode::CountryCode( const std::string & code_r )
+  : _pimpl( new Impl( code_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : CountryCode::~CountryCode
+  //   METHOD TYPE : Dtor
+  //
+  CountryCode::~CountryCode()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : CountryCode::code
+  //   METHOD TYPE : std::string
+  //
+  std::string CountryCode::code() const
+  { return _pimpl->code(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : CountryCode::name
+  //   METHOD TYPE : std::string
+  //
+  std::string CountryCode::name() const
+  { return _pimpl->name(); }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    CodeMaps::CodeMaps()
+    {
+      // Defined CountryCode constants
+      codes[""]        = N_( "No Code" );
+
+      struct Init
+      {
+         const char *iso3166;
+         const char *name;
+      };
+
+      const Init init[] = {
+          {"AD", N_( "Andorra" ) },                            // :AND:020:
+          {"AE", N_( "United Arab Emirates" ) },               // :ARE:784:
+          {"AF", N_( "Afghanistan" ) },                        // :AFG:004:
+          {"AG", N_( "Antigua and Barbuda" ) },                // :ATG:028:
+          {"AI", N_( "Anguilla" ) },                           // :AIA:660:
+          {"AL", N_( "Albania" ) },                            // :ALB:008:
+          {"AM", N_( "Armenia" ) },                            // :ARM:051:
+          {"AN", N_( "Netherlands Antilles" ) },               // :ANT:530:
+          {"AO", N_( "Angola" ) },                             // :AGO:024:
+          {"AQ", N_( "Antarctica" ) },                                 // :ATA:010:
+          {"AR", N_( "Argentina" ) },                          // :ARG:032:
+          {"AS", N_( "American Samoa" ) },                     // :ASM:016:
+          {"AT", N_( "Austria" ) },                            // :AUT:040:
+          {"AU", N_( "Australia" ) },                          // :AUS:036:
+          {"AW", N_( "Aruba" ) },                              // :ABW:533:
+          {"AX", N_( "Aland Islands" ) },                      // :ALA:248:
+          {"AZ", N_( "Azerbaijan" ) },                                 // :AZE:031:
+          {"BA", N_( "Bosnia and Herzegovina" ) },             // :BIH:070:
+          {"BB", N_( "Barbados" ) },                           // :BRB:052:
+          {"BD", N_( "Bangladesh" ) },                                 // :BGD:050:
+          {"BE", N_( "Belgium" ) },                            // :BEL:056:
+          {"BF", N_( "Burkina Faso" ) },                       // :BFA:854:
+          {"BG", N_( "Bulgaria" ) },                           // :BGR:100:
+          {"BH", N_( "Bahrain" ) },                            // :BHR:048:
+          {"BI", N_( "Burundi" ) },                            // :BDI:108:
+          {"BJ", N_( "Benin" ) },                              // :BEN:204:
+          {"BM", N_( "Bermuda" ) },                            // :BMU:060:
+          {"BN", N_( "Brunei Darussalam" ) },                  // :BRN:096:
+          {"BO", N_( "Bolivia" ) },                            // :BOL:068:
+          {"BR", N_( "Brazil" ) },                             // :BRA:076:
+          {"BS", N_( "Bahamas" ) },                            // :BHS:044:
+          {"BT", N_( "Bhutan" ) },                             // :BTN:064:
+          {"BV", N_( "Bouvet Island" ) },                      // :BVT:074:
+          {"BW", N_( "Botswana" ) },                           // :BWA:072:
+          {"BY", N_( "Belarus" ) },                            // :BLR:112:
+          {"BZ", N_( "Belize" ) },                             // :BLZ:084:
+          {"CA", N_( "Canada" ) },                             // :CAN:124:
+          {"CC", N_( "Cocos (Keeling) Islands" ) },            // :CCK:166:
+          {"CD", N_( "Congo" ) },                              // :COD:180:
+          {"CF", N_( "Central African Republic" ) },           // :CAF:140:
+          {"CG", N_( "Congo" ) },                              // :COG:178:
+          {"CH", N_( "Switzerland" ) },                        // :CHE:756:
+          {"CI", N_( "Cote D'Ivoire" ) },                      // :CIV:384:
+          {"CK", N_( "Cook Islands" ) },                       // :COK:184:
+          {"CL", N_( "Chile" ) },                              // :CHL:152:
+          {"CM", N_( "Cameroon" ) },                           // :CMR:120:
+          {"CN", N_( "China" ) },                              // :CHN:156:
+          {"CO", N_( "Colombia" ) },                           // :COL:170:
+          {"CR", N_( "Costa Rica" ) },                                 // :CRI:188:
+          {"CU", N_( "Cuba" ) },                               // :CUB:192:
+          {"CV", N_( "Cape Verde" ) },                                 // :CPV:132:
+          {"CX", N_( "Christmas Island" ) },                   // :CXR:162:
+          {"CY", N_( "Cyprus" ) },                             // :CYP:196:
+          {"CZ", N_( "Czech Republic" ) },                     // :CZE:203:
+          {"DE", N_( "Germany" ) },                            // :DEU:276:
+          {"DJ", N_( "Djibouti" ) },                           // :DJI:262:
+          {"DK", N_( "Denmark" ) },                            // :DNK:208:
+          {"DM", N_( "Dominica" ) },                           // :DMA:212:
+          {"DO", N_( "Dominican Republic" ) },                         // :DOM:214:
+          {"DZ", N_( "Algeria" ) },                            // :DZA:012:
+          {"EC", N_( "Ecuador" ) },                            // :ECU:218:
+          {"EE", N_( "Estonia" ) },                            // :EST:233:
+          {"EG", N_( "Egypt" ) },                              // :EGY:818:
+          {"EH", N_( "Western Sahara" ) },                     // :ESH:732:
+          {"ER", N_( "Eritrea" ) },                            // :ERI:232:
+          {"ES", N_( "Spain" ) },                              // :ESP:724:
+          {"ET", N_( "Ethiopia" ) },                           // :ETH:231:
+          {"FI", N_( "Finland" ) },                            // :FIN:246:
+          {"FJ", N_( "Fiji" ) },                               // :FJI:242:
+          {"FK", N_( "Falkland Islands (Malvinas)" ) },        // :FLK:238:
+          {"FM", N_( "Federated States of Micronesia" ) },     // :FSM:583:
+          {"FO", N_( "Faroe Islands" ) },                      // :FRO:234:
+          {"FR", N_( "France" ) },                             // :FRA:250:
+          {"FX", N_( "Metropolitan France" ) },                // :FXX:249:
+          {"GA", N_( "Gabon" ) },                              // :GAB:266:
+          {"GB", N_( "United Kingdom" ) },                     // :GBR:826:
+          {"GD", N_( "Grenada" ) },                            // :GRD:308:
+          {"GE", N_( "Georgia" ) },                            // :GEO:268:
+          {"GF", N_( "French Guiana" ) },                      // :GUF:254:
+          {"GG", N_( "Guernsey" ) },
+          {"GH", N_( "Ghana" ) },                              // :GHA:288:
+          {"GI", N_( "Gibraltar" ) },                          // :GIB:292:
+          {"GL", N_( "Greenland" ) },                          // :GRL:304:
+          {"GM", N_( "Gambia" ) },                             // :GMB:270:
+          {"GN", N_( "Guinea" ) },                             // :GIN:324:
+          {"GP", N_( "Guadeloupe" ) },                                 // :GLP:312:
+          {"GQ", N_( "Equatorial Guinea" ) },                  // :GNQ:226:
+          {"GR", N_( "Greece" ) },                             // :GRC:300:
+          {"GS", N_( "South Georgia and the South Sandwich Islands" ) },       // :SGS:239:
+          {"GT", N_( "Guatemala" ) },                          // :GTM:320:
+          {"GU", N_( "Guam" ) },                               // :GUM:316:
+          {"GW", N_( "Guinea-Bissau" ) },                      // :GNB:624:
+          {"GY", N_( "Guyana" ) },                             // :GUY:328:
+          {"HK", N_( "Hong Kong" ) },                          // :HKG:344:
+          {"HM", N_( "Heard Island and McDonald Islands" ) }, // :HMD:334:
+          {"HN", N_( "Honduras" ) },                           // :HND:340:
+          {"HR", N_( "Croatia" ) },                            // :HRV:191:
+          {"HT", N_( "Haiti" ) },                              // :HTI:332:
+          {"HU", N_( "Hungary" ) },                            // :HUN:348:
+          {"ID", N_( "Indonesia" ) },                          // :IDN:360:
+          {"IE", N_( "Ireland" ) },                            // :IRL:372:
+          {"IL", N_( "Israel" ) },                             // :ISR:376:
+          {"IM", N_( "Isle of Man" ) },
+          {"IN", N_( "India" ) },                              // :IND:356:
+          {"IO", N_( "British Indian Ocean Territory" ) },     // :IOT:086:
+          {"IQ", N_( "Iraq" ) },                               // :IRQ:368:
+          {"IR", N_( "Iran" ) },                               // :IRN:364:
+          {"IS", N_( "Iceland" ) },                            // :ISL:352:
+          {"IT", N_( "Italy" ) },                              // :ITA:380:
+          {"JE", N_( "Jersey" ) },
+          {"JM", N_( "Jamaica" ) },                            // :JAM:388:
+          {"JO", N_( "Jordan" ) },                             // :JOR:400:
+          {"JP", N_( "Japan" ) },                              // :JPN:392:
+          {"KE", N_( "Kenya" ) },                              // :KEN:404:
+          {"KG", N_( "Kyrgyzstan" ) },                                 // :KGZ:417:
+          {"KH", N_( "Cambodia" ) },                           // :KHM:116:
+          {"KI", N_( "Kiribati" ) },                           // :KIR:296:
+          {"KM", N_( "Comoros" ) },                            // :COM:174:
+          {"KN", N_( "Saint Kitts and Nevis" ) },              // :KNA:659:
+          {"KP", N_( "North Korea" ) },                        // :PRK:408:
+          {"KR", N_( "South Korea" ) },                        // :KOR:410:
+          {"KW", N_( "Kuwait" ) },                             // :KWT:414:
+          {"KY", N_( "Cayman Islands" ) },                     // :CYM:136:
+          {"KZ", N_( "Kazakhstan" ) },                                 // :KAZ:398:
+          {"LA", N_( "Lao People's Democratic Republic" ) },   // :LAO:418:
+          {"LB", N_( "Lebanon" ) },                            // :LBN:422:
+          {"LC", N_( "Saint Lucia" ) },                        // :LCA:662:
+          {"LI", N_( "Liechtenstein" ) },                      // :LIE:438:
+          {"LK", N_( "Sri Lanka" ) },                          // :LKA:144:
+          {"LR", N_( "Liberia" ) },                            // :LBR:430:
+          {"LS", N_( "Lesotho" ) },                            // :LSO:426:
+          {"LT", N_( "Lithuania" ) },                          // :LTU:440:
+          {"LU", N_( "Luxembourg" ) },                                 // :LUX:442:
+          {"LV", N_( "Latvia" ) },                             // :LVA:428:
+          {"LY", N_( "Libya" ) },                              // :LBY:434:
+          {"MA", N_( "Morocco" ) },                            // :MAR:504:
+          {"MC", N_( "Monaco" ) },                             // :MCO:492:
+          {"MD", N_( "Moldova" ) },                            // :MDA:498:
+          {"ME", N_( "Montenegro" ) },
+          {"MF", N_( "Saint Martin" ) },
+          {"MG", N_( "Madagascar" ) },                                 // :MDG:450:
+          {"MH", N_( "Marshall Islands" ) },                   // :MHL:584:
+          {"MK", N_( "Macedonia" ) },                          // :MKD:807:
+          {"ML", N_( "Mali" ) },                               // :MLI:466:
+          {"MM", N_( "Myanmar" ) },                            // :MMR:104:
+          {"MN", N_( "Mongolia" ) },                           // :MNG:496:
+          {"MO", N_( "Macao" ) },                              // :MAC:446:
+          {"MP", N_( "Northern Mariana Islands" ) },           // :MNP:580:
+          {"MQ", N_( "Martinique" ) },                                 // :MTQ:474:
+          {"MR", N_( "Mauritania" ) },                                 // :MRT:478:
+          {"MS", N_( "Montserrat" ) },                                 // :MSR:500:
+          {"MT", N_( "Malta" ) },                              // :MLT:470:
+          {"MU", N_( "Mauritius" ) },                          // :MUS:480:
+          {"MV", N_( "Maldives" ) },                           // :MDV:462:
+          {"MW", N_( "Malawi" ) },                             // :MWI:454:
+          {"MX", N_( "Mexico" ) },                             // :MEX:484:
+          {"MY", N_( "Malaysia" ) },                           // :MYS:458:
+          {"MZ", N_( "Mozambique" ) },                                 // :MOZ:508:
+          {"NA", N_( "Namibia" ) },                            // :NAM:516:
+          {"NC", N_( "New Caledonia" ) },                      // :NCL:540:
+          {"NE", N_( "Niger" ) },                              // :NER:562:
+          {"NF", N_( "Norfolk Island" ) },                     // :NFK:574:
+          {"NG", N_( "Nigeria" ) },                            // :NGA:566:
+          {"NI", N_( "Nicaragua" ) },                          // :NIC:558:
+          {"NL", N_( "Netherlands" ) },                        // :NLD:528:
+          {"NO", N_( "Norway" ) },                             // :NOR:578:
+          {"NP", N_( "Nepal" ) },                              // :NPL:524:
+          {"NR", N_( "Nauru" ) },                              // :NRU:520:
+          {"NU", N_( "Niue" ) },                               // :NIU:570:
+          {"NZ", N_( "New Zealand" ) },                        // :NZL:554:
+          {"OM", N_( "Oman" ) },                               // :OMN:512:
+          {"PA", N_( "Panama" ) },                             // :PAN:591:
+          {"PE", N_( "Peru" ) },                               // :PER:604:
+          {"PF", N_( "French Polynesia" ) },                   // :PYF:258:
+          {"PG", N_( "Papua New Guinea" ) },                   // :PNG:598:
+          {"PH", N_( "Philippines" ) },                        // :PHL:608:
+          {"PK", N_( "Pakistan" ) },                           // :PAK:586:
+          {"PL", N_( "Poland" ) },                             // :POL:616:
+          {"PM", N_( "Saint Pierre and Miquelon" ) },          // :SPM:666:
+          {"PN", N_( "Pitcairn" ) },                           // :PCN:612:
+          {"PR", N_( "Puerto Rico" ) },                        // :PRI:630:
+          {"PS", N_( "Palestinian Territory" ) },              // :PSE:275:
+          {"PT", N_( "Portugal" ) },                           // :PRT:620:
+          {"PW", N_( "Palau" ) },                              // :PLW:585:
+          {"PY", N_( "Paraguay" ) },                           // :PRY:600:
+          {"QA", N_( "Qatar" ) },                              // :QAT:634:
+          {"RE", N_( "Reunion" ) },                            // :REU:638:
+          {"RO", N_( "Romania" ) },                            // :ROU:642:
+          {"RS", N_( "Serbia" ) },
+          {"RU", N_( "Russian Federation" ) },                         // :RUS:643:
+          {"RW", N_( "Rwanda" ) },                             // :RWA:646:
+          {"SA", N_( "Saudi Arabia" ) },                       // :SAU:682:
+          {"SB", N_( "Solomon Islands" ) },                    // :SLB:090:
+          {"SC", N_( "Seychelles" ) },                                 // :SYC:690:
+          {"SD", N_( "Sudan" ) },                              // :SDN:736:
+          {"SE", N_( "Sweden" ) },                             // :SWE:752:
+          {"SG", N_( "Singapore" ) },                          // :SGP:702:
+          {"SH", N_( "Saint Helena" ) },                       // :SHN:654:
+          {"SI", N_( "Slovenia" ) },                           // :SVN:705:
+          {"SJ", N_( "Svalbard and Jan Mayen" ) },             // :SJM:744:
+          {"SK", N_( "Slovakia" ) },                           // :SVK:703:
+          {"SL", N_( "Sierra Leone" ) },                       // :SLE:694:
+          {"SM", N_( "San Marino" ) },                                 // :SMR:674:
+          {"SN", N_( "Senegal" ) },                            // :SEN:686:
+          {"SO", N_( "Somalia" ) },                            // :SOM:706:
+          {"SR", N_( "Suriname" ) },                           // :SUR:740:
+          {"ST", N_( "Sao Tome and Principe" ) },              // :STP:678:
+          {"SV", N_( "El Salvador" ) },                        // :SLV:222:
+          {"SY", N_( "Syria" ) },                              // :SYR:760:
+          {"SZ", N_( "Swaziland" ) },                          // :SWZ:748:
+          {"TC", N_( "Turks and Caicos Islands" ) },           // :TCA:796:
+          {"TD", N_( "Chad" ) },                               // :TCD:148:
+          {"TF", N_( "French Southern Territories" ) },        // :ATF:260:
+          {"TG", N_( "Togo" ) },                               // :TGO:768:
+          {"TH", N_( "Thailand" ) },                           // :THA:764:
+          {"TJ", N_( "Tajikistan" ) },                                 // :TJK:762:
+          {"TK", N_( "Tokelau" ) },                            // :TKL:772:
+          {"TM", N_( "Turkmenistan" ) },                       // :TKM:795:
+          {"TN", N_( "Tunisia" ) },                            // :TUN:788:
+          {"TO", N_( "Tonga" ) },                              // :TON:776:
+          {"TL", N_( "East Timor" ) },                                 // :TLS:626:
+          {"TR", N_( "Turkey" ) },                             // :TUR:792:
+          {"TT", N_( "Trinidad and Tobago" ) },                // :TTO:780:
+          {"TV", N_( "Tuvalu" ) },                             // :TUV:798:
+          {"TW", N_( "Taiwan" ) },                             // :TWN:158:
+          {"TZ", N_( "Tanzania" ) },                           // :TZA:834:
+          {"UA", N_( "Ukraine" ) },                            // :UKR:804:
+          {"UG", N_( "Uganda" ) },                             // :UGA:800:
+          {"UM", N_( "United States Minor Outlying Islands" ) },// :UMI:581:
+          {"US", N_( "United States" ) },                      // :USA:840:
+          {"UY", N_( "Uruguay" ) },                            // :URY:858:
+          {"UZ", N_( "Uzbekistan" ) },                                 // :UZB:860:
+          {"VA", N_( "Holy See (Vatican City State)" ) },      // :VAT:336:
+          {"VC", N_( "Saint Vincent and the Grenadines" ) },   // :VCT:670:
+          {"VE", N_( "Venezuela" ) },                          // :VEN:862:
+          {"VG", N_( "British Virgin Islands" ) },             // :VGB:092:
+          {"VI", N_( "Virgin Islands, U.S." ) },               // :VIR:850:
+          {"VN", N_( "Vietnam" ) },                            // :VNM:704:
+          {"VU", N_( "Vanuatu" ) },                            // :VUT:548:
+          {"WF", N_( "Wallis and Futuna" ) },                  // :WLF:876:
+          {"WS", N_( "Samoa" ) },                              // :WSM:882:
+          {"YE", N_( "Yemen" ) },                              // :YEM:887:
+          {"YT", N_( "Mayotte" ) },                            // :MYT:175:
+          {"ZA", N_( "South Africa" ) },                       // :ZAF:710:
+          {"ZM", N_( "Zambia" ) },                             // :ZMB:894:
+          {"ZW", N_( "Zimbabwe" ) },                           // :ZWE:716:
+
+         { NULL, NULL }
+      };
+
+      for (const Init * i = init; i->iso3166 != NULL; ++i)
+         codes[i->iso3166] = i->name;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/CountryCode.h b/zypp/CountryCode.h
new file mode 100644 (file)
index 0000000..e7e915a
--- /dev/null
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/CountryCode.h
+ *
+*/
+#ifndef ZYPP_COUNTRYCODE_H
+#define ZYPP_COUNTRYCODE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class CountryCode;
+  inline bool operator==( const CountryCode & lhs, const CountryCode & rhs );
+  inline bool operator!=( const CountryCode & lhs, const CountryCode & rhs );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : CountryCode
+  //
+  /** Country codes (iso3166-1-alpha-2).
+   *
+   * In fact the class will not prevent to use a non iso country code.
+   * Just a warning will appear in the log.
+  */
+  class CountryCode
+  {
+    friend std::ostream & operator<<( std::ostream & str, const CountryCode & obj );
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor */
+    CountryCode();
+
+    /** Ctor taking a string. */
+    explicit
+    CountryCode( const std::string & code_r );
+
+    /** Dtor */
+    ~CountryCode();
+
+  public:
+
+    /** \name CountryCode constants. */
+    //@{
+    /** No or empty code. */
+    static const CountryCode noCode;
+    //@}
+
+  public:
+    /** Return the country code. */
+    std::string code() const;
+
+    /** Return the country name; if not available the country code. */
+    std::string name() const;
+
+    /** <tt>*this != noCode</tt>. */
+    bool hasCode() const
+    { return *this != noCode; }
+
+  private:
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates CountryCode Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const CountryCode & obj )
+  { return str << obj.code(); }
+
+  /** Comparison based on string value. */
+  //@{
+  /** \relates CountryCode */
+  inline bool operator==( const CountryCode & lhs, const CountryCode & rhs ) {
+    return( lhs.code() == rhs.code() );
+  }
+  /** \relates CountryCode */
+  inline bool operator==( const std::string & lhs, const CountryCode & rhs ) {
+    return( lhs == rhs.code() );
+  }
+  /** \relates CountryCode */
+  inline bool operator==( const CountryCode & lhs, const std::string & rhs ) {
+    return( lhs.code() == rhs );
+  }
+
+  /** \relates CountryCode */
+  inline bool operator!=( const CountryCode & lhs, const CountryCode & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates CountryCode */
+  inline bool operator!=( const std::string & lhs, const CountryCode & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates CountryCode */
+  inline bool operator!=( const CountryCode & lhs, const std::string & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  //@}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+  /** \relates zypp::CountryCode Default order for std::container based on code string value.*/
+  template<>
+    inline bool less<zypp::CountryCode>::operator()( const zypp::CountryCode & lhs, const zypp::CountryCode & rhs ) const
+    { return lhs.code() < rhs.code(); }
+  /////////////////////////////////////////////////////////////////
+} // namespace std
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_COUNTRYCODE_H
diff --git a/zypp/Date.cc b/zypp/Date.cc
new file mode 100644 (file)
index 0000000..9344254
--- /dev/null
@@ -0,0 +1,151 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Date.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/String.h"
+
+#include "zypp/Date.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  static std::string adjustLocale();
+  static void restoreLocale(const std::string & locale);
+
+  namespace
+  {
+    inline bool isDST( struct tm & tm )
+    {
+      time_t t = ::mktime( &tm );
+      struct tm *tm2 = ::localtime( &t );
+      return ( tm2 && tm2->tm_isdst > 0 );
+    }
+  }
+
+  const Date::ValueType Date::second;
+  const Date::ValueType Date::minute;
+  const Date::ValueType Date::hour;
+  const Date::ValueType Date::day;
+  const Date::ValueType Date::month28;
+  const Date::ValueType Date::month29;
+  const Date::ValueType Date::month30;
+  const Date::ValueType Date::month31;
+  const Date::ValueType Date::month;
+  const Date::ValueType Date::year365;
+  const Date::ValueType Date::year366;
+  const Date::ValueType Date::year;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Date::Date
+  //   METHOD TYPE : Constructor
+  //
+  Date::Date( const std::string & seconds_r )
+  { str::strtonum( seconds_r, _date ); }
+
+  Date::Date( const std::string & date_str, const std::string & format )
+    : _date( Date( date_str, format, TB_LOCALTIME ) )
+  {}
+
+  Date::Date( const std::string & date_str, const std::string & format, Date::TimeBase base_r )
+    : _date(0)
+  {
+    struct tm tm = {0,0,0,0,0,0,0,0,0,0,0};
+    std::string thisLocale = adjustLocale();
+
+    char * res = ::strptime( date_str.c_str(), format.c_str(), &tm );
+    if ( isDST(tm) )
+      tm.tm_isdst = 1;
+    if ( res != NULL )
+      _date = (base_r == TB_UTC ? ::timegm : ::timelocal)( &tm );
+
+    restoreLocale(thisLocale);
+
+    if (res == NULL)
+      throw DateFormatException(
+          str::form( "Invalid date format: '%s'", date_str.c_str() ) );
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Date::form
+  //   METHOD TYPE : std::string
+  //
+  std::string Date::form( const std::string & format_r, Date::TimeBase base_r ) const
+  {
+    static char buf[1024];
+    std::string thisLocale = adjustLocale();
+
+    if ( ! strftime( buf, 1024, format_r.c_str(), (base_r == TB_UTC ? gmtime : localtime)( &_date ) ) )
+      *buf = '\0';
+
+    restoreLocale(thisLocale);
+
+    return buf;
+  }
+
+  static std::string adjustLocale()
+  {
+    const char * tmp = ::setlocale( LC_TIME, NULL );
+    std::string thisLocale( tmp ? tmp : "" );
+
+    if (    thisLocale.find( "UTF-8" ) == std::string::npos
+         && thisLocale.find( "utf-8" ) == std::string::npos
+         && thisLocale != "POSIX"
+         && thisLocale != "C"
+         && thisLocale != "" )
+    {
+      // language[_territory][.codeset][@modifier]
+      // add/exchange codeset with UTF-8
+      std::string needLocale = ".UTF-8";
+      std::string::size_type loc = thisLocale.find_first_of( ".@" );
+      if ( loc != std::string::npos )
+      {
+        // prepend language[_territory]
+        needLocale = thisLocale.substr( 0, loc ) + needLocale;
+        loc = thisLocale.find_last_of( "@" );
+        if ( loc != std::string::npos )
+        {
+          // append [@modifier]
+          needLocale += thisLocale.substr( loc );
+        }
+      }
+      else
+      {
+        // append ".UTF-8"
+        needLocale = thisLocale + needLocale;
+      }
+      ::setlocale( LC_TIME, needLocale.c_str() );
+    }
+    else
+    {
+      // no need to change the locale
+      thisLocale.clear();
+    }
+
+    return thisLocale;
+  }
+
+  static void restoreLocale(const std::string & locale)
+  {
+    if ( ! locale.empty() )
+      ::setlocale( LC_TIME, locale.c_str() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Date.h b/zypp/Date.h
new file mode 100644 (file)
index 0000000..9fe7cb9
--- /dev/null
@@ -0,0 +1,147 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Date.h
+ *
+*/
+#ifndef ZYPP_DATE_H
+#define ZYPP_DATE_H
+
+#include <ctime>
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Date
+  //
+  /** Store and operate on date (time_t).
+  */
+  class Date
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Date & obj );
+
+  public:
+
+    typedef time_t ValueType;
+
+    static const ValueType second      = 1;
+    static const ValueType minute      = 60;
+    static const ValueType hour                = 3600;
+    static const ValueType day         = 86400;
+    static const ValueType month28     = 2419200;
+    static const ValueType month29     = 2505600;
+    static const ValueType month30     = 2592000;
+    static const ValueType month31     = 2678400;
+    static const ValueType month       = month30;
+    static const ValueType year365     = 31536000;
+    static const ValueType year366     = 31622400;
+    static const ValueType year                = year365;
+
+    enum TimeBase { TB_LOCALTIME, TB_UTC };
+
+    /** Default ctor: 0 */
+    Date()
+    : _date( 0 )
+    {}
+    /** Ctor taking time_t value. */
+    Date( ValueType date_r )
+    : _date( date_r )
+    {}
+    /** Ctor taking time_t value as string. */
+    Date( const std::string & seconds_r );
+
+    /**
+     * Ctor from a \a date_str (in localtime) formatted using \a format.
+     *
+     * \throws DateFormatException in case \a date_str cannot be
+     *         parsed according to \a format.
+     */
+    Date( const std::string & date_str, const std::string & format );
+    /** \overload with explicitly given \ref TimeBase. */
+    Date( const std::string & date_str, const std::string & format, TimeBase base_r );
+
+    /** Return the current time. */
+    static Date now()
+    { return ::time( 0 ); }
+
+  public:
+    /** Conversion to time_t. */
+    operator ValueType() const
+    { return _date; }
+
+    /** \name Arithmetic operations.
+     * \c + \c - \c * \c / are provided via conversion to time_t.
+    */
+    //@{
+    Date & operator+=( const time_t rhs ) { _date += rhs; return *this; }
+    Date & operator-=( const time_t rhs ) { _date -= rhs; return *this; }
+    Date & operator*=( const time_t rhs ) { _date *= rhs; return *this; }
+    Date & operator/=( const time_t rhs ) { _date /= rhs; return *this; }
+
+    Date & operator++(/*prefix*/) { _date += 1; return *this; }
+    Date & operator--(/*prefix*/) { _date -= 1; return *this; }
+
+    Date operator++(int/*postfix*/) { return _date++; }
+    Date operator--(int/*postfix*/) { return _date--; }
+    //@}
+
+  public:
+    /** Return string representation according to format as localtime.
+     * \see 'man strftime' (which is used internaly) for valid
+     * conversion specifiers in format.
+     *
+     * \return An empty string on illegal format.
+     **/
+    std::string form( const std::string & format_r ) const
+    { return form( format_r, TB_LOCALTIME ); }
+    /** \overload with explicitly given \ref TimeBase. */
+    std::string form( const std::string & format_r, TimeBase base_r ) const;
+
+    /** Default string representation of Date.
+     * The preferred date and time representation for the current locale.
+     **/
+    std::string asString() const
+    { return form( "%c" ); }
+
+    /** Convert to string representation of calendar time in
+     *  numeric form (like "1029255142").
+     **/
+    std::string asSeconds() const
+    { return form( "%s" ); }
+
+  private:
+    /** Calendar time.
+     * The number of seconds elapsed since 00:00:00 on January 1, 1970,
+     * Coordinated Universal Time (UTC).
+     **/
+    ValueType _date;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Date Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Date & obj )
+  { return str << obj.asString(); }
+
+  class DateFormatException : public Exception
+  {
+  public:
+    DateFormatException( const std::string & msg ) : Exception( msg )
+    {}
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_DATE_H
diff --git a/zypp/Dep.cc b/zypp/Dep.cc
new file mode 100644 (file)
index 0000000..5ec642a
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Dep.cc
+ *
+*/
+#include <map>
+#include <iostream>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/String.h"
+
+#include "zypp/Dep.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace
+  {
+
+    std::map<std::string,Dep::for_use_in_switch> _table;
+
+    Dep::for_use_in_switch parse( const std::string & strval_r )
+    {
+      if ( _table.empty() )
+        {
+          // initialize it
+          _table["provides"]    = Dep::PROVIDES_e;
+          _table["prerequires"] = Dep::PREREQUIRES_e;
+          _table["requires"]    = Dep::REQUIRES_e;
+          _table["conflicts"]   = Dep::CONFLICTS_e;
+          _table["obsoletes"]   = Dep::OBSOLETES_e;
+          _table["recommends"]  = Dep::RECOMMENDS_e;
+          _table["suggests"]    = Dep::SUGGESTS_e;
+          _table["enhances"]    = Dep::ENHANCES_e;
+          _table["supplements"]        = Dep::SUPPLEMENTS_e;
+        }
+
+      std::map<std::string,Dep::for_use_in_switch>::const_iterator it
+      = _table.find( str::toLower( strval_r ) );
+      if ( it == _table.end() )
+        {
+          ZYPP_THROW( Exception("Dep parse: illegal string value '"+strval_r+"'") );
+        }
+      return it->second;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  const Dep Dep::PROVIDES   ( Dep::PROVIDES_e );
+  const Dep Dep::PREREQUIRES( Dep::PREREQUIRES_e );
+  const Dep Dep::REQUIRES   ( Dep::REQUIRES_e );
+  const Dep Dep::CONFLICTS  ( Dep::CONFLICTS_e );
+  const Dep Dep::OBSOLETES  ( Dep::OBSOLETES_e );
+  const Dep Dep::RECOMMENDS ( Dep::RECOMMENDS_e );
+  const Dep Dep::SUGGESTS   ( Dep::SUGGESTS_e );
+  const Dep Dep::ENHANCES   ( Dep::ENHANCES_e );
+  const Dep Dep::SUPPLEMENTS( Dep::SUPPLEMENTS_e );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Dep::Dep
+  //   METHOD TYPE : Ctor
+  //
+  Dep::Dep( const std::string & strval_r )
+  : _type( parse( strval_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Dep::asString
+  //   METHOD TYPE : const std::string &
+  //
+  const std::string & Dep::asString() const
+  {
+    static std::map<for_use_in_switch,std::string> _table;
+    if ( _table.empty() )
+      {
+        // initialize it
+        _table[PROVIDES_e]    = "provides";
+        _table[PREREQUIRES_e] = "prerequires";
+        _table[REQUIRES_e]    = "requires";
+        _table[CONFLICTS_e]   = "conflicts";
+        _table[OBSOLETES_e]   = "obsoletes";
+        _table[RECOMMENDS_e]  = "recommends";
+        _table[SUGGESTS_e]    = "suggests";
+        _table[ENHANCES_e]    = "enhances";
+        _table[SUPPLEMENTS_e] = "supplements";
+      }
+    return _table[_type];
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Dep.h b/zypp/Dep.h
new file mode 100644 (file)
index 0000000..5ff7d85
--- /dev/null
@@ -0,0 +1,118 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Dep.h
+ *
+*/
+#ifndef ZYPP_DEP_H
+#define ZYPP_DEP_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Dep
+  //
+  /** Enumeration class of dependency types.
+   * \ingroup g_EnumerationClass
+  */
+  struct Dep
+  {
+    friend bool operator==( const Dep & lhs, const Dep & rhs );
+    friend bool operator!=( const Dep & lhs, const Dep & rhs );
+    /** Arbitrary order to allow Dep as key in std::container. */
+    friend bool operator<( const Dep & lhs, const Dep & rhs );
+
+    /** \name Dependency types
+     * These are the \em real dependency type contants to
+     * use. Don't mind that it's not an enum.
+     * \see \ref zypp::Dep::inSwitch
+    */
+    //@{
+    static const Dep PROVIDES;
+    static const Dep PREREQUIRES;
+    static const Dep REQUIRES;
+    static const Dep CONFLICTS;
+    static const Dep OBSOLETES;
+    static const Dep RECOMMENDS;
+    static const Dep SUGGESTS;
+    static const Dep ENHANCES;
+    static const Dep SUPPLEMENTS;
+    //@}
+
+    /** Enumarators provided \b only for use \ref inSwitch statement.
+     * \see inSwitch
+    */
+    enum for_use_in_switch {
+      PROVIDES_e,
+      PREREQUIRES_e,
+      REQUIRES_e,
+      CONFLICTS_e,
+      OBSOLETES_e,
+      RECOMMENDS_e,
+      SUGGESTS_e,
+      ENHANCES_e,
+      SUPPLEMENTS_e,
+    };
+
+    /** Ctor from string.
+     * Legal values for \a strval_r are the constants names
+     * (case insignificant).
+     *
+     * \throw PARSE if \a strval_r is not legal.
+     * \todo refine exceptions and check throw.
+    */
+    explicit
+    Dep( const std::string & strval_r );
+
+    /** String representation of dependency type.
+     * \return The constants names lowercased.
+    */
+    const std::string & asString() const;
+
+    /** Enumarator provided for use in \c switch statement. */
+    for_use_in_switch inSwitch() const
+    { return _type; }
+
+  private:
+    /** Ctor to initialize the dependency type contants. */
+    Dep( for_use_in_switch type_r )
+    : _type( type_r )
+    {}
+    /** The operator. */
+    for_use_in_switch _type;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Dep Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Dep & obj )
+  { return str << obj.asString(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Dep */
+  inline bool operator==( const Dep & lhs, const Dep & rhs )
+  { return lhs._type == rhs._type; }
+
+  /** \relates Dep */
+  inline bool operator!=( const Dep & lhs, const Dep & rhs )
+  { return lhs._type != rhs._type; }
+
+  /** \relates Dep */
+  inline bool operator<( const Dep & lhs, const Dep & rhs )
+  { return lhs._type < rhs._type; }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_DEP_H
diff --git a/zypp/Digest.cc b/zypp/Digest.cc
new file mode 100644 (file)
index 0000000..fcbe997
--- /dev/null
@@ -0,0 +1,298 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Digest.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#include <cstdio> // snprintf
+#include <openssl/evp.h>
+#include <string>
+#include <string.h>
+
+#include <iostream>
+#include <sstream>
+
+#ifdef DIGEST_TESTSUITE
+#include <fstream>
+#endif
+
+#include "zypp/Digest.h"
+
+namespace zypp {
+
+    bool DigestReport::askUserToAcceptNoDigest( const zypp::Pathname &file )
+    { return false; }
+
+    bool DigestReport::askUserToAccepUnknownDigest( const Pathname &file, const std::string &name )
+    { return false; }
+
+    bool DigestReport::askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found )
+    { return false; }
+
+
+    const std::string & Digest::md5()
+    { static std::string _type( "md5" ); return _type; }
+
+    const std::string & Digest::sha1()
+    { static std::string _type( "sha1" ); return _type; }
+
+    const std::string & Digest::sha256()
+    { static std::string _type( "sha256" ); return _type; }
+
+    // private data
+    class Digest::P
+    {
+       P(const P& p);
+       const P& operator=(const P& p);
+      public:
+       P();
+       ~P();
+
+       EVP_MD_CTX mdctx;
+
+       const EVP_MD *md;
+       unsigned char md_value[EVP_MAX_MD_SIZE];
+       unsigned md_len;
+
+       bool initialized : 1;
+       bool finalized : 1;
+       static bool openssl_digests_added;
+
+       std::string name;
+
+       inline bool maybeInit();
+       inline void cleanup();
+    };
+
+
+    using namespace std;
+
+    bool Digest::P::openssl_digests_added = false;
+
+    Digest::P::P() :
+      md(NULL),
+      initialized(false),
+      finalized(false)
+    {
+    }
+
+    Digest::P::~P()
+    {
+      cleanup();
+    }
+
+    bool Digest::P::maybeInit()
+    {
+      if(!openssl_digests_added)
+      {
+       OpenSSL_add_all_digests();
+       openssl_digests_added = true;
+      }
+
+      if(!initialized)
+      {
+       md = EVP_get_digestbyname(name.c_str());
+       if(!md)
+           return false;
+
+       EVP_MD_CTX_init(&mdctx);
+
+       if(!EVP_DigestInit_ex(&mdctx, md, NULL))
+           return false;
+
+       md_len = 0;
+       ::memset(md_value, 0, sizeof(md_value));
+       initialized = true;
+      }
+      return true;
+    }
+
+    void Digest::P::cleanup()
+    {
+      if(initialized)
+      {
+       EVP_MD_CTX_cleanup(&mdctx);
+       initialized = false;
+       finalized = false;
+      }
+    }
+
+    Digest::Digest() : _dp(new P())
+    {
+    }
+
+    Digest::~Digest()
+    {
+      delete _dp;
+    }
+
+    bool Digest::create(const std::string& name)
+    {
+      if(name.empty()) return false;
+
+      if(_dp->initialized)
+       _dp->cleanup();
+
+      _dp->name = name;
+
+      return _dp->maybeInit();
+    }
+
+    const std::string& Digest::name()
+    {
+      return _dp->name;
+    }
+
+    bool Digest::reset()
+    {
+      if (!_dp->initialized)
+       return false;
+      if(!_dp->finalized)
+       {
+         (void)EVP_DigestFinal_ex(&_dp->mdctx, _dp->md_value, &_dp->md_len);
+          _dp->finalized = true;
+       }
+      if(!EVP_DigestInit_ex(&_dp->mdctx, _dp->md, NULL))
+       return false;
+      _dp->finalized = false;
+      return true;
+    }
+
+    std::string Digest::digest()
+    {
+      if(!_dp->maybeInit())
+       return false;
+
+      if(!_dp->finalized)
+      {
+       if(!EVP_DigestFinal_ex(&_dp->mdctx, _dp->md_value, &_dp->md_len))
+           return false;
+
+       _dp->finalized = true;
+      }
+
+      char mdtxt[_dp->md_len*2 + 1];
+      mdtxt[_dp->md_len*2] = '\0';
+
+      for(unsigned i = 0; i < _dp->md_len; ++i)
+      {
+       ::snprintf(mdtxt + i*2, 3, "%02hhx", _dp->md_value[i]);
+      }
+
+      return std::string(mdtxt);
+    }
+
+    std::vector<unsigned char> Digest::digestVector()
+    {
+      std::vector<unsigned char> r;
+      if(!_dp->maybeInit())
+        return r;
+
+      if(!_dp->finalized)
+      {   
+        if(!EVP_DigestFinal_ex(&_dp->mdctx, _dp->md_value, &_dp->md_len))
+            return r;
+        _dp->finalized = true;
+      }   
+      r.reserve(_dp->md_len);
+      for(unsigned i = 0; i < _dp->md_len; ++i)
+       r.push_back(_dp->md_value[i]);
+      return r;
+    }
+
+    bool Digest::update(const char* bytes, size_t len)
+    {
+      if(!bytes)
+      {
+       return false;
+      }
+
+      if(!_dp->maybeInit())
+       return false;
+
+      if(_dp->finalized)
+      {
+       _dp->cleanup();
+       if(!_dp->maybeInit())
+           return false;
+
+      }
+      if(!EVP_DigestUpdate(&_dp->mdctx, reinterpret_cast<const unsigned char*>(bytes), len))
+       return false;
+
+      return true;
+    }
+
+    std::string Digest::digest(const std::string& name, std::istream& is, size_t bufsize)
+    {
+      if(name.empty() || !is)
+       return string();
+
+      char buf[bufsize];
+
+      Digest digest;
+      if(!digest.create(name))
+       return string();
+
+
+      while(is.good())
+      {
+        int readed;
+        is.read(buf, bufsize);
+       readed = is.gcount();
+        if(readed && !digest.update(buf, readed))
+           return string();
+      }
+
+      return digest.digest();
+    }
+
+    std::string Digest::digest( const std::string & name, const std::string & input, size_t bufsize )
+    {
+      istringstream is( input );
+      return digest( name, is, bufsize );
+    }
+
+#ifdef DIGEST_TESTSUITE
+    int main(int argc, char *argv[])
+    {
+      bool openssl = false;
+      unsigned argpos = 1;
+
+      if(argc > 1 && string(argv[argpos]) == "--openssl")
+      {
+       openssl = true;
+       ++argpos;
+      }
+
+      if(argc - argpos < 2)
+      {
+       cerr << "Usage: " << argv[0] << " <DIGESTNAME> <FILE>" << endl;
+       return 1;
+      }
+
+      const char* digestname = argv[argpos++];
+      const char* fn = argv[argpos++];
+
+      ifstream file(fn);
+
+      string digest = Digest::digest(digestname, file);
+
+      if(openssl)
+       cout << digestname << "(" << fn << ")= " << digest << endl;
+      else
+       cout << digest << "  " << fn << endl;
+
+      return 0;
+    }
+#endif
+
+} // namespace zypp
diff --git a/zypp/Digest.h b/zypp/Digest.h
new file mode 100644 (file)
index 0000000..69556bf
--- /dev/null
@@ -0,0 +1,139 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Digest.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#ifndef ZYPP_MEDIA_DIGEST_H
+#define ZYPP_MEDIA_DIGEST_H
+
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+#include <zypp/Callback.h>
+#include <zypp/Pathname.h>
+
+namespace zypp {
+
+
+  struct DigestReport : public callback::ReportBase
+  {
+    virtual bool askUserToAcceptNoDigest( const zypp::Pathname &file );
+    virtual bool askUserToAccepUnknownDigest( const Pathname &file, const std::string &name );
+    virtual bool askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found );
+  };
+
+
+
+    /** \brief Compute Message Digests (MD5, SHA1 etc)
+     *
+     * The computation works by initializing the algorithm using create(). This
+     * will construct an internal state. successive calls to update() deliver the
+     * data for which the digest is to be computed. After all data has been
+     * deliverd, a call to digest() finalizes the computation and returns the
+     * result
+     * */
+    class Digest
+    {
+      private:
+       class P;
+       P* _dp;
+
+       // disabled
+       Digest(const Digest& d);
+       // disabled
+       const Digest& operator=(const Digest& d);
+
+      public:
+       /** \name Well known digest algorithm names. */
+       //@{
+       /** md5 */
+       static const std::string & md5();
+       /** sha1 */
+       static const std::string & sha1();
+       /** sha256 */
+       static const std::string & sha256();
+       //@}
+
+      public:
+       Digest();
+       ~Digest();
+
+       /** \brief initialize creation of a new message digest
+        *
+        * Since openssl is used as backend you may use anything that openssl
+        * supports (see man 1 dgst). Common examples are md5 or sha1. sha1
+        * should be preferred when creating digests to verify the authenticity
+        * of something.
+        *
+        * successive calls to this funcion will destroy the internal state and
+        * reinit from scratch
+        *
+        * @param name name of the message digest algorithm.
+        * @return whether an error occured
+        * */
+       bool create(const std::string& name);
+
+       /** \brief get the name of the current digest algorithm */
+       const std::string& name();
+
+       /** \brief feed data into digest computation algorithm
+        * @param bytes
+        * @param len
+        * @return whether an error occured
+        * */
+       bool update(const char* bytes, size_t len);
+
+       /** \brief get hex string representation of the digest
+        *
+        * this function will finalize the digest computation. calls to update
+        * after this function will start from scratch
+        *
+        * @return hex string representation of the digest
+        * */
+       std::string digest();
+
+       /** \brief get vector of unsigned char representation of the digest
+        *
+        * this function will finalize the digest computation. calls to update
+        * after this function will start from scratch
+        *
+        * @return vector representation of the digest
+        * */
+       std::vector<unsigned char> digestVector();
+
+       /** \brief reset internal digest state
+        *
+        * this function is equivalent to calling create() with an unchanged name,
+        * but it may be implemented in a more efficient way.
+        */
+       bool reset();
+
+       /** \brief compute digest of a stream. convenience function
+        *
+        * calls create, update and digest in one function. The data for the
+        * computation is read from the stream
+        *
+        * @param name name of the digest algorithm, \see create
+        * @param is an input stream to get the data from
+        * @param bufsize size of the buffer used for update(). Be careful, this is on the stack.
+        * @return the digest or empty on error
+        * */
+       static std::string digest(const std::string& name, std::istream& is, size_t bufsize = 4096);
+
+       /** \overload Reading input data from \c string. */
+       static std::string digest( const std::string & name, const std::string & input, size_t bufsize = 4096 );
+    };
+
+} // namespace zypp
+
+#endif
diff --git a/zypp/DiskUsage.cc b/zypp/DiskUsage.cc
new file mode 100644 (file)
index 0000000..61af853
--- /dev/null
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DiskUsage.cc
+ *
+*/
+#include "zypp/DiskUsage.h"
+#include <iostream>
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  std::ostream & operator<<( std::ostream & str, const DiskUsage::Entry & obj )
+  {
+    return str << obj.path << '\t' << obj._size << "; files " << obj._files;
+  }
+
+  DiskUsage::Entry DiskUsage::extract( const std::string & dirname_r )
+  {
+    Entry ret( dirname_r );
+
+    iterator fst = begin();
+    for ( ; fst != end() && !fst->isBelow( ret ); ++fst )
+      ; // seek 1st equal or below
+
+    bool found = false;
+    if ( fst != end() ) {
+      iterator lst = fst;
+      found = true;
+      // return the first found, the value is sum of all subdirectories below
+      ret += *lst;
+      for ( ; lst != end() && lst->isBelow( ret ); ++lst ) {
+      }
+      // remove
+      _dirs.erase( fst, lst );
+    }
+
+    // the dir entry has been found, update all parent entries
+    if (found)
+    {
+       std::string dname = dirname_r;
+       if (dname.size() > 1 && dname[0] != '/')
+       {
+           dname.insert(dname.begin(), '/');
+       }
+
+       Entry tmp( dname );
+
+       tmp._size = ret._size;
+       tmp._files = ret._files;
+       // subtract du from directories above
+       iterator fst = begin();
+       for ( ; fst != end(); ++fst )
+       {
+           // add slash if it's missing
+           std::string dd = fst->path;
+           if (dd.size() > 1 && dd[0] != '/')
+           {
+               dd.insert(dd.begin(), '/');
+           }
+
+           // update the entry
+           if (tmp.isBelow(dd))
+           {
+               *fst -= tmp;
+           }
+       }
+    }
+
+    return ret;
+  }
+
+  std::ostream & operator<<( std::ostream & str, const DiskUsage & obj )
+  {
+    str << "Package Disk Usage {" << endl;
+    for ( DiskUsage::EntrySet::const_iterator it = obj._dirs.begin(); it != obj._dirs.end(); ++it ) {
+      str << "   " << *it << endl;
+    }
+    return str << "}";
+  }
+
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/DiskUsage.h b/zypp/DiskUsage.h
new file mode 100644 (file)
index 0000000..18fe1a9
--- /dev/null
@@ -0,0 +1,178 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DiskUsage.h
+ *
+*/
+#ifndef ZYPP_DISKUSAGE_H
+#define ZYPP_DISKUSAGE_H
+
+#include <set>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class DiskUsage {
+  public:
+    /**
+    * @short Holds data about how much space will be needed per directory.
+    **/
+    struct Entry {
+      Entry() : _size(0), _files(0) {};
+      Entry(const std::string& path_r,
+              const unsigned size_r = 0,
+              const unsigned files_r = 0)
+      : path(path_r), _size(size_r), _files(files_r)
+      {
+        // assert leading and trailing '/'
+        if ( ! path.empty() )
+        {
+          if ( *path.begin() != '/' ) path.insert( path.begin(), '/' );
+          if ( *path.rbegin() != '/' ) path.insert( path.end(), '/' );
+        }
+      }
+      std::string path;
+      mutable unsigned _size;
+      mutable unsigned _files;
+      friend std::ostream & operator<<( std::ostream & str, const Entry & obj );
+      /**
+       * Test for equality based on directory name.
+       **/
+      bool operator==( const Entry & rhs ) const {
+        return  path == rhs.path;
+      }
+      /**
+       * Order based on directory name.
+       **/
+      bool operator<( const Entry & rhs ) const {
+        return  path < rhs.path;
+      }
+      /**
+       * Return true if this entry denotes a directory equal to or below rhs._dirname.
+       **/
+      bool isBelow( const Entry & rhs ) const {
+        // whether _dirname has prefix rhs._dirname
+        return( path.compare( 0, rhs.path.size(), rhs.path ) == 0 );
+      }
+      /**
+       * Return true if this entry denotes a directory equal to or below dirname_r.
+       **/
+      bool isBelow( const std::string & dirname_r ) const {
+        return  isBelow( Entry( dirname_r ) );
+      }
+
+      /**
+       * Numerical operation based on size and files values.
+       **/
+      const Entry & operator+=( const Entry & rhs ) const {
+        _size  += rhs._size;
+        _files += rhs._files;
+        return *this;
+      }
+      /**
+       * Numerical operation based on size and files values.
+       **/
+      const Entry & operator-=( const Entry & rhs ) const {
+        _size  -= rhs._size;
+        _files -= rhs._files;
+        return *this;
+      }
+    };
+  private:
+    typedef std::set<Entry> EntrySet;
+    EntrySet _dirs;
+  public:
+
+    DiskUsage() {};
+   /**
+     * Add an entry. If already present, sum up the new entries size and files value.
+     **/
+    void add( const Entry & newent_r ) {
+      std::pair<EntrySet::iterator,bool> res = _dirs.insert( newent_r );
+      if ( !res.second ) {
+        *res.first += newent_r;
+      }
+    }
+   /**
+     * Add an entry. If already present, sum up the new entries size and files value.
+     **/
+    void add( const std::string & dirname_r, const unsigned & size_r = 0, const unsigned & files_r = 0 ) {
+      add( Entry( dirname_r, size_r, files_r ) );
+    }
+    /**
+     * Whether there is no entry available.
+     */
+    bool empty() const { return _dirs.empty(); }
+    /**
+     * Number of entries
+     **/
+    unsigned size() const { return _dirs.size(); }
+    /**
+     * Clear EntrySet
+     **/
+    void clear() { _dirs.clear(); }
+    /**
+     * Sum up any entries for dirname_r and its descendants and remove them
+     * on the fly. Return the result.
+     **/
+    Entry extract( const std::string & dirname_r );
+
+  public:
+
+    typedef EntrySet::iterator               iterator;
+    typedef EntrySet::reverse_iterator       reverse_iterator;
+
+    /**
+     * Forward iterator pointing to the first entry (if any)
+     **/
+    iterator begin() { return _dirs.begin(); }
+    /**
+     * Forward iterator pointing behind the last entry.
+     **/
+    iterator end() { return _dirs.end(); }
+    /**
+     * Reverse iterator pointing to the last entry (if any)
+     **/
+    reverse_iterator rbegin() { return _dirs.rbegin(); }
+    /**
+     * Reverse iterator pointing before the first entry.
+     **/
+    reverse_iterator rend() { return _dirs.rend(); }
+
+    typedef EntrySet::const_iterator         const_iterator;
+    typedef EntrySet::const_reverse_iterator const_reverse_iterator;
+
+    /**
+     * Forward const iterator pointing to the first entry (if any)
+     **/
+    const_iterator begin() const { return _dirs.begin(); }
+    /**
+     * Forward const iterator pointing behind the last entry.
+     **/
+    const_iterator end() const { return _dirs.end(); }
+    /**
+     * Reverse const iterator pointing to the last entry (if any)
+     **/
+    const_reverse_iterator rbegin() const { return _dirs.rbegin(); }
+    /**
+     * Reverse const iterator pointing before the first entry.
+     **/
+    const_reverse_iterator rend()const { return _dirs.rend(); }
+
+  public:
+
+    friend std::ostream & operator<<( std::ostream & str, const DiskUsage & obj );
+
+  };
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_DISKUSAGE_H
diff --git a/zypp/DiskUsageCounter.cc b/zypp/DiskUsageCounter.cc
new file mode 100644 (file)
index 0000000..98c4883
--- /dev/null
@@ -0,0 +1,297 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DiskUsageCounter.cc
+ *
+ */
+extern "C"
+{
+#include <sys/statvfs.h>
+}
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+#include "zypp/DiskUsageCounter.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/detail/PoolImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    struct SatMap : private base::NonCopyable
+    {
+      SatMap( unsigned capacity_r = 1 )
+      {
+        ::map_init( &_installedmap, sat::Pool::instance().capacity() );
+      }
+
+      void add( sat::Solvable solv_r )
+      {
+        MAPSET( &_installedmap, solv_r.id() );
+      }
+
+      void add( const PoolItem & pi_r )
+      { add( pi_r->satSolvable() ); }
+
+      void add( const ResObject::constPtr & obj_r )
+      { add( obj_r->satSolvable() ); }
+
+      mutable ::Map _installedmap;
+    };
+
+    DiskUsageCounter::MountPointSet calcDiskUsage( const DiskUsageCounter::MountPointSet & mps_r, const SatMap & installedmap_r )
+    {
+      DiskUsageCounter::MountPointSet result = mps_r;
+
+      if ( result.empty() )
+      {
+        // partitioning is not set
+        return result;
+      }
+
+      sat::Pool satpool( sat::Pool::instance() );
+
+      // init satsolver result vector with mountpoints
+      static const ::DUChanges _initdu = { 0, 0, 0 };
+      std::vector< ::DUChanges> duchanges( result.size(), _initdu );
+      {
+        unsigned idx = 0;
+        for_( it, result.begin(), result.end() )
+        {
+          duchanges[idx].path = it->dir.c_str();
+          ++idx;
+        }
+      }
+
+      // now calc...
+      ::pool_calc_duchanges( satpool.get(),
+                             &installedmap_r._installedmap,
+                             &duchanges[0],
+                             duchanges.size() );
+
+      // and process the result
+      {
+        unsigned idx = 0;
+        for_( it, result.begin(), result.end() )
+        {
+          static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / (2 * 1K)
+
+          it->pkg_size = it->used_size          // current usage
+                       + duchanges[idx].kbytes  // package data size
+                       + ( duchanges[idx].files * it->block_size / blockAdjust ); // half block per file
+          ++idx;
+        }
+      }
+
+      return result;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
+  {
+    SatMap installedmap( sat::Pool::instance().capacity() );
+    // build installedmap (installed != transact)
+    // stays installed or gets installed
+    for_( it, pool_r.begin(), pool_r.end() )
+    {
+      if ( it->status().isInstalled() != it->status().transacts() )
+      {
+        installedmap.add( *it );
+      }
+    }
+    return calcDiskUsage( mps, installedmap );
+  }
+
+  DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r )
+  {
+    SatMap installedmap;
+    installedmap.add( solv_r );
+    return calcDiskUsage( mps, installedmap );
+  }
+
+  DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
+  {
+    DiskUsageCounter::MountPointSet ret;
+
+      std::ifstream procmounts( "/proc/mounts" );
+
+      if ( !procmounts ) {
+       WAR << "Unable to read /proc/mounts" << std::endl;
+      } else {
+
+       std::string prfx;
+       if ( rootdir != "/" )
+         prfx = rootdir; // rootdir not /
+
+       while ( procmounts ) {
+         std::string l = str::getline( procmounts );
+         if ( !(procmounts.fail() || procmounts.bad()) ) {
+           // data to consume
+
+           // rootfs   /               rootfs          rw 0 0
+           // /dev/root        /               reiserfs        rw 0 0
+           // proc     /proc           proc            rw 0 0
+           // devpts   /dev/pts        devpts          rw 0 0
+           // /dev/hda5        /boot           ext2            rw 0 0
+           // shmfs    /dev/shm        shm             rw 0 0
+           // usbdevfs         /proc/bus/usb   usbdevfs        rw 0 0
+
+           std::vector<std::string> words;
+           str::split( l, std::back_inserter(words) );
+
+           if ( words.size() < 3 ) {
+             WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
+             continue;
+           }
+
+           //
+           // Filter devices without '/' (proc,shmfs,..)
+           //
+           if ( words[0].find( '/' ) == std::string::npos ) {
+             DBG << "Discard mount point : " << l << std::endl;
+             continue;
+           }
+
+           // remove /proc entry
+           if (words[0] == "/proc")
+           {
+             DBG << "Discard /proc filesystem: " << l << std::endl;
+             continue;
+           }
+
+           //
+           // Filter mountpoints not at or below _rootdir
+           //
+           std::string mp = words[1];
+           if ( prfx.size() ) {
+             if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
+               // mountpoint not below rootdir
+               DBG << "Unwanted mount point : " << l << std::endl;
+               continue;
+             }
+             // strip prfx
+             mp.erase( 0, prfx.size() );
+             if ( mp.empty() ) {
+               mp = "/";
+             } else if ( mp[0] != '/' ) {
+               // mountpoint not below rootdir
+               DBG << "Unwanted mount point : " << l << std::endl;
+               continue;
+             }
+           }
+
+           //
+           // Filter cdrom
+           //
+           if ( words[2] == "iso9660" ) {
+             DBG << "Discard cdrom : " << l << std::endl;
+             continue;
+           }
+
+           if ( words[2] == "vfat" || words[2] == "fat" || words[2] == "ntfs" || words[2] == "ntfs-3g")
+           {
+             MIL << words[1] << " contains ignored fs (" << words[2] << ')' << std::endl;
+             continue;
+           }
+
+           //
+           // Filter some common unwanted mountpoints
+           //
+           const char * mpunwanted[] = {
+             "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
+             "/suse", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
+             /*last*/0/*entry*/
+           };
+
+           const char ** nomp = mpunwanted;
+           for ( ; *nomp; ++nomp ) {
+             std::string pre( *nomp );
+             if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
+                  && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
+               break;
+             }
+           }
+           if ( *nomp ) {
+             DBG << "Filter mount point : " << l << std::endl;
+             continue;
+           }
+
+           //
+           // Check whether mounted readonly
+           //
+           bool ro = false;
+           std::vector<std::string> flags;
+           str::split( words[3], std::back_inserter(flags), "," );
+
+           for ( unsigned i = 0; i < flags.size(); ++i ) {
+             if ( flags[i] == "ro" ) {
+               ro = true;
+               break;
+             }
+           }
+            if ( ro ) {
+             DBG << "Filter ro mount point : " << l << std::endl;
+             continue;
+           }
+
+           //
+           // statvfs (full path!) and get the data
+           //
+           struct statvfs sb;
+           if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
+             WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
+             ret.insert( DiskUsageCounter::MountPoint( mp ) );
+           }
+           else
+           {
+             ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
+               ((long long)sb.f_blocks)*sb.f_bsize/1024,
+               ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
+           }
+         }
+       }
+    }
+
+    return ret;
+  }
+
+  DiskUsageCounter::MountPointSet DiskUsageCounter::justRootPartition()
+  {
+    DiskUsageCounter::MountPointSet ret;
+    ret.insert( DiskUsageCounter::MountPoint() );
+    return ret;
+  }
+
+  std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
+  {
+     str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
+        << " ts: " << obj.totalSize()
+        << " us: " << obj.usedSize()
+        << " (+-: " << obj.commitDiff()
+        << ")]";
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/DiskUsageCounter.h b/zypp/DiskUsageCounter.h
new file mode 100644 (file)
index 0000000..553f48a
--- /dev/null
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DiskUsageCounter.h
+ *
+*/
+#ifndef ZYPP_DISKUSAGE_COUNTER_H
+#define ZYPP_DISKUSAGE_COUNTER_H
+
+#include <zypp/ResPool.h>
+
+#include <set>
+#include <string>
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class DiskUsageCounter
+  {
+
+  public:
+
+    /**
+    * @short Mount point description
+    **/
+    struct MountPoint
+    {
+      /**
+       * Directory name
+       **/
+      std::string dir;
+
+      /**
+       * Block size of the mount point
+       **/
+      long long block_size;
+
+      /**
+       * Total size in K (1024)
+       **/
+      long long total_size;
+
+      /**
+       * Used size in K (1024)
+       **/
+      long long used_size;
+
+      /**
+       * Used size after commiting the pool (in K)
+       **/
+      mutable long long pkg_size;
+
+      bool readonly;
+
+      /**
+       * Ctor - initialize directory and package size
+       **/
+      MountPoint(std::string d = "/", long long bs = 0LL, long long total = 0LL, long long used = 0LL, long long pkg = 0LL, bool ro=false) :
+        dir(d), block_size(bs), total_size(total), used_size(used), pkg_size(pkg), readonly(ro)
+      {}
+
+      // sort by directory name
+      bool operator<( const MountPoint & rhs ) const
+      {
+        return dir < rhs.dir;
+      }
+
+      ByteCount blockSize() const
+      { return ByteCount( block_size, ByteCount::B ); }
+
+      ByteCount totalSize() const
+      { return ByteCount( total_size, ByteCount::K ); }
+
+      ByteCount usedSize() const
+      { return ByteCount( used_size, ByteCount::K ); }
+
+      ByteCount freeSize() const
+      { return ByteCount( total_size-used_size, ByteCount::K ); }
+
+      ByteCount usedAfterCommit() const
+      { return ByteCount( pkg_size, ByteCount::K ); }
+
+      ByteCount freeAfterCommit() const
+      { return ByteCount( total_size-pkg_size, ByteCount::K ); }
+
+      ByteCount commitDiff() const
+      { return ByteCount( pkg_size-used_size, ByteCount::K ); }
+    };
+
+    typedef std::set<MountPoint> MountPointSet;
+
+    DiskUsageCounter()
+    {}
+
+    DiskUsageCounter( const MountPointSet & m )
+    : mps( m )
+    {}
+
+    bool setMountPoints( const MountPointSet & m )
+    {
+       mps = m;
+       return true;
+    }
+
+    const MountPointSet & getMountPoints() const
+    {
+       return mps;
+    }
+
+    static MountPointSet detectMountPoints(const std::string &rootdir = "/");
+
+    static MountPointSet justRootPartition();
+
+    /**
+     * Compute disk usage of the pool
+     **/
+    MountPointSet disk_usage( const ResPool & pool );
+
+    /**
+     * Compute disk usage of one solvable
+     */
+    MountPointSet disk_usage( sat::Solvable solv_r );
+    /** \overload */
+    MountPointSet disk_usage( const PoolItem & pi_r  )
+    { return disk_usage( pi_r.satSolvable() ); }
+    /** \overload */
+    MountPointSet disk_usage( const ResObject::constPtr & obj_r )
+    {
+      if ( ! obj_r )
+        return MountPointSet();
+      return disk_usage( obj_r->satSolvable() );
+    }
+
+  private:
+
+    MountPointSet mps;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates DiskUsageCounter::MountPoint Stream output */
+  std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_DISKUSAGE_COUNTER_H
diff --git a/zypp/DownloadMode.cc b/zypp/DownloadMode.cc
new file mode 100644 (file)
index 0000000..f27bc00
--- /dev/null
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DownloadMode.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/DownloadMode.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  bool deserialize( const std::string & str_r, DownloadMode & result_r )
+  {
+#define OUTS(VAL) if ( str::compareCI( str_r, #VAL ) == 0 ) { result_r = VAL; return true; }
+    OUTS( DownloadOnly );
+    OUTS( DownloadInAdvance );
+    OUTS( DownloadInHeaps );
+    OUTS( DownloadAsNeeded );
+#undef OUTS
+    return false;
+  }
+
+  std::ostream & operator<<( std::ostream & str, DownloadMode obj )
+  {
+    switch ( obj )
+    {
+#define OUTS(VAL) case VAL: return str << #VAL; break
+      OUTS( DownloadDefault );
+      OUTS( DownloadOnly );
+      OUTS( DownloadInAdvance );
+      OUTS( DownloadInHeaps );
+      OUTS( DownloadAsNeeded );
+#undef OUTS
+    }
+    return str << "DownloadMode(" << int(obj) << ")";
+  }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/DownloadMode.h b/zypp/DownloadMode.h
new file mode 100644 (file)
index 0000000..15a9bc9
--- /dev/null
@@ -0,0 +1,61 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/DownloadMode.h
+ *
+*/
+#ifndef ZYPP_DOWNLOADMODE_H
+#define ZYPP_DOWNLOADMODE_H
+
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Supported commit download policies. */
+  enum DownloadMode
+  {
+    DownloadDefault, //!< libzypp will decide what to do.
+    DownloadOnly,      //!< Just download all packages to the local cache.
+                       //!< Do not install. Implies a dry-run.
+    DownloadInAdvance, //!< First download all packages to the local cache.
+                       //!< Then start to install.
+    DownloadInHeaps,   //!< Similar to DownloadInAdvance, but try to split
+                       //!< the transaction into heaps, where at the end of
+                       //!< each heap a consistent system state is reached.
+    DownloadAsNeeded   //!< Alternating download and install. Packages are
+                       //!< cached just to avid CD/DVD hopping. This is the
+                       //!< traditional behaviour.
+  };
+
+  /** \relates DownloadMode Parse from string.
+   * On success the \ref DownloadMode is returned via \a result_r,
+   * and the function returns \c true. Otherwise it returns \c false
+   * and \a result_r remains unchanged.
+   */
+  bool deserialize( const std::string & str_r, DownloadMode & result_r );
+
+  /** \relates DownloadMode Parse from string.
+   * Similar as \ref deserialize, but silently return \ref DownloadDefault
+   * in case of a parse error.
+   */
+  inline DownloadMode deserializeDownloadMode( const std::string & str_r )
+  {
+    DownloadMode ret( DownloadDefault );
+    deserialize( str_r, ret );
+    return ret;
+  }
+
+  /** \relates DownloadMode Stream output. */
+  std::ostream & operator<<( std::ostream & str, DownloadMode obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_DOWNLOADMODE_H
diff --git a/zypp/Edition.cc b/zypp/Edition.cc
new file mode 100644 (file)
index 0000000..e3c3bb3
--- /dev/null
@@ -0,0 +1,138 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Edition.cc
+ *
+*/
+extern "C"
+{
+#include <satsolver/evr.h>
+}
+#include "zypp/base/String.h"
+
+#include "zypp/Edition.h"
+#include "zypp/sat/detail/PoolImpl.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    inline std::string makeevrstr( const std::string & version_r,
+                                   const std::string & release_r,
+                                   Edition::epoch_t epoch_r )
+    {
+      std::string ret( version_r );
+      if ( ! release_r.empty() )
+      {
+        ret += "-";
+        ret += release_r;
+      }
+      return ( epoch_r ? str::numstring( epoch_r ) + ":" + ret
+                       : ret );
+    }
+
+    inline std::string makeevrstr( const char * version_r,
+                                   const char * release_r,
+                                   Edition::epoch_t epoch_r )
+    { return makeevrstr( std::string(version_r?version_r:""),
+                         std::string(release_r?release_r:""),
+                         epoch_r );
+    }
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  const Edition Edition::noedition;
+
+  ///////////////////////////////////////////////////////////////////
+
+  Edition::Edition( const std::string & version_r,
+                    const std::string & release_r,
+                    epoch_t epoch_r )
+  : _str( makeevrstr( version_r, release_r, epoch_r ) )
+  {}
+
+  Edition::Edition( const char * version_r,
+                    const char * release_r,
+                    epoch_t epoch_r )
+  : _str( makeevrstr( version_r, release_r, epoch_r ) )
+  {}
+
+  Edition::Edition( const std::string & version_r,
+                    const std::string & release_r,
+                    const std::string & epoch_r )
+  : _str( makeevrstr( version_r, release_r, str::strtonum<epoch_t>( epoch_r ) ) )
+  {}
+
+  Edition::Edition( const char * version_r,
+                    const char * release_r,
+                    const char * epoch_r )
+  : _str( makeevrstr( version_r, release_r, str::strtonum<epoch_t>( epoch_r ) ) )
+  {}
+
+  Edition::epoch_t Edition::epoch() const
+  {
+    const char * str( c_str() );
+    const char * sep = str;
+    // skip edition
+    for ( ; *sep >= '0' && *sep <= '9'; ++sep )
+      ; // NOOP
+    if ( *sep == ':' )
+      return str::strtonum<epoch_t>( std::string( str, sep-str ) );
+    return 0;
+  }
+
+  std::string Edition::version() const
+  {
+    const char * str( c_str() );
+    const char * sep = str;
+    // skip edition
+    for ( ; *sep >= '0' && *sep <= '9'; ++sep )
+      ; // NOOP
+    if ( *sep == ':' )
+      str = sep+1;
+    // strip release
+    sep = ::strrchr( str, '-' );
+    if ( sep )
+      return std::string( str, sep-str );
+    return str;
+  }
+
+  std::string Edition::release() const
+  {
+    const char * str( c_str() );
+    const char * sep = ::strrchr( str, '-' );
+    // get release
+    if ( sep )
+      return sep+1;
+    return std::string();
+  }
+
+  int Edition::_doCompare( const char * lhs,  const char * rhs )
+  {
+    if ( lhs == rhs ) return 0;
+    if ( lhs && rhs ) return ::evrcmp_str( myPool().getPool(), lhs, rhs, EVRCMP_COMPARE );
+    return( lhs ? 1 : -1 );
+  }
+
+  int Edition::_doMatch( const char * lhs,  const char * rhs )
+  {
+    if ( lhs == rhs ) return 0;
+    if ( lhs && rhs ) return ::evrcmp_str( myPool().getPool(), lhs, rhs, EVRCMP_MATCH );
+    return( lhs ? 1 : -1 );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+
diff --git a/zypp/Edition.h b/zypp/Edition.h
new file mode 100644 (file)
index 0000000..dec2bc7
--- /dev/null
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Edition.h
+ *
+*/
+#ifndef ZYPP_EDITION_H
+#define ZYPP_EDITION_H
+
+#include <iosfwd>
+#include <string>
+#include <functional>
+
+#include "zypp/IdStringType.h"
+#include "zypp/RelCompare.h"
+#include "zypp/Range.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Edition
+  //
+  /** Edition represents <code>[epoch:]version[-release]</code>
+   *
+   * \li \c epoch   (optional) number, Edition::noepoch if not supplied
+   * \li \c version (required) string, may not contain '-'
+   * \li \c release (optional) string, may not contain '-'
+   *
+   * Comparison is actually \reg g_BackendSpecific.
+   *
+   * \li \b RPM: Edition are ordered according to \c epoch, then \c version,
+   * then \c release. Version and release strings are compared by splitting
+   * them into segments of alpha or digit sequences. Segments are compared
+   * according to their type. On mixed types a string compares less than a
+   * number.
+   * \code
+   *   compare( 1.a, 1.0 ) == -1 (<)
+   *   compare( 1.0, 1.a ) ==  1 (>)
+   *   compare( 1.0, 1_0 ) ==  0 (==)
+   * \endcode
+   *
+   * \attention operator< defines equivalence classes of version strings, as non
+   * alphanumeric chars are ignored. That' why \c 1.0 and \c 1_0 compare equal
+   * in the example.<BR>
+   *
+   * \attention Edition::match compares two editions, treating empty
+   * version or release strings as wildcard. Thus match is not transitive,
+   * and you don't want to use it to order keys in a a std::container.
+   *
+   * \ingroup g_BackendSpecific
+  */
+  class Edition : public IdStringType<Edition>
+  {
+    public:
+      /** Type of an epoch. */
+      typedef unsigned epoch_t;
+
+      /** Value representing \c noepoch. */
+      static const epoch_t noepoch = 0;
+
+    /** Value representing \c noedition (<tt>""</tt>)
+     * This is in fact a valid Edition. It's what the default ctor
+     * creates or will be parsed from an empty string.
+     */
+      static const Edition noedition;
+
+    public:
+      /** Default ctor: \ref noedition. */
+      Edition() {}
+
+      /** Ctor taking edition as string. */
+      explicit Edition( IdString::IdType id_r )     : _str( id_r ) {}
+      explicit Edition( const IdString & idstr_r )  : _str( idstr_r ) {}
+      explicit Edition( const std::string & str_r ) : _str( str_r ) {}
+      explicit Edition( const char * cstr_r )       : _str( cstr_r ) {}
+
+      /** Ctor taking \a version_r, \a release_r and optional \a epoch_r */
+      Edition( const std::string & version_r,
+               const std::string & release_r,
+               epoch_t epoch_r = noepoch );
+      /** \overload */
+      Edition( const char * version_r,
+               const char * release_r,
+               epoch_t epoch_r = noepoch );
+
+      /** Ctor taking \a version_r, \a release_r and optional \a epoch_r as string. */
+      Edition( const std::string & version_r,
+               const std::string & release_r,
+               const std::string & epoch_r );
+      /** \overload */
+      Edition( const char * version_r,
+               const char * release_r,
+               const char * epoch_r );
+
+    public:
+      /** Epoch */
+      epoch_t epoch() const;
+
+      /** Version */
+      std::string version() const;
+
+      /** Release */
+      std::string release() const;
+
+    public:
+      /** \ref compare functor.
+       * \see \ref RelCompare.
+       */
+      typedef zypp::Compare<Edition> Compare;
+
+      /** \ref Edition \ref Range based on \ref Compare.
+       * \see \ref RelCompare.
+       */
+      typedef Range<Edition> CompareRange;
+
+    public:
+      /** \name Match two Editions
+       *  Match two Editions returning <tt>-1,0,1</tt>, treating empty
+       *  version/release strings as \c ANY.
+       */
+      //@{
+      static int match( const Edition & lhs,     const Edition & rhs )     { return match( lhs.idStr(), rhs.idStr() ); }
+      static int match( const Edition & lhs,     const IdString & rhs )    { return match( lhs.idStr(), rhs ); }
+      static int match( const Edition & lhs,     const std::string & rhs ) { return _doMatch( lhs.c_str(), rhs.c_str() ); }
+      static int match( const Edition & lhs,     const char * rhs )        { return _doMatch( lhs.c_str(), rhs );}
+
+      static int match( const IdString & lhs,    const Edition & rhs )     { return match( lhs, rhs.idStr() ); }
+      static int match( const IdString & lhs,    const IdString & rhs )    { return lhs.compareEQ( rhs ) ? 0 :
+                                                                                    _doMatch( lhs.c_str(), rhs.c_str() ); }
+      static int match( const IdString & lhs,    const std::string & rhs ) { return _doMatch( lhs.c_str(), rhs.c_str() ); }
+      static int match( const IdString & lhs,    const char * rhs )        { return _doMatch( lhs.c_str(), rhs ); }
+
+      static int match( const std::string & lhs, const Edition & rhs )     { return _doMatch( lhs.c_str(), rhs.c_str() );}
+      static int match( const std::string & lhs, const IdString & rhs )    { return _doMatch( lhs.c_str(), rhs.c_str() ); }
+      static int match( const std::string & lhs, const std::string & rhs ) { return _doMatch( lhs.c_str(), rhs.c_str() ); }
+      static int match( const std::string & lhs, const char * rhs )        { return _doMatch( lhs.c_str(), rhs ); }
+
+      static int match( const char * lhs,        const Edition & rhs )     { return _doMatch( lhs, rhs.c_str() );}
+      static int match( const char * lhs,        const IdString & rhs )    { return _doMatch( lhs, rhs.c_str() ); }
+      static int match( const char * lhs,        const std::string & rhs ) { return _doMatch( lhs, rhs.c_str() ); }
+      static int match( const char * lhs,        const char * rhs )        { return _doMatch( lhs, rhs ); }
+
+      int match( const Edition & rhs )     const { return match( idStr(), rhs.idStr() ); }
+      int match( const IdString & rhs )    const { return match( idStr(), rhs ); }
+      int match( const std::string & rhs ) const { return _doMatch( c_str(), rhs.c_str() ); }
+      int match( const char * rhs )        const { return _doMatch( c_str(), rhs ); }
+      //@}
+
+      /** \ref match functor.
+       * \see \ref RelCompare.
+       */
+      struct Match: public std::binary_function<Edition,Edition,int>
+      {
+        int operator()( const Edition & lhs, const Edition & rhs ) const
+        { return Edition::match( lhs, rhs ); }
+      };
+
+      /** \ref Edition \ref Range based on \ref Match.
+       * \see \ref RelCompare.
+       */
+      typedef Range<Edition, Match> MatchRange;
+
+    private:
+      static int _doCompare( const char * lhs,  const char * rhs );
+      static int _doMatch( const char * lhs,  const char * rhs );
+
+    private:
+      friend class IdStringType<Edition>;
+      IdString _str;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_EDITION_H
diff --git a/zypp/ExternalProgram.cc b/zypp/ExternalProgram.cc
new file mode 100644 (file)
index 0000000..c319192
--- /dev/null
@@ -0,0 +1,638 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ExternalProgram.cc
+*/
+
+#define _GNU_SOURCE 1 // for ::getline
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <pty.h> // openpty
+#include <stdlib.h> // setenv
+
+#include <cstring> // strsignal
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/ExternalProgram.h"
+
+using namespace std;
+
+namespace zypp {
+
+    ExternalProgram::ExternalProgram()
+      : use_pty (false)
+      , pid( -1 )
+    {
+    }
+
+
+    ExternalProgram::ExternalProgram( std::string commandline,
+                                      Stderr_Disposition stderr_disp,
+                                      bool use_pty,
+                                      int stderr_fd,
+                                      bool default_locale,
+                                      const Pathname & root )
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+      const char *argv[4];
+      argv[0] = "/bin/sh";
+      argv[1] = "-c";
+      argv[2] = commandline.c_str();
+      argv[3] = 0;
+
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      Environment environment;
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+
+
+    ExternalProgram::ExternalProgram (const Arguments &argv,
+                                      Stderr_Disposition stderr_disp,
+                                      bool use_pty, int stderr_fd,
+                                      bool default_locale,
+                                      const Pathname& root)
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+        const char * argvp[argv.size() + 1];
+        unsigned c = 0;
+        for_( i, argv.begin(), argv.end() )
+        {
+            argvp[c] = i->c_str();
+            ++c;
+        }
+        argvp[c] = 0;
+
+        Environment environment;
+        const char* rootdir = NULL;
+        if(!root.empty() && root != "/")
+        {
+            rootdir = root.asString().c_str();
+        }
+        start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+
+
+    ExternalProgram::ExternalProgram (const Arguments &argv,
+                                      const Environment & environment,
+                                      Stderr_Disposition stderr_disp,
+                                      bool use_pty, int stderr_fd,
+                                      bool default_locale,
+                                      const Pathname& root)
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+        const char * argvp[argv.size() + 1];
+        unsigned c = 0;
+        for_( i, argv.begin(), argv.end() )
+        {
+            argvp[c] = i->c_str();
+            ++c;
+        }
+        argvp[c] = 0;
+
+        const char* rootdir = NULL;
+        if(!root.empty() && root != "/")
+        {
+            rootdir = root.asString().c_str();
+        }
+        start_program (argvp, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+
+    }
+
+
+
+
+    ExternalProgram::ExternalProgram( const char *const *argv,
+                                      Stderr_Disposition stderr_disp,
+                                      bool use_pty,
+                                      int stderr_fd,
+                                      bool default_locale,
+                                      const Pathname & root )
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      Environment environment;
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+
+
+    ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
+                                 Stderr_Disposition stderr_disp, bool use_pty,
+                                 int stderr_fd, bool default_locale,
+                                 const Pathname& root)
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+
+
+    ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
+                                 bool use_pty)
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+      int i = 0;
+      while (argv_1[i++])
+       ;
+      const char *argv[i + 1];
+      argv[0] = binpath;
+      memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
+      Environment environment;
+      start_program (argv, environment);
+    }
+
+
+    ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
+                                 bool use_pty)
+      : use_pty (use_pty)
+      , pid( -1 )
+    {
+      int i = 0;
+      while (argv_1[i++])
+       ;
+      const char *argv[i + 1];
+      argv[0] = binpath;
+      memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
+      start_program (argv, environment);
+    }
+
+
+    ExternalProgram::~ExternalProgram()
+    {
+    }
+
+
+    void
+    ExternalProgram::start_program (const char *const *argv, const Environment & environment,
+                               Stderr_Disposition stderr_disp,
+                               int stderr_fd, bool default_locale, const char* root)
+    {
+      pid = -1;
+      _exitStatus = 0;
+      int to_external[2], from_external[2];  // fds for pair of pipes
+      int master_tty,  slave_tty;         // fds for pair of ttys
+
+      const char * redirectStdin = 0;
+      if ( argv[0] && *argv[0] == '<' )
+      {
+        redirectStdin = argv[0]+1;
+        if ( *redirectStdin == '\0' )
+          redirectStdin = "/dev/null";
+        ++argv;
+      }
+
+      // do not remove the single quotes around every argument, copy&paste of
+      // command to shell will not work otherwise!
+      {
+        stringstream cmdstr;
+        for (int i = 0; argv[i]; i++)
+        {
+          if (i>0) cmdstr << ' ';
+          cmdstr << '\'';
+          cmdstr << argv[i];
+          cmdstr << '\'';
+        }
+        if ( redirectStdin )
+          cmdstr << " < '" << redirectStdin << "'";
+        _command = cmdstr.str();
+      }
+      DBG << "Executing " << _command << endl;
+
+
+      if (use_pty)
+      {
+       // Create pair of ttys
+        DBG << "Using ttys for communication with " << argv[0] << endl;
+       if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
+       {
+          _execError = str::form( _("Can't open pty (%s)."), strerror(errno) );
+          _exitStatus = 126;
+          ERR << _execError << endl;
+          return;
+       }
+      }
+      else
+      {
+       // Create pair of pipes
+       if (pipe (to_external) != 0 || pipe (from_external) != 0)
+       {
+          _execError = str::form( _("Can't open pipe (%s)."), strerror(errno) );
+          _exitStatus = 126;
+          ERR << _execError << endl;
+          return;
+       }
+      }
+
+      // Create module process
+      if ((pid = fork()) == 0)
+      {
+        //////////////////////////////////////////////////////////////////////
+        // Don't write to the logfile after fork!
+        //////////////////////////////////////////////////////////////////////
+       if (use_pty)
+       {
+           setsid();
+           if(slave_tty != 1)
+               dup2 (slave_tty, 1);      // set new stdout
+           renumber_fd (slave_tty, 0);   // set new stdin
+           ::close(master_tty);          // Belongs to father process
+
+           // We currently have no controlling terminal (due to setsid).
+           // The first open call will also set the new ctty (due to historical
+           // unix guru knowledge ;-) )
+
+           char name[512];
+           ttyname_r(slave_tty, name, sizeof(name));
+           ::close(open(name, O_RDONLY));
+       }
+       else
+       {
+           renumber_fd (to_external[0], 0); // set new stdin
+           ::close(from_external[0]);    // Belongs to father process
+
+           renumber_fd (from_external[1], 1); // set new stdout
+           ::close(to_external  [1]);    // Belongs to father process
+       }
+
+        if ( redirectStdin )
+        {
+          ::close( 0 );
+          int inp_fd = open( redirectStdin, O_RDONLY );
+          dup2( inp_fd, 0 );
+        }
+
+       // Handle stderr
+       if (stderr_disp == Discard_Stderr)
+       {
+           int null_fd = open("/dev/null", O_WRONLY);
+           dup2(null_fd, 2);
+           ::close(null_fd);
+       }
+       else if (stderr_disp == Stderr_To_Stdout)
+       {
+           dup2(1, 2);
+       }
+       else if (stderr_disp == Stderr_To_FileDesc)
+       {
+           // Note: We don't have to close anything regarding stderr_fd.
+           // Our caller is responsible for that.
+           dup2 (stderr_fd, 2);
+       }
+
+       for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
+         setenv( it->first.c_str(), it->second.c_str(), 1 );
+       }
+
+       if(default_locale)
+               setenv("LC_ALL","C",1);
+
+       if(root)
+       {
+           if(chroot(root) == -1)
+           {
+                _execError = str::form( _("Can't chroot to '%s' (%s)."), root, strerror(errno) );
+                std::cerr << _execError << endl;// After fork log on stderr too
+               _exit (128);                    // No sense in returning! I am forked away!!
+           }
+           if(chdir("/") == -1)
+           {
+                _execError = str::form( _("Can't chdir to '/' inside chroot (%s)."), strerror(errno) );
+                std::cerr << _execError << endl;// After fork log on stderr too
+               _exit (128);                    // No sense in returning! I am forked away!!
+           }
+       }
+
+       // close all filedesctiptors above stderr
+       for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
+         ::close( i );
+       }
+
+       execvp(argv[0], const_cast<char *const *>(argv));
+        // don't want to get here
+        _execError = str::form( _("Can't exec '%s' (%s)."), argv[0], strerror(errno) );
+        std::cerr << _execError << endl;// After fork log on stderr too
+        _exit (129);                   // No sense in returning! I am forked away!!
+        //////////////////////////////////////////////////////////////////////
+      }
+
+      else if (pid == -1)       // Fork failed, close everything.
+      {
+        _execError = str::form( _("Can't fork (%s)."), strerror(errno) );
+        _exitStatus = 127;
+        ERR << _execError << endl;
+
+       if (use_pty) {
+           ::close(master_tty);
+           ::close(slave_tty);
+       }
+       else {
+           ::close(to_external[0]);
+           ::close(to_external[1]);
+           ::close(from_external[0]);
+           ::close(from_external[1]);
+       }
+      }
+
+      else {
+       if (use_pty)
+       {
+           ::close(slave_tty);        // belongs to child process
+           inputfile  = fdopen(master_tty, "r");
+           outputfile = fdopen(master_tty, "w");
+       }
+       else
+       {
+           ::close(to_external[0]);   // belongs to child process
+           ::close(from_external[1]); // belongs to child process
+           inputfile = fdopen(from_external[0], "r");
+           outputfile = fdopen(to_external[1], "w");
+       }
+
+       DBG << "pid " << pid << " launched" << endl;
+
+       if (!inputfile || !outputfile)
+       {
+           ERR << "Cannot create streams to external program " << argv[0] << endl;
+           close();
+       }
+      }
+    }
+
+
+    int
+    ExternalProgram::close()
+    {
+      if (pid > 0)
+      {
+       if ( inputFile() )
+       {
+         // Discard any output instead of closing the pipe,
+         // but watch out for the command exiting while some
+         // subprocess keeps the filedescriptor open.
+         setBlocking( false );
+         FILE * inputfile = inputFile();
+         int    inputfileFd = ::fileno( inputfile );
+         long   delay = 0;
+         do
+         {
+           /* Watch inputFile to see when it has input. */
+           fd_set rfds;
+           FD_ZERO( &rfds );
+           FD_SET( inputfileFd, &rfds );
+
+           /* Wait up to 1 seconds. */
+           struct timeval tv;
+           tv.tv_sec  = (delay < 0 ? 1 : 0);
+           tv.tv_usec = (delay < 0 ? 0 : delay*100000);
+           if ( delay >= 0 && ++delay > 9 )
+             delay = -1;
+           int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
+
+           if ( retval == -1 )
+           {
+             ERR << "select error: " << strerror(errno) << endl;
+             if ( errno != EINTR )
+               break;
+           }
+           else if ( retval )
+           {
+             // Data is available now.
+             static size_t linebuffer_size = 0;      // static because getline allocs
+             static char * linebuffer = 0;           // and reallocs if buffer is too small
+             getline( &linebuffer, &linebuffer_size, inputfile );
+             // ::feof check is important as select returns
+             // positive if the file was closed.
+             if ( ::feof( inputfile ) )
+               break;
+             clearerr( inputfile );
+           }
+           else
+           {
+             // No data within time.
+             if ( ! running() )
+               break;
+           }
+         } while ( true );
+       }
+
+       // Wait for child to exit
+       int ret;
+       int status = 0;
+       do
+       {
+         ret = waitpid(pid, &status, 0);
+       }
+       while (ret == -1 && errno == EINTR);
+
+       if (ret != -1)
+       {
+        _exitStatus = checkStatus( status );
+       }
+       pid = -1;
+      }
+
+      return _exitStatus;
+    }
+
+
+    int ExternalProgram::checkStatus( int status )
+    {
+      if (WIFEXITED (status))
+      {
+       status = WEXITSTATUS (status);
+       if(status)
+       {
+           DBG << "Pid " << pid << " exited with status " << status << endl;
+            _execError = str::form( _("Command exited with status %d."), status );
+       }
+       else
+       {
+           // if 'launch' is logged, completion should be logged,
+           // even if successfull.
+           DBG << "Pid " << pid << " successfully completed" << endl;
+            _execError.clear(); // empty if running or successfully completed
+       }
+      }
+      else if (WIFSIGNALED (status))
+      {
+       status = WTERMSIG (status);
+       WAR << "Pid " << pid << " was killed by signal " << status
+               << " (" << strsignal(status);
+       if (WCOREDUMP (status))
+       {
+           WAR << ", core dumped";
+       }
+       WAR << ")" << endl;
+        _execError = str::form( _("Command was killed by signal %d (%s)."), status, strsignal(status) );
+       status+=128;
+      }
+      else {
+       ERR << "Pid " << pid << " exited with unknown error" << endl;
+        _execError = _("Command exited with unknown error.");
+      }
+
+      return status;
+    }
+
+    bool
+    ExternalProgram::kill()
+    {
+      if (pid > 0)
+      {
+       ::kill(pid, SIGKILL);
+       close();
+      }
+      return true;
+    }
+
+
+    bool
+    ExternalProgram::running()
+    {
+      if ( pid < 0 ) return false;
+
+      int status = 0;
+      int p = waitpid( pid, &status, WNOHANG );
+      switch ( p )
+        {
+        case -1:
+          ERR << "waitpid( " << pid << ") returned error '" << strerror(errno) << "'" << endl;
+          return false;
+          break;
+        case 0:
+          return true; // still running
+          break;
+        }
+
+      // Here: completed...
+      _exitStatus = checkStatus( status );
+      pid = -1;
+      return false;
+    }
+
+    // origfd will be accessible as newfd and closed (unless they were equal)
+    void ExternalProgram::renumber_fd (int origfd, int newfd)
+    {
+      // It may happen that origfd is already the one we want
+      // (Although in our circumstances, that would mean somebody has closed
+      // our stdin or stdout... weird but has appened to Cray, #49797)
+      if (origfd != newfd)
+      {
+       dup2 (origfd, newfd);
+       ::close (origfd);
+      }
+    }
+
+    std::ostream & ExternalProgram::operator>>( std::ostream & out_r )
+    {
+      setBlocking( true );
+      for ( std::string line = receiveLine(); line.length(); line = receiveLine() )
+        out_r << line;
+      return out_r;
+    }
+
+    //////////////////////////////////////////////////////////////////////
+    //
+    // class ExternalProgramWithStderr
+    //
+    //////////////////////////////////////////////////////////////////////
+
+    namespace _ExternalProgram
+    {
+      EarlyPipe::EarlyPipe()
+      {
+       _fds[R] = _fds[W] = -1;
+#ifdef HAVE_PIPE2
+       ::pipe2( _fds, O_NONBLOCK );
+#else
+        ::pipe( _fds );
+        ::fcntl(_fds[R], F_SETFD, O_NONBLOCK );
+        ::fcntl(_fds[W], F_SETFD, O_NONBLOCK );
+#endif
+       _stderr = ::fdopen( _fds[R], "r" );
+      }
+
+      EarlyPipe::~EarlyPipe()
+      {
+       closeW();
+       if ( _stderr )
+         ::fclose( _stderr );
+      }
+    }
+
+    bool ExternalProgramWithStderr::stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r )
+    {
+      if ( ! _stderr )
+       return false;
+      if ( delim_r && ! _buffer.empty() )
+      {
+       // check for delim already in buffer
+       std::string::size_type pos( _buffer.find( delim_r ) );
+       if ( pos != std::string::npos )
+       {
+         retval_r = _buffer.substr( 0, returnDelim_r ? pos+1 : pos );
+         _buffer.erase( 0, pos+1 );
+         return true;
+       }
+      }
+      ::clearerr( _stderr );
+      do {
+       int ch = fgetc( _stderr );
+       if ( ch != EOF )
+       {
+         if ( ch != delim_r || ! delim_r )
+           _buffer.push_back( ch );
+         else
+         {
+           if ( returnDelim_r )
+             _buffer.push_back( delim_r );
+           break;
+         }
+       }
+       else if ( ::feof( _stderr ) )
+       {
+         if ( _buffer.empty() )
+           return false;
+         break;
+       }
+       else if ( errno != EINTR )
+         return false;
+      } while ( true );
+      // HERE: we left after readig at least one char (\n)
+      retval_r.swap( _buffer );
+      _buffer.clear();
+      return true;
+    }
+
+
+} // namespace zypp
diff --git a/zypp/ExternalProgram.h b/zypp/ExternalProgram.h
new file mode 100644 (file)
index 0000000..dd5b089
--- /dev/null
@@ -0,0 +1,288 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ExternalProgram.h
+*/
+
+
+#ifndef ZYPP_EXTERNALPROGRAM_H
+#define ZYPP_EXTERNALPROGRAM_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "zypp/base/ExternalDataSource.h"
+#include "zypp/Pathname.h"
+
+namespace zypp {
+
+    /**
+     * @short Execute a program and give access to its io
+     * An object of this class encapsulates the execution of
+     * an external program. It starts the program using fork
+     * and some exec.. call, gives you access to the program's
+     * stdio and closes the program after use.
+     *
+     * \code
+     *
+     * const char* argv[] =
+     * {
+     *     "/usr/bin/foo,
+     *     "--option1",
+     *     "--option2",
+     *     NULL
+     * };
+     *
+     * ExternalProgram prog( argv,
+     *                        ExternalProgram::Discard_Stderr,
+     *                        false, -1, true);
+     * string line;
+     * for(line = prog.receiveLine();
+     *     ! line.empty();
+     *     line = prog.receiveLine() )
+     * {
+     *     stream << line;
+     * }
+     * prog.close();
+     *
+     * \endcode
+     */
+    class ExternalProgram : public zypp::externalprogram::ExternalDataSource
+    {
+
+    public:
+
+      typedef std::vector<std::string> Arguments;
+
+      /**
+       * Define symbols for different policies on the handling
+       * of stderr
+       */
+      enum Stderr_Disposition {
+       Normal_Stderr,
+       Discard_Stderr,
+       Stderr_To_Stdout,
+       Stderr_To_FileDesc
+      };
+
+
+      /**
+       * For passing additional environment variables to set
+       */
+      typedef std::map<std::string,std::string> Environment;
+
+      /**
+       * Start the external program by using the shell <tt>/bin/sh<tt>
+       * with the option <tt>-c</tt>. You can use io direction symbols < and >.
+       * @param commandline a shell commandline that is appended to
+       * <tt>/bin/sh -c</tt>.
+       * @param default_locale whether to set LC_ALL=C before starting
+       * @param root directory to chroot into, / or empty to not chroot
+       */
+      ExternalProgram (std::string commandline,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      /**
+       * Start an external program by giving the arguments as an arry of char *pointers.
+       * If environment is provided, varaiables will be added to the childs environment,
+       * overwriting existing ones.
+       *
+       * Stdin redirection: If the \b 1st argument starts with a \b '<', the remaining
+       * part is treated as file opened for reading on standard input (or \c /dev/null
+       * if empty).
+       * \code
+       *   // cat file /tmp/x
+       *   const char* argv[] = { "</tmp/x", "cat", NULL };
+       *   ExternalProgram prog( argv );
+       * \endcode
+       */
+
+      ExternalProgram();
+
+      ExternalProgram (const Arguments &argv,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      ExternalProgram (const Arguments &argv, const Environment & environment,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      ExternalProgram (const char *const *argv,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      ExternalProgram (const char *const *argv, const Environment & environment,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+
+      ExternalProgram (const char *binpath, const char *const *argv_1,
+                    bool use_pty = false);
+
+
+      ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
+                    bool use_pty = false);
+
+
+      ~ExternalProgram();
+
+      /** Wait for the progamm to complete. */
+      int close();
+
+      /**
+       * Kill the program
+       */
+      bool kill();
+
+      /**
+       * Return whether program is running
+       */
+      bool running();
+
+      /**
+       * return pid
+       * */
+      pid_t getpid() { return pid; }
+
+      /** The command we're executing. */
+      const std::string & command() const
+      { return _command; }
+
+      /** Some detail telling why the execution failed, if it failed.
+       * Empty if the command is still running or successfully completed.
+       *
+       * \li <tt>Can't open pty (%s).</tt>
+       * \li <tt>Can't open pipe (%s).</tt>
+       * \li <tt>Can't fork (%s).</tt>
+       * \li <tt>Command exited with status %d.</tt>
+       * \li <tt>Command was killed by signal %d (%s).</tt>
+      */
+      const std::string & execError() const
+      { return _execError; }
+
+      /**
+       * origfd will be accessible as newfd and closed (unless they were equal)
+       */
+      static void renumber_fd (int origfd, int newfd);
+
+    public:
+
+      /**
+       * Redirect all command output to an \c ostream.
+       * Returns when the command has completed.
+       * \code
+       *   std::ostringstream s;
+       *   ExternalProgram("pwd") >> s;
+       *   SEC << s.str() << endl;
+       * \endcode
+       * \code
+       *   std::ostringstream s;
+       *   ExternalProgram prog("ls -l wrzl");
+       *   prog >> s;
+       *   if ( prog.close() == 0 )
+       *     MIL << s.str() << endl;
+       *   else
+       *     ERR << prog.execError() << endl;
+       * \endcode
+       */
+      std::ostream & operator>>( std::ostream & out_r );
+
+    protected:
+      int checkStatus( int );
+
+    private:
+
+      /**
+       * Set to true, if a pair of ttys is used for communication
+       * instead of a pair of pipes.
+       */
+      bool use_pty;
+
+      pid_t pid;
+      int _exitStatus;
+      /** Store the command we're executing. */
+      std::string _command;
+      /** Remember execution errors like failed fork/exec. */
+      std::string _execError;
+
+      void start_program (const char *const *argv, const Environment & environment,
+                       Stderr_Disposition stderr_disp = Normal_Stderr,
+                       int stderr_fd = -1, bool default_locale = false,
+                       const char* root = NULL);
+
+    };
+
+
+  namespace _ExternalProgram
+  {
+    /** Helper providing pipe FDs for \ref ExternalProgramWithStderr.
+     * Moved to a basse class because the pipe needs to be initialized
+     * before the \ref ExternalProgram base class is initialized.
+     * \see \ref ExternalProgramWithStderr
+     */
+    struct EarlyPipe
+    {
+      enum { R=0, W=1 };
+      EarlyPipe();
+      ~EarlyPipe();
+      void closeW()            { if ( _fds[W] != -1 ) { ::close( _fds[W] ); _fds[W] = -1; } }
+      FILE * stderr()          { return _stderr; }
+      protected:
+       FILE * _stderr;
+       int _fds[2];
+    };
+  }
+
+  /** ExternalProgram extended to offer reading programs stderr.
+   * \see \ref ExternalProgram
+   */
+  class ExternalProgramWithStderr : private _ExternalProgram::EarlyPipe, public ExternalProgram
+  {
+    public:
+      ExternalProgramWithStderr( const Arguments & argv_r )
+       : ExternalProgram( argv_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
+      { _initStdErr(); }
+
+      ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r )
+        : ExternalProgram( argv_r, environment_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
+      { _initStdErr(); }
+
+    public:
+      /** Return \c FILE* to read programms stderr (O_NONBLOCK set). */
+      _ExternalProgram::EarlyPipe::stderr;
+
+      /** Read data up to \c delim_r from stderr (nonblocking).
+       * \note If \c delim_r is '\0', we read as much data as possible.
+       * \return \c false if data are not yet available (\c retval_r remains untouched then).
+       */
+      bool stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r = false );
+
+      /** Read next complete line from stderr (nonblocking).
+       * \return \c false if data are not yet available (\c retval_r remains untouched then).
+       */
+      bool stderrGetline( std::string & retval_r, bool returnDelim_r = false  )
+      { return stderrGetUpTo( retval_r, '\n', returnDelim_r ); }
+
+    private:
+      /** Close write end of the pipe (childs end). */
+      void _initStdErr()
+      { closeW(); }
+
+    private:
+      std::string _buffer;
+  };
+
+} // namespace zypp
+
+#endif // ZYPP_EXTERNALPROGRAM_H
diff --git a/zypp/Fetcher.cc b/zypp/Fetcher.cc
new file mode 100644 (file)
index 0000000..beebbcc
--- /dev/null
@@ -0,0 +1,920 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Fetcher.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <map>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/base/String.h"
+#include "zypp/Fetcher.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/CheckSum.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/parser/susetags/ContentFileReader.h"
+#include "zypp/parser/susetags/RepoIndex.h"
+
+using namespace std;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp:fetcher"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /**
+   * class that represents indexes which add metadata
+   * to fetcher jobs and therefore need to be retrieved
+   * in advance.
+   */
+  struct FetcherIndex
+  {
+    FetcherIndex( const OnMediaLocation &loc )
+      : location(loc)
+    {}
+    /** Index localtion. */
+    OnMediaLocation             location;
+    /** Whether we read this index. */
+    DefaultIntegral<bool,false> read;
+  };
+
+  typedef shared_ptr<FetcherIndex> FetcherIndex_Ptr;
+
+  /** std::set ordering (less semantic) */
+  struct SameFetcherIndex
+  {
+    bool operator()( const FetcherIndex_Ptr & lhs, const FetcherIndex_Ptr & rhs )
+    {
+      if ( lhs == rhs )
+        return false; // incl. NULL == NULL
+      if ( ! lhs )
+        return true;  // NULL < nonNULL
+      if ( ! rhs )
+        return false; // nonNULL > NULL
+      // both nonNULL ==> compare medianr and path
+      if ( lhs->location.medianr() == rhs->location.medianr() )
+        return lhs->location.filename() < rhs->location.filename();
+      //else
+        return lhs->location.medianr() < rhs->location.medianr();
+    }
+  };
+
+  /**
+   * Class to encapsulate the \ref OnMediaLocation object
+   * and the \ref FileChecker together
+   */
+  struct FetcherJob
+  {
+    enum Flag
+    {
+        None      = 0x0000,
+        Directory = 0x0001,
+        Recursive = 0x0002,
+        RecursiveDirectory = Directory | Recursive,
+        // check checksums even if there is no such
+        // checksum (warns of no checksum)
+        AlwaysVerifyChecksum = 0x0004,
+    };
+    ZYPP_DECLARE_FLAGS(Flags, Flag);
+
+
+    FetcherJob( const OnMediaLocation &loc, const Pathname dfile = Pathname())
+      : location(loc)
+      , deltafile(dfile)
+      , flags(None)
+    {
+      //MIL << location << endl;
+    }
+
+    ~FetcherJob()
+    {
+      //MIL << location << " | * " << checkers.size() << endl;
+    }
+
+    OnMediaLocation location;
+    Pathname deltafile;
+    //CompositeFileChecker checkers;
+    list<FileChecker> checkers;
+    Flags flags;
+  };
+
+  ZYPP_DECLARE_OPERATORS_FOR_FLAGS(FetcherJob::Flags);
+  typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
+
+  std::ostream & operator<<( std::ostream & str, const FetcherJob_Ptr & obj )
+  {
+    return str << obj->location;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Fetcher::Impl
+  //
+  /** Fetcher implementation. */
+  class Fetcher::Impl
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj );
+
+  public:
+    Impl();
+
+    ~Impl() {}
+
+    void setOptions( Fetcher::Options options );
+    Fetcher::Options options() const;
+
+    void addIndex( const OnMediaLocation &resource );
+
+    void enqueueDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker = FileChecker() );
+    void enqueueDigestedDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker = FileChecker() );
+
+    void enqueue( const OnMediaLocation &resource, const FileChecker &checker = FileChecker()  );
+    void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker = FileChecker(), const Pathname &deltafile = Pathname() );
+    void addCachePath( const Pathname &cache_dir );
+    void reset();
+    void start( const Pathname &dest_dir,
+                MediaSetAccess &media,
+                const ProgressData::ReceiverFnc & progress_receiver );
+
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+  private:
+      /**
+       * download the indexes and reads them
+       */
+      void downloadAndReadIndexList( MediaSetAccess &media, const Pathname &dest_dir);
+
+      /**
+       * download the indexes and reads them
+       */
+      void downloadIndex( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir);
+
+      /**
+       * reads a downloaded index file and updates internal
+       * attributes table
+       *
+       * The index lists files relative to a directory, which is
+       * normally the same as the index file is located.
+       */
+      void readIndex( const Pathname &index, const Pathname &basedir );
+
+      /** specific version of \ref readIndex for SHA1SUMS file */
+      void readSha1sumsIndex( const Pathname &index, const Pathname &basedir );
+
+      /** specific version of \ref readIndex for SHA1SUMS file */
+      void readContentFileIndex( const Pathname &index, const Pathname &basedir );
+
+      /** reads the content of a directory but keeps a cache **/
+      void getDirectoryContent( MediaSetAccess &media, const OnMediaLocation &resource, filesystem::DirContent &content );
+
+      /**
+       * tries to provide the file represented by job into dest_dir by
+       * looking at the cache. If success, returns true, and the desired
+       * file should be available on dest_dir
+       */
+      bool provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir );
+      /**
+       * Validates the job against is checkers, by using the file instance
+       * on dest_dir
+       * \throws Exception
+       */
+      void validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers );
+
+      /**
+       * scan the directory and adds the individual jobs
+       */
+       void addDirJobs( MediaSetAccess &media, const OnMediaLocation &resource,
+                        const Pathname &dest_dir, FetcherJob::Flags flags );
+
+      /**
+       * auto discovery and reading of indexes
+       */
+      void autoaddIndexes( const filesystem::DirContent &content,
+                           MediaSetAccess &media,
+                           const OnMediaLocation &resource,
+                           const Pathname &dest_dir );
+      /**
+       * Provide the resource to \ref dest_dir
+       */
+      void provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir , const Pathname &deltafile);
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+
+    list<FetcherJob_Ptr>   _resources;
+    std::set<FetcherIndex_Ptr,SameFetcherIndex> _indexes;
+    std::set<Pathname> _caches;
+    // checksums read from the indexes
+    map<string, CheckSum> _checksums;
+    // cache of dir contents
+    map<string, filesystem::DirContent> _dircontent;
+
+    Fetcher::Options _options;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker, const Pathname &deltafile )
+  {
+    FetcherJob_Ptr job;
+    job.reset(new FetcherJob(resource, deltafile));
+    job->flags |= FetcherJob:: AlwaysVerifyChecksum;
+    _resources.push_back(job);
+  }
+
+  Fetcher::Impl::Impl()
+      : _options(0)
+  {
+  }
+
+  void Fetcher::Impl::setOptions( Fetcher::Options options )
+  { _options = options; }
+
+  Fetcher::Options Fetcher::Impl::options() const
+  { return _options; }
+
+  void Fetcher::Impl::enqueueDir( const OnMediaLocation &resource,
+                                  bool recursive,
+                                  const FileChecker &checker )
+  {
+    FetcherJob_Ptr job;
+    job.reset(new FetcherJob(resource));
+    if ( checker )
+        job->checkers.push_back(checker);
+    if ( recursive )
+        job->flags |= FetcherJob::Recursive;
+    job->flags |= FetcherJob::Directory;
+
+    _resources.push_back(job);
+  }
+
+  void Fetcher::Impl::enqueueDigestedDir( const OnMediaLocation &resource,
+                                          bool recursive,
+                                          const FileChecker &checker )
+  {
+    FetcherJob_Ptr job;
+    job.reset(new FetcherJob(resource));
+    if ( checker )
+        job->checkers.push_back(checker);
+    if ( recursive )
+        job->flags |= FetcherJob::Recursive;
+    job->flags |= FetcherJob::Directory;
+    job->flags |= FetcherJob::AlwaysVerifyChecksum;
+
+    _resources.push_back(job);
+
+  }
+
+  void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
+  {
+    FetcherJob_Ptr job;
+    job.reset(new FetcherJob(resource));
+    if ( checker )
+      job->checkers.push_back(checker);
+    _resources.push_back(job);
+  }
+
+  void Fetcher::Impl::addIndex( const OnMediaLocation &resource )
+  {
+    MIL << "adding index " << resource << endl;
+    _indexes.insert(FetcherIndex_Ptr(new FetcherIndex(resource)));
+  }
+
+
+  void Fetcher::Impl::reset()
+  {
+    _resources.clear();
+    _indexes.clear();
+    _checksums.clear();
+    _dircontent.clear();
+  }
+
+  void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
+  {
+    PathInfo info(cache_dir);
+    if ( info.isExist() )
+    {
+      if ( info.isDir() )
+      {
+        DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
+        _caches.insert(cache_dir);
+      }
+      else
+      {
+        // don't add bad cache directory, just log the error
+        ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
+      }
+    }
+    else
+    {
+        ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
+    }
+
+  }
+
+  // tries to provide resource to dest_dir from any of the configured additional
+  // cache paths where the file may already be present. returns true if the
+  // file was provided from the cache.
+  bool Fetcher::Impl::provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir )
+  {
+    Pathname dest_full_path = dest_dir + resource.filename();
+
+    // first check in the destination directory
+    if ( PathInfo(dest_full_path).isExist() )
+    {
+      if ( is_checksum( dest_full_path, resource.checksum() )
+           && (! resource.checksum().empty() ) )
+          return true;
+    }
+
+    MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
+    for_ ( it_cache, _caches.begin(), _caches.end() )
+    {
+      // does the current file exists in the current cache?
+      Pathname cached_file = *it_cache + resource.filename();
+      if ( PathInfo( cached_file ).isExist() )
+      {
+        DBG << "File '" << cached_file << "' exist, testing checksum " << resource.checksum() << endl;
+         // check the checksum
+        if ( is_checksum( cached_file, resource.checksum() ) && (! resource.checksum().empty() ) )
+        {
+          // cached
+          MIL << "file " << resource.filename() << " found in previous cache. Using cached copy." << endl;
+          // checksum is already checked.
+          // we could later implement double failover and try to download if file copy fails.
+           // replicate the complete path in the target directory
+          if( dest_full_path != cached_file )
+          {
+            if ( assert_dir( dest_full_path.dirname() ) != 0 )
+              ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
+
+            if ( filesystem::hardlinkCopy(cached_file, dest_full_path ) != 0 )
+            {
+              ERR << "Can't hardlink/copy " << cached_file + " to " + dest_dir << endl;
+              continue;
+            }
+          }
+          // found in cache
+          return true;
+        }
+      }
+    } // iterate over caches
+    return false;
+  }
+
+    void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
+  {
+    // no matter where did we got the file, try to validate it:
+    Pathname localfile = dest_dir + resource.filename();
+    // call the checker function
+    try
+    {
+      MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
+
+      for ( list<FileChecker>::const_iterator it = checkers.begin();
+            it != checkers.end();
+            ++it )
+      {
+        if (*it)
+        {
+          (*it)(localfile);
+        }
+        else
+        {
+          ERR << "Invalid checker for '" << localfile << "'" << endl;
+        }
+      }
+
+    }
+    catch ( const FileCheckException &e )
+    {
+      ZYPP_RETHROW(e);
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_RETHROW(e);
+    }
+    catch (...)
+    {
+      ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
+    }
+  }
+
+  void Fetcher::Impl::autoaddIndexes( const filesystem::DirContent &content,
+                                      MediaSetAccess &media,
+                                      const OnMediaLocation &resource,
+                                      const Pathname &dest_dir )
+  {
+      if ( _options & AutoAddSha1sumsIndexes )
+      {
+          // only try to add an index if it exists
+          filesystem::DirEntry shafile;
+          shafile.name = "SHA1SUMS"; shafile.type = filesystem::FT_FILE;
+          if ( find( content.begin(), content.end(), shafile ) != content.end() )
+          {
+              // add the index of this directory
+              OnMediaLocation indexloc(resource);
+              indexloc.changeFilename(resource.filename() + "SHA1SUMS");
+              addIndex(indexloc);
+              // we need to read it now
+              downloadAndReadIndexList(media, dest_dir);
+          }
+      }
+      if ( _options & AutoAddContentFileIndexes )
+      {
+          // only try to add an index if it exists
+          filesystem::DirEntry contentfile;
+          contentfile.name = "content"; contentfile.type = filesystem::FT_FILE;
+          if ( find( content.begin(), content.end(), contentfile ) != content.end() )
+          {
+              // add the index of this directory
+              OnMediaLocation indexloc(resource);
+              indexloc.changeFilename(resource.filename() + "content");
+              addIndex(indexloc);
+              // we need to read it now
+              downloadAndReadIndexList(media, dest_dir);
+          }
+      }
+  }
+
+  void Fetcher::Impl::getDirectoryContent( MediaSetAccess &media,
+                                           const OnMediaLocation &resource,
+                                           filesystem::DirContent &content )
+  {
+      if ( _dircontent.find(resource.filename().asString())
+           != _dircontent.end() )
+      {
+          filesystem::DirContent filled(_dircontent[resource.filename().asString()]);
+
+          std::copy(filled.begin(), filled.end(), std::back_inserter(content));
+      }
+      else
+      {
+          filesystem::DirContent tofill;
+          media.dirInfo( tofill,
+                         resource.filename(),
+                         false /* dots */,
+                         resource.medianr());
+          std::copy(tofill.begin(), tofill.end(), std::back_inserter(content));
+          _dircontent[resource.filename().asString()] = tofill;
+      }
+  }
+
+  void Fetcher::Impl::addDirJobs( MediaSetAccess &media,
+                                  const OnMediaLocation &resource,
+                                  const Pathname &dest_dir, FetcherJob::Flags flags  )
+  {
+      // first get the content of the directory so we can add
+      // individual transfer jobs
+      MIL << "Adding directory " << resource.filename() << endl;
+      filesystem::DirContent content;
+      getDirectoryContent(media, resource, content);
+
+      // this method test for the option flags so indexes are added
+      // only if the options are enabled
+      autoaddIndexes(content, media, resource, dest_dir);
+
+      for ( filesystem::DirContent::const_iterator it = content.begin();
+            it != content.end();
+            ++it )
+      {
+          // skip SHA1SUMS* as they were already retrieved
+          if ( str::hasPrefix(it->name, "SHA1SUMS") )
+              continue;
+
+          Pathname filename = resource.filename() + it->name;
+
+          switch ( it->type )
+          {
+          case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
+          case filesystem::FT_FILE:
+          {
+              CheckSum chksm(resource.checksum());
+              if ( _checksums.find(filename.asString()) != _checksums.end() )
+              {
+                  // the checksum can be replaced with the one in the index.
+                  chksm = _checksums[filename.asString()];
+                  //MIL << "resource " << filename << " has checksum in the index file." << endl;
+              }
+              else
+                  WAR << "Resource " << filename << " has no checksum in the index either." << endl;
+
+              if ( flags & FetcherJob::AlwaysVerifyChecksum )
+                  enqueueDigested(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
+              else
+                  enqueue(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
+              break;
+          }
+          case filesystem::FT_DIR: // newer directory.yast contain at least directory info
+              if ( flags & FetcherJob::Recursive )
+                  addDirJobs(media, filename, dest_dir, flags);
+              break;
+          default:
+              // don't provide devices, sockets, etc.
+              break;
+          }
+      }
+  }
+
+  void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir, const Pathname &deltafile )
+  {
+    bool got_from_cache = false;
+
+    // start look in cache
+    got_from_cache = provideFromCache(resource, dest_dir);
+
+    if ( ! got_from_cache )
+    {
+      MIL << "Not found in cache, downloading" << endl;
+
+      // try to get the file from the net
+      try
+      {
+        Pathname tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, deltafile );
+
+        Pathname dest_full_path = dest_dir + resource.filename();
+
+        if ( assert_dir( dest_full_path.dirname() ) != 0 )
+              ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
+        if ( filesystem::hardlinkCopy( tmp_file, dest_full_path ) != 0 )
+        {
+          if ( ! PathInfo(tmp_file).isExist() )
+              ERR << tmp_file << " does not exist" << endl;
+          if ( ! PathInfo(dest_full_path.dirname()).isExist() )
+              ERR << dest_full_path.dirname() << " does not exist" << endl;
+
+          media.releaseFile(resource); //not needed anymore, only eat space
+          ZYPP_THROW( Exception("Can't hardlink/copy " + tmp_file.asString() + " to " + dest_dir.asString()));
+        }
+
+        media.releaseFile(resource); //not needed anymore, only eat space
+      }
+      catch (Exception & excpt_r)
+      {
+        if ( resource.optional() )
+        {
+           ZYPP_CAUGHT(excpt_r);
+            WAR << "optional resource " << resource << " could not be transfered" << endl;
+            return;
+        }
+        else
+        {
+           excpt_r.remember("Can't provide " + resource.filename().asString() );
+            ZYPP_RETHROW(excpt_r);
+        }
+      }
+    }
+    else
+    {
+      // We got the file from cache
+      // continue with next file
+        return;
+    }
+  }
+
+  // helper class to consume a content file
+  struct ContentReaderHelper : public parser::susetags::ContentFileReader
+  {
+    ContentReaderHelper()
+    {
+      setRepoIndexConsumer( bind( &ContentReaderHelper::consumeIndex, this, _1 ) );
+    }
+
+    void consumeIndex( const parser::susetags::RepoIndex_Ptr & data_r )
+    { _repoindex = data_r; }
+
+    parser::susetags::RepoIndex_Ptr _repoindex;
+  };
+
+  // generic function for reading indexes
+  void Fetcher::Impl::readIndex( const Pathname &index, const Pathname &basedir )
+  {
+    if ( index.basename() == "SHA1SUMS" )
+      readSha1sumsIndex(index, basedir);
+    else if ( index.basename() == "content" )
+      readContentFileIndex(index, basedir);
+    else
+      WAR << index << ": index file format not known" << endl;
+  }
+
+  // reads a content file index
+  void Fetcher::Impl::readContentFileIndex( const Pathname &index, const Pathname &basedir )
+  {
+      ContentReaderHelper reader;
+      reader.parse(index);
+      MIL << index << " contains " << reader._repoindex->mediaFileChecksums.size() << " checksums." << endl;
+      for_( it, reader._repoindex->mediaFileChecksums.begin(), reader._repoindex->mediaFileChecksums.end() )
+      {
+          // content file entries don't start with /
+          _checksums[(basedir + it->first).asString()] = it->second;
+      }
+  }
+
+  // reads a SHA1SUMS file index
+  void Fetcher::Impl::readSha1sumsIndex( const Pathname &index, const Pathname &basedir )
+  {
+      std::ifstream in( index.c_str() );
+      string buffer;
+      if ( ! in.fail() )
+      {
+          while ( getline(in, buffer) )
+          {
+              vector<string> words;
+              str::split( buffer, back_inserter(words) );
+              if ( words.size() != 2 )
+                  ZYPP_THROW(Exception("Wrong format for SHA1SUMS file"));
+              //MIL << "check: '" << words[0] << "' | '" << words[1] << "'" << endl;
+              if ( ! words[1].empty() )
+                  _checksums[(basedir + words[1]).asString()] = CheckSum::sha1(words[0]);
+          }
+      }
+      else
+          ZYPP_THROW(Exception("Can't open SHA1SUMS file: " + index.asString()));
+  }
+
+  void Fetcher::Impl::downloadIndex( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir)
+  {
+    MIL << "downloading index " << resource << endl;
+    // create a new fetcher with a different state to transfer the
+    // file containing checksums and its signature
+    Fetcher fetcher;
+    // signature checker for index. We havent got the signature from
+    // the nextwork yet.
+    SignatureFileChecker sigchecker;
+
+    // build the name of the index and the signature
+    OnMediaLocation idxloc(resource);
+    OnMediaLocation sigloc(resource);
+    OnMediaLocation keyloc(resource);
+
+    // we should not fail the download if those don't exists
+    // the checking will warn later
+    sigloc.setOptional(true);
+    keyloc.setOptional(true);
+
+    // calculate signature and key name
+    sigloc.changeFilename( sigloc.filename().extend(".asc") );
+    keyloc.changeFilename( keyloc.filename().extend(".key") );
+
+    //assert_dir(dest_dir + idxloc.filename().dirname());
+
+    // transfer the signature
+    fetcher.enqueue(sigloc);
+    fetcher.start( dest_dir, media );
+    // if we get the signature, update the checker
+    if ( PathInfo(dest_dir + sigloc.filename()).isExist() )
+        sigchecker = SignatureFileChecker(dest_dir + sigloc.filename());
+
+    fetcher.reset();
+
+    // now the key
+    fetcher.enqueue(keyloc);
+    fetcher.start( dest_dir, media );
+    fetcher.reset();
+
+    // try to import the key
+    if ( PathInfo(dest_dir + keyloc.filename()).isExist() )
+        getZYpp()->keyRing()->importKey(PublicKey(dest_dir + keyloc.filename()), false);
+    else
+        WAR << "No public key specified by user for index '" << keyloc.filename() << "'"<< endl;
+
+    // now the index itself
+    fetcher.enqueue( idxloc, FileChecker(sigchecker) );
+    fetcher.start( dest_dir, media );
+    fetcher.reset();
+ }
+
+  // this method takes all the user pointed indexes, gets them and also tries to
+  // download their signature, and verify them. After that, its parses each one
+  // to fill the checksum cache.
+  void Fetcher::Impl::downloadAndReadIndexList( MediaSetAccess &media, const Pathname &dest_dir)
+  {
+      // if there is no indexes, then just return to avoid
+      // the directory listing
+      if ( _indexes.empty() )
+      {
+          MIL << "No indexes to read." << endl;
+          return;
+      }
+
+      for_( it_idx, _indexes.begin(), _indexes.end() )
+      {
+        if ( (*it_idx)->read )
+        {
+          DBG << "Already read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
+        }
+        else
+        {
+          // base::LogControl::TmpLineWriter shutUp;
+          downloadIndex( media, (*it_idx)->location, dest_dir );
+          // now we have the indexes in dest_dir
+          readIndex( dest_dir + (*it_idx)->location.filename(), (*it_idx)->location.filename().dirname() );
+          // Take care we don't process it again
+          MIL << "Remember read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
+          (*it_idx)->read = true;
+        }
+      }
+      MIL << "done reading indexes" << endl;
+  }
+
+  // start processing all fetcher jobs.
+  // it processes any user pointed index first
+  void Fetcher::Impl::start( const Pathname &dest_dir,
+                             MediaSetAccess &media,
+                             const ProgressData::ReceiverFnc & progress_receiver )
+  {
+    ProgressData progress(_resources.size());
+    progress.sendTo(progress_receiver);
+
+    downloadAndReadIndexList(media, dest_dir);
+
+    for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
+    {
+
+      if ( (*it_res)->flags & FetcherJob::Directory )
+      {
+          const OnMediaLocation location((*it_res)->location);
+          addDirJobs(media, location, dest_dir, (*it_res)->flags);
+          continue;
+      }
+
+      // may be this code can be factored out
+      // together with the autodiscovery of indexes
+      // of addDirJobs
+      if ( ( _options & AutoAddSha1sumsIndexes ) ||
+           ( _options & AutoAddContentFileIndexes ) )
+      {
+          // if auto indexing is enabled, then we need to read the
+          // index for each file. We look only in the directory
+          // where the file is. this is expensive of course.
+          filesystem::DirContent content;
+          getDirectoryContent(media, (*it_res)->location.filename().dirname(), content);
+          // this method test for the option flags so indexes are added
+          // only if the options are enabled
+          MIL << "Autodiscovering signed indexes on '"
+              << (*it_res)->location.filename().dirname() << "' for '"
+              << (*it_res)->location.filename() << "'" << endl;
+
+          autoaddIndexes(content, media, (*it_res)->location.filename().dirname(), dest_dir);
+
+          // also look in the root of the media
+          content.clear();
+          getDirectoryContent(media, Pathname("/"), content);
+          // this method test for the option flags so indexes are added
+          // only if the options are enabled
+          MIL << "Autodiscovering signed indexes on '"
+              << "/" << "' for '"
+              << (*it_res)->location.filename() << "'" << endl;
+
+          autoaddIndexes(content, media, Pathname("/"), dest_dir);
+      }
+
+      provideToDest(media, (*it_res)->location, dest_dir, (*it_res)->deltafile);
+
+      // if the file was not transfered, and no exception, just
+      // return, as it was an optional file
+      if ( ! PathInfo(dest_dir + (*it_res)->location.filename()).isExist() )
+          return;
+
+      // if the checksum is empty, but the checksum is in one of the
+      // indexes checksum, then add a checker
+      if ( (*it_res)->location.checksum().empty() )
+      {
+          if ( _checksums.find((*it_res)->location.filename().asString())
+               != _checksums.end() )
+          {
+              CheckSum chksm = _checksums[(*it_res)->location.filename().asString()];
+              ChecksumFileChecker digest_check(chksm);
+              (*it_res)->checkers.push_back(digest_check);
+          }
+          else
+          {
+              // if the index checksum is empty too, we only add the checker
+              // if the  AlwaysVerifyChecksum option is set on
+              if ( (*it_res)->flags & FetcherJob::AlwaysVerifyChecksum )
+              {
+                  // add the checker with the empty checksum
+                  ChecksumFileChecker digest_check((*it_res)->location.checksum());
+                  (*it_res)->checkers.push_back(digest_check);
+              }
+          }
+      }
+      else
+      {
+          // checksum is not empty, so add a checksum checker
+          ChecksumFileChecker digest_check((*it_res)->location.checksum());
+          (*it_res)->checkers.push_back(digest_check);
+      }
+
+      // validate job, this throws if not valid
+      validate((*it_res)->location, dest_dir, (*it_res)->checkers);
+
+      if ( ! progress.incr() )
+        ZYPP_THROW(AbortRequestException());
+    } // for each job
+  }
+
+  /** \relates Fetcher::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
+  {
+      for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
+      {
+          str << *it_res;
+      }
+      return str;
+  }
+
+  Fetcher::Fetcher()
+  : _pimpl( new Impl() )
+  {}
+
+  Fetcher::~Fetcher()
+  {}
+
+  void Fetcher::setOptions( Fetcher::Options options )
+  {
+    _pimpl->setOptions(options);
+  }
+
+  Fetcher::Options Fetcher::options() const
+  {
+    return _pimpl->options();
+  }
+
+  void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker, const Pathname &deltafile )
+  {
+    _pimpl->enqueueDigested(resource, checker, deltafile);
+  }
+
+  void Fetcher::enqueueDir( const OnMediaLocation &resource,
+                            bool recursive,
+                            const FileChecker &checker )
+  {
+      _pimpl->enqueueDir(resource, recursive, checker);
+  }
+
+  void Fetcher::enqueueDigestedDir( const OnMediaLocation &resource,
+                                    bool recursive,
+                                    const FileChecker &checker )
+  {
+      _pimpl->enqueueDigestedDir(resource, recursive, checker);
+  }
+
+
+  void Fetcher::addIndex( const OnMediaLocation &resource )
+  {
+    _pimpl->addIndex(resource);
+  }
+
+
+  void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker  )
+  {
+    _pimpl->enqueue(resource, checker);
+  }
+
+  void Fetcher::addCachePath( const Pathname &cache_dir )
+  {
+    _pimpl->addCachePath(cache_dir);
+  }
+
+  void Fetcher::reset()
+  {
+    _pimpl->reset();
+  }
+
+  void Fetcher::start( const Pathname &dest_dir,
+                       MediaSetAccess &media,
+                       const ProgressData::ReceiverFnc & progress_receiver )
+  {
+    _pimpl->start(dest_dir, media, progress_receiver);
+  }
+
+  std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
+  {
+    return str << *obj._pimpl;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/Fetcher.h b/zypp/Fetcher.h
new file mode 100644 (file)
index 0000000..4cb8c54
--- /dev/null
@@ -0,0 +1,329 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Fetcher.h
+ *
+*/
+#ifndef ZYPP_FETCHER_H
+#define ZYPP_FETCHER_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/base/Flags.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/Digest.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/FileChecker.h"
+#include "zypp/ProgressData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /**
+  * This class allows to retrieve a group of files in a confortable
+  * way, providing some smartness that does not belong to the
+  * media layer like:
+  *
+  * \li Configurable local caches to retrieve already
+  *     donwloaded files.
+  * \li File checkers that can check for right checksums
+  *     digital signatures, etc.
+  *
+  * \code
+  * MediaSetAccess access(url, path);
+  * Fetcher fetcher;
+  * fetcher.enqueue( OnMediaLocation().setLocation("/somefile") );
+  * fetcher.addCachePath("/tmp/cache")
+  * fetcher.start( "/download-dir, access );
+  * fetcher.reset();
+  * \endcode
+  *
+  * To use the checkers. just create a functor implementing
+  * bool operator()(const Pathname &file) \see FileChecker.
+  * Pass the necessary validation data in the constructor
+  * of the functor, and pass the object to the \ref enqueue
+  * method.
+  *
+  * \code
+  * ChecksumFileChecker checker(CheckSum("sha1", "....");
+  * fetcher.enqueue( location, checker);
+  * \endcode
+  *
+  * If you need to use more than one checker
+  * \see CompositeFileChecker
+  *
+  * Additionally, you can automatically enqueue a job
+  * with a checksum checker by using \ref enqueueDigested
+  * which will use the \ref OnMediaLocation checksum
+  * automatically.
+  *
+  * \code
+  * location.setChecksum(CheckSum("sha1", "...."));
+  * fetcher.enqueueDigested(location);
+  * \endcode
+  *
+  * \note If the checksum of the location is empty, but
+  * \ref enqueueDigested is used, then the user will get a
+  * warning that the file has no checksum.
+  *
+  * Additionally, Fetcher supports checking the downloaded
+  * content by using signed indexes on the remote side.
+  *
+  * \code
+  * MediaSetAccess access(url, path);
+  * Fetcher fetcher;
+  * fetcher.addIndex(OnMediaLocation("/content"));
+  * fetcher.enqueue( OnMediaLocation().setLocation("/somefile") );
+  * fetcher.start( "/download-dir, access );
+  * fetcher.reset();
+  * \endcode
+  *
+  * Indexes are supported in SHA1SUMS format (simple text file)
+  * with sha1sums and file name, or content file, whith
+  * HASH SHA1 line entries.
+  * 
+  * \note The indexes file names are relative to the directory
+  * where the index is.
+  */
+  class Fetcher
+  {
+    friend std::ostream & operator<<( std::ostream & str,
+                                      const Fetcher & obj );
+  public:
+    /** Implementation  */
+    class Impl;
+  public:
+
+    /**
+     * Various option flags to change behavior
+     */
+    enum Option
+    {
+      /**
+       * If a content file is found, it is
+       * downloaded and read.
+       */
+      AutoAddContentFileIndexes = 0x0001,
+      /**
+       * If a SHA1SUMS file is found, it is
+       * downloaded and read.
+       */
+      AutoAddSha1sumsIndexes = 0x0002,
+      /**
+       * If a content or SHA1SUMS file is found, 
+       * it is downloaded and read.
+       */
+      AutoAddIndexes = 0x0001 | 0x0002,
+    };
+    ZYPP_DECLARE_FLAGS(Options, Option);
+
+    /** Default ctor */
+    Fetcher();
+    /** Dtor */
+    virtual ~Fetcher();
+
+  public:
+
+   /**
+    * Set the Fetcher options
+    * \see Fetcher::Options
+    */
+    void setOptions( Options options );
+
+   /**
+    * Get current options
+    * \see Fetcher::Options
+    */
+    Options options() const;
+
+   /**
+    * Adds an index containing metadata (for example
+    * checksums ) that will be retrieved and read
+    * before the job processing starts.
+    *
+    * Nothing will be transfered or checked
+    * until \ref start() is called.
+    *
+    * The index is relative to the media path, and
+    * the listed files too.
+    *
+    * Indexes in the SHA1SUM format, and YaST
+    * content file
+    *
+    * The file has to be signed or the user will be
+    * warned that the file is unsigned. You can
+    * place the signature next to the file adding the
+    * .asc extension.
+    *
+    * If you expect the key to not to be in the target
+    * system, then you can place it next to the index
+    * using adding the .key extension.
+    *
+    */
+    void addIndex( const OnMediaLocation &resource );
+   
+   /**
+    * Enqueue a object for transferal, they will not
+    * be transfered until \ref start() is called
+    *
+    */
+    void enqueue( const OnMediaLocation &resource,
+                  const FileChecker &checker = FileChecker() );
+    
+    /**
+    * Enqueue a object for transferal, they will not
+    * be transfered until \ref start() is called
+    *
+    * \note As \ref OnMediaLocation contains the digest information,
+    * a \ref ChecksumFileChecker is automatically added to the
+    * transfer job, so make sure you don't add another one or
+    * the user could be asked twice.
+    *
+    * \todo FIXME implement checker == operator to avoid this.
+    *
+    * the optional deltafile argument describes a file that can
+    * be used for delta download algorithms. Usable files are
+    * uncompressed files or files compressed with gzip --rsyncable.
+    * Other files like rpms do not work, as the compression
+    * breaks the delta algorithm.
+    */
+    void enqueueDigested( const OnMediaLocation &resource,
+                          const FileChecker &checker = FileChecker(), const Pathname &deltafile = Pathname());
+
+
+    /**
+     * Enqueue a directory
+     *
+     * As the files to be enqueued are not known
+     * in advance, all files whose checksum can
+     * be found in some index are enqueued with
+     * checksum checking. Otherwise they are not.
+     *
+     * Some index may provide
+     * the checksums, either by \ref addIndex or
+     * using \ref AutoAddIndexes flag.
+     *
+     * Files are checked by providing a
+     * SHA1SUMS or content file listing
+     * <checksum> filename
+     * and a respective SHA1SUMS.asc/content.asc which has
+     * the signature for the checksums.
+     *
+     * If you expect the user to not have the key of
+     * the signature either in the trusted or untrusted
+     * keyring, you can offer it as SHA1SUMS.key (or content.key)
+     *
+     * \param recursive True if the complete tree should
+     * be enqueued.
+     *
+     * \note As \ref checksums are read from the index,
+     * a \ref ChecksumFileChecker is automatically added to
+     * transfer jobs having a checksum available,
+     * so make sure you don't add another one or
+     * the user could be asked twice.
+     *
+     * \note The format of the file SHA1SUMS is the output of:
+     * ls | grep -v SHA1SUMS | xargs sha1sum > SHA1SUMS
+     * in each subdirectory.
+     *
+     * \note Every file SHA1SUMS.* except of SHA1SUMS.(asc|key|(void)) will
+     * not be transfered and will be ignored.
+     *
+     */
+    void enqueueDir( const OnMediaLocation &resource,
+                     bool recursive = false,
+                     const FileChecker &checker = FileChecker() );
+
+    /**
+     * Enqueue a directory and always check for
+     * checksums.
+     *
+     * As the files to be enqueued are not known
+     * in advance, all files are enqueued with
+     * checksum checking. If the checksum of some file is
+     * not in some index, then there will be a verification
+     * warning ( \ref DigestReport ).
+     *
+     * Therefore some index will need to provide
+     * the checksums, either by \ref addIndex or
+     * using \ref AutoAddIndexes flag.
+     *
+     * Files are checked by providing a
+     * SHA1SUMS or content file listing
+     * <checksum> filename
+     * and a respective SHA1SUMS.asc/content.asc which has
+     * the signature for the checksums.
+     *
+     * If you expect the user to not have the key of
+     * the signature either in the trusted or untrusted
+     * keyring, you can offer it as SHA1SUMS.key (or content.key)
+     *
+     * \param recursive True if the complete tree should
+     * be enqueued.
+     *
+     * \note As \ref checksums are read from the index,
+     * a \ref ChecksumFileChecker is automatically added to every
+     * transfer job, so make sure you don't add another one or
+     * the user could be asked twice.
+     *
+     * \note The format of the file SHA1SUMS is the output of:
+     * ls | grep -v SHA1SUMS | xargs sha1sum > SHA1SUMS
+     * in each subdirectory.
+     *
+     * \note Every file SHA1SUMS.* except of SHA1SUMS.(asc|key|(void)) will
+     * not be transfered and will be ignored.
+     *
+     */
+    void enqueueDigestedDir( const OnMediaLocation &resource,
+                             bool recursive = false,
+                             const FileChecker &checker = FileChecker() );
+    
+    /**
+    * adds a directory to the list of directories
+    * where to look for cached files
+    */
+    void addCachePath( const Pathname &cache_dir );
+    
+    /**
+     * Reset the transfer (jobs) list
+     * \note It does not reset the cache directory list
+     */
+    void reset();
+    
+    /**
+    * start the transfer to a destination directory
+    * \a dest_dir
+    * You have to provde a media set access
+    * \a media to get the files from
+    * The file tree will be replicated inside this
+    * directory
+    *
+    */
+    void start( const Pathname &dest_dir,
+                MediaSetAccess &media,
+                const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+  ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Fetcher::Options);
+
+  /** \relates Fetcher Stream output */
+  std::ostream & operator<<( std::ostream & str, const Fetcher & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_FETCHER_H
diff --git a/zypp/FileChecker.cc b/zypp/FileChecker.cc
new file mode 100644 (file)
index 0000000..f6b7abc
--- /dev/null
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/FileChecker.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/FileChecker.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/Digest.h"
+#include "zypp/KeyRing.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
+    : _checksum(checksum)
+  {
+  }
+
+  void ChecksumFileChecker::operator()( const Pathname &file ) const
+  {
+      //MIL << "checking " << file << " file against checksum '" << _checksum << "'" << endl;
+    callback::SendReport<DigestReport> report;
+
+    if ( _checksum.empty() )
+    {
+      MIL << "File " <<  file << " has no checksum available." << std::endl;
+      if ( report->askUserToAcceptNoDigest(file) )
+      {
+        MIL << "User accepted " <<  file << " with no checksum." << std::endl;
+        return;
+      }
+      else
+      {
+        ZYPP_THROW( FileCheckException( file.basename() + " has no checksum" ) );
+      }
+    }
+    else
+    {
+      CheckSum real_checksum( _checksum.type(), filesystem::checksum( file, _checksum.type() ));
+      if ( (real_checksum != _checksum) )
+      {
+        if ( report->askUserToAcceptWrongDigest( file, _checksum.checksum(), real_checksum.checksum() ) )
+        {
+          WAR << "User accepted " <<  file << " with WRONG CHECKSUM." << std::endl;
+          return;
+        }
+        else
+        {
+          ZYPP_THROW( FileCheckException( file.basename() + " has wrong checksum" ) );
+        }
+      }
+    }
+  }
+
+  void NullFileChecker::operator()(const Pathname &file ) const
+  {
+    MIL << "+ null check on " << file << endl;
+    return;
+  }
+
+  void CompositeFileChecker::operator()(const Pathname &file ) const
+  {
+    //MIL << _checkers.size() << " checkers" << endl;
+    for ( list<FileChecker>::const_iterator it = _checkers.begin(); it != _checkers.end(); ++it )
+    {
+      if ( *it )
+      {
+        //MIL << "+ chk" << endl;
+        (*it)(file);
+      }
+      else
+      {
+        ERR << "Invalid checker" << endl;
+      }
+    }
+  }
+
+  void CompositeFileChecker::add( const FileChecker &checker )
+  {
+    //MIL << "||# " << _checkers.size() << endl;
+    _checkers.push_back(checker);
+    //MIL << "||* " << _checkers.size() << endl;
+
+  }
+
+   SignatureFileChecker::SignatureFileChecker( const Pathname &signature )
+       : _signature(signature)
+  {
+
+  }
+
+
+  SignatureFileChecker::SignatureFileChecker()
+  {
+  }
+
+  void SignatureFileChecker::setKeyContext(const KeyContext & keycontext)
+  { _context = keycontext; }
+
+  void SignatureFileChecker::addPublicKey( const Pathname & publickey, const KeyContext & keycontext )
+  { addPublicKey( PublicKey(publickey), keycontext ); }
+
+  void SignatureFileChecker::addPublicKey( const PublicKey & publickey, const KeyContext & keycontext )
+  {
+    getZYpp()->keyRing()->importKey(publickey, false);
+    _context = keycontext;
+  }
+
+  void SignatureFileChecker::operator()(const Pathname &file ) const
+  {
+    ZYpp::Ptr z = getZYpp();
+
+    if ( (! PathInfo(_signature).isExist()) && (!_signature.empty()))
+    {
+      ZYPP_THROW(FileCheckException("Signature " + _signature.asString() + " not found."));
+    }
+
+    MIL << "checking " << file << " file validity using digital signature.." << endl;
+    bool valid = z->keyRing()->verifyFileSignatureWorkflow( file, file.basename(), _signature, _context);
+
+    if (!valid)
+      ZYPP_THROW( FileCheckException( "Signature verification failed for "  + file.basename() ) );
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const FileChecker & obj )
+  {
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/FileChecker.h b/zypp/FileChecker.h
new file mode 100644 (file)
index 0000000..579a627
--- /dev/null
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/FileChecker.h
+ *
+*/
+#ifndef ZYPP_FILECHECKER_H
+#define ZYPP_FILECHECKER_H
+
+#include <iosfwd>
+#include <list>
+#include "zypp/base/Exception.h"
+#include "zypp/base/Function.h"
+#include "zypp/PathInfo.h"
+#include "zypp/CheckSum.h"
+#include "zypp/KeyContext.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class PublicKey;
+
+  /**
+   * Functor signature used to check files.
+   * \param file File to check.
+   *
+   * \throws FileCheckException when the file does not
+   * validate and the user don't want to continue.
+   */
+  typedef function<void ( const Pathname &file )> FileChecker;
+
+  class FileCheckException : public Exception
+  {
+  public:
+    FileCheckException(const std::string &msg)
+      : Exception(msg)
+    {}
+  };
+
+  class CheckSumCheckException : public FileCheckException
+  {
+    //TODO
+  };
+
+  class SignatureCheckException : public FileCheckException
+  {
+    //TODO
+  };
+
+  /**
+   * Built in file checkers
+   */
+
+  /**
+   * \short Checks for a valid checksum and interacts with the user.
+   */
+   class ChecksumFileChecker
+   {
+   public:
+     /**
+      * Constructor.
+      * \param checksum Checksum that validates the file
+      */
+     ChecksumFileChecker( const CheckSum &checksum );
+     /**
+      * \short Try to validate the file
+      * \param file File to validate.
+      *
+      * \throws CheckSumCheckException if validation fails
+      */
+     void operator()( const Pathname &file ) const;
+   private:
+     CheckSum _checksum;
+   };
+
+   /**
+    * \short Checks for the validity of a signature
+    */
+   class SignatureFileChecker
+   {
+     public:
+      /**
+      * Constructor.
+      * \param signature Signature that validates the file
+      */
+      SignatureFileChecker( const Pathname &signature );
+
+      /**
+      * Default Constructor.
+      * \short Signature for unsigned files
+      * Use it when you dont have a signature but you want
+      * to check the user to accept an unsigned file.
+      */
+      SignatureFileChecker();
+
+      /**
+       * Set context for this checker.
+       *
+       * Use this method if you're not adding the key (with context) via
+       * one of the addPublicKey methods. The addPublicKey method overwrites
+       * the context.
+       */
+      void setKeyContext(const KeyContext & keycontext);
+
+      /**
+       * add a public key to the list of known keys
+       */
+      void addPublicKey( const PublicKey & publickey, const KeyContext & keycontext = KeyContext());
+      /** \overload Convenience taking the public keys pathname. */
+      void addPublicKey( const Pathname & publickey, const KeyContext & keycontext = KeyContext());
+
+      /**
+      * \short Try to validate the file
+      * \param file File to validate.
+      *
+      * \throws SignatureCheckException if validation fails
+      */
+      void operator()( const Pathname &file ) const;
+
+     protected:
+      Pathname _signature;
+      KeyContext _context;
+   };
+
+   /**
+   * \short Checks for nothing
+   * Used as the default checker
+   */
+   class NullFileChecker
+   {
+   public:
+     void operator()( const Pathname &file )  const;
+   };
+
+   /**
+    * \short Checker composed of more checkers.
+    *
+    * Allows to create a checker composed of various
+    * checkers altothether. It will only
+    * validate if all the checkers validate.
+    *
+    * \code
+    * CompositeFileChecker com;
+    * com.add(checker1);
+    * com.add(checker2);
+    * fetcher.enqueue(location, com);
+    * \endcode
+    */
+   class CompositeFileChecker
+   {
+   public:
+     void add( const FileChecker &checker );
+    /**
+     * \throws FileCheckException if validation fails
+     */
+     void operator()( const Pathname &file ) const;
+
+     int checkersSize() const { return _checkers.size(); }
+   private:
+     std::list<FileChecker> _checkers;
+   };
+
+  /** \relates FileChecker Stream output */
+  std::ostream & operator<<( std::ostream & str, const FileChecker & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_FILECHECKER_H
diff --git a/zypp/Filter.h b/zypp/Filter.h
new file mode 100644 (file)
index 0000000..e425836
--- /dev/null
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Filter.h
+ *
+*/
+#ifndef ZYPP_FILTER_H
+#define ZYPP_FILTER_H
+
+#include <iosfwd>
+
+#include "zypp/base/Functional.h"
+#include "zypp/base/Function.h"
+// #include "zypp/ResFilters.h"  included at the end!
+#include "zypp/sat/Pool.h"
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace filter
+  { /////////////////////////////////////////////////////////////////
+    /** \defgroup POOLFILTER Collection solvable filter functors.
+     *
+     * All functors should be able to process \ref Solvable as well
+     * as \ref PoolItem.
+     *
+     * \code
+     *   // The same filter...
+     *   filter::ByLocaleSupport f( Locale("de") );
+     *
+     *   // ...can be used to iterate the sat::Pool...
+     *   sat::Pool satpool( sat::Pool::instance() );
+     *   for_( it, satpool.filterBegin(f), satpool.filterEnd(f) )
+     *   {
+     *     MIL << *it << endl; // prints sat::Solvable
+     *   }
+     *
+     *   // ...as well as the ResPool.
+     *   ResPool   pool   ( ResPool::instance() );
+     *   for_( it, pool.filterBegin(f), pool.filterEnd(f) )
+     *   {
+     *     MIL << *it << endl; // prints PoolItem
+     *   }
+     * \endcode
+     * \ingroup g_Functor
+     */
+    //@{
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ByLocaleSupport
+    //
+    /** Filter solvables according to their locale support.
+    */
+    class ByLocaleSupport
+    {
+      private:
+        typedef bool (sat::Solvable::*LS1) (const Locale &) const;
+        typedef bool (sat::Solvable::*LS2) (const LocaleSet &) const;
+
+      public:
+        /** Solvables with locale support. */
+        ByLocaleSupport()
+        : _sel( boost::mem_fun_ref( &sat::Solvable::supportsLocales ) )
+        {}
+
+        /** Solvables supporting \c locale_r. */
+        explicit ByLocaleSupport( const Locale & locale_r )
+        : _sel( boost::bind( boost::mem_fun_ref( (LS1)&sat::Solvable::supportsLocale ), _1, locale_r ) )
+        {}
+
+        /** Solvables supporting at least one locale in \c locales_r. */
+        explicit ByLocaleSupport( const LocaleSet & locales_r )
+        : _sel( boost::bind( boost::mem_fun_ref( (LS2)&sat::Solvable::supportsLocale ), _1, locales_r ) )
+        {}
+
+      public:
+        /** Filter on \ref Solvable. */
+        bool operator()( const sat::Solvable & solv_r ) const
+        { return _sel && _sel( solv_r ); }
+
+        /** Filter fitting PoolItem/ResObject. */
+        template<class _Solv>
+        bool operator()( const _Solv & solv_r ) const
+        { return operator()( solv_r.satSolvable() ); }
+
+      private:
+        function<bool(const sat::Solvable &)> _sel;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ByKind
+    //
+    /** Filter solvables according to their kind.
+    */
+    class ByKind
+    {
+      public:
+        ByKind( const ResKind & kind_r )
+        : _kind( kind_r )
+        {}
+
+      public:
+        /** Filter on \ref Solvable. */
+        bool operator()( const sat::Solvable & solv_r ) const
+        { return solv_r.isKind( _kind ); }
+
+        /** Filter fitting PoolItem/ResObject. */
+        template<class _Solv>
+        bool operator()( const _Solv & solv_r ) const
+        { return operator()( solv_r.satSolvable() ); }
+
+      private:
+        ResKind _kind;
+    };
+
+    /** \relates ByKind templated convenience ctor. */
+    template<class _Res>
+    inline ByKind byKind()
+    { return ByKind( ResTraits<_Res>::kind ); }
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ByStatus
+    //
+    /** Filter solvables according to their status.
+    */
+    class ByStatus
+    {
+      public:
+        typedef bool (ResStatus::*Predicate)() const;
+
+      public:
+        ByStatus( Predicate pred_r = 0 )
+        : _pred( pred_r )
+        {}
+
+      public:
+        /** Filter on \ref PoolItem. */
+        bool operator()( const PoolItem & pi_r ) const
+        { return _pred && (pi_r.status().*_pred)(); }
+
+        /** Filter fitting sat::Solvable/ResObject. */
+        template<class _Solv>
+        bool operator()( const _Solv & solv_r ) const
+        { return operator()( PoolItem(solv_r) ); }
+
+      private:
+        Predicate _pred;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SameItemAs
+    //
+    /** Filter items with at least same NVRA, vendor.
+     * This is usually used to find available packages
+     * that matche an insytalled one.
+    */
+    class SameItemAs
+    {
+      public:
+        SameItemAs( const sat::Solvable & solv_r )
+        : _item( solv_r )
+        {}
+
+        /** Fitting PoolItem/ResObject. */
+        template<class _Solv>
+        SameItemAs( const _Solv & solv_r )
+        : _item( solv_r.satSolvable() )
+        {}
+
+      public:
+        /** Filter on \ref Solvable. */
+        bool operator()( const sat::Solvable & solv_r ) const
+        {
+          return solv_r.name()    == _item.name()
+              && solv_r.edition() == _item.edition()
+              && solv_r.arch()    == _item.arch()
+              && solv_r.vendor()  == _item.vendor();
+        }
+
+        /** Filter fitting PoolItem/ResObject. */
+        template<class _Solv>
+        bool operator()( const _Solv & solv_r ) const
+        { return operator()( solv_r.satSolvable() ); }
+
+      private:
+        sat::Solvable _item;
+    };
+    ///////////////////////////////////////////////////////////////////
+    //@}
+
+  /////////////////////////////////////////////////////////////////
+  } // namespace filter
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#include "zypp/ResFilters.h"
+
+#endif // ZYPP_FILTER_H
diff --git a/zypp/Glob.cc b/zypp/Glob.cc
new file mode 100644 (file)
index 0000000..a7281eb
--- /dev/null
@@ -0,0 +1,63 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Glob.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/Glob.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    int Glob::add( const char * pattern_r, Flags flags_r )
+    {
+      static Flags _APPEND( GLOB_APPEND ); // not published
+      if ( ! flags_r )
+        flags_r = _defaultFlags;
+      if ( _result )
+        flags_r |= _APPEND;
+      else
+        _result.reset( new ::glob_t );
+      return( _lastGlobReturn = ::glob( pattern_r, flags_r, NULL, &(*_result) ) );
+    }
+
+    void Glob::clear()
+    {
+      if ( _result )
+      {
+        ::globfree( &(*_result) );
+        _result.reset();
+        _lastGlobReturn = 0;
+      }
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Glob & obj )
+    {
+      return dumpRange( str << "(" << obj.size() << ")", obj.begin(), obj.end() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Glob.h b/zypp/Glob.h
new file mode 100644 (file)
index 0000000..c86f734
--- /dev/null
@@ -0,0 +1,271 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Glob.h
+ *
+*/
+#ifndef ZYPP_GLOB_H
+#define ZYPP_GLOB_H
+
+extern "C"
+{
+#include <glob.h>
+}
+
+#include <iosfwd>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/Flags.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Glob
+    //
+    /** Find pathnames matching a pattern.
+     * \code
+     * Glob glob( Glob::_BRACE );
+     * glob.add( "/somewhere/solverTestcase/ *{.xml,.xml.gz}" );
+     * glob.add( "/somewhere/else/a*" );
+     * for_( it, glob.begin(), glob.end() )
+     *   ...
+     * \endcode
+     * \code
+     * std::list<Pathname> plist;
+     * Glob::collect( "/somewherre/solverTestcase/ *{.xml,.xml.gz}", Glob::_BRACE,
+     *                std::back_inserter( plist ) );
+     * \endcode
+     * \see Manual page glob(3)
+     */
+    class Glob : private base::NonCopyable
+    {
+      public:
+        typedef size_t size_type;
+        typedef const char * value_type;
+
+        /** Iterate NULL terminated \c char* array. */
+        class const_iterator : public boost::iterator_adaptor<
+              const_iterator                // Derived
+            , char **                       // Base
+            , value_type                    // Value
+            , boost::forward_traversal_tag  // CategoryOrTraversal
+            , const value_type              // Reference
+            >
+        {
+          public:
+            const_iterator()
+            : const_iterator::iterator_adaptor_( 0 )
+            {}
+
+            explicit const_iterator( char ** _idx )
+            : const_iterator::iterator_adaptor_( _idx && *_idx ? _idx : 0 )
+            {}
+
+          private:
+            friend class boost::iterator_core_access;
+            void increment()
+            {
+              if ( base_reference() && !*(++base_reference()) )
+                base_reference() = 0;
+            }
+            reference dereference() const
+            { return( base() ? *base() : 0 ); }
+        };
+        ///////////////////////////////////////////////////////////////////
+
+      public:
+        /** Individual bits to combine in \ref Flags. */
+        enum Bits {
+          _ERR         = GLOB_ERR,             //!< Return on read errors.
+          _MARK                = GLOB_MARK,            //!< Append a slash to each name.
+          _NOSORT      = GLOB_NOSORT,          //!< Don't sort the names.
+          // unsupported _DOOFFS = GLOB_DOOFFS,        //!< Insert PGLOB->gl_offs NULLs.
+          _NOCHECK     = GLOB_NOCHECK,         //!< If nothing matches, return the pattern.
+          // autoapplied _APPEND = GLOB_APPEND,        //!< Append to results of a previous call.
+          _NOESCAPE    = GLOB_NOESCAPE,        //!< Backslashes don't quote metacharacters.
+          _PERIOD      = GLOB_PERIOD,          //!< Leading `.' can be matched by metachars.
+          // unsupported _MAGCHAR = GLOB_MAGCHAR,//!< Set in gl_flags if any metachars seen.
+          _ALTDIRFUNC  = GLOB_ALTDIRFUNC,      //!< Use gl_opendir et al functions.
+          _BRACE       = GLOB_BRACE,           //!< Expand "{a,b}" to "a" "b".
+          _NOMAGIC     = GLOB_NOMAGIC,         //!< If no magic chars, return the pattern.
+          _TILDE       = GLOB_TILDE,           //!< Expand ~user and ~ to home directories.
+          _ONLYDIR     = GLOB_ONLYDIR,         //!< Match only directories.
+          _TILDE_CHECK = GLOB_TILDE_CHECK,     //!< Like GLOB_TILDE but return an error if the user name is not available.
+        };
+
+        /** type Flags: Type-safe OR-combination of \ref Bits. */
+        ZYPP_DECLARE_FLAGS( Flags, Bits );
+
+      public:
+        /** Default ctor optionally taking the default flags.
+         * The flags passed here are the default for \ref add.
+         * \see \ref setDefaultFlags
+        */
+        Glob( Flags flags_r = Flags() )
+        : _defaultFlags( flags_r )
+        {}
+
+        /** Ctor adding pathnames matching \a pattern_r.
+         * The flags passed here are the default for \ref add.
+         * \see \ref setDefaultFlags
+        */
+        explicit Glob( const Pathname & pattern_r, Flags flags_r = Flags() )
+        : _defaultFlags( flags_r )
+        { add( pattern_r, flags_r ); }
+        /** \overload */
+        explicit Glob( const std::string & pattern_r, Flags flags_r = Flags() )
+        : _defaultFlags( flags_r )
+        { add( pattern_r, flags_r ); }
+        /** \overload */
+        explicit Glob( const char * pattern_r, Flags flags_r = Flags() )
+        : _defaultFlags( flags_r )
+        { add( pattern_r, flags_r ); }
+
+        /** Dtor */
+        ~Glob()
+        { if ( _result ) ::globfree( &(*_result) ); }
+
+        /** Add pathnames matching \a pattern_r to the current result.
+         *
+         * Any flags passed here override the global default passed to
+         * the ctor. GLOB_APPEND is atomatically added to the flags
+         * f needed.
+         *
+         * This invalidates all iterators.
+         * \see \ref setDefaultFlags
+         * \return the value returned by ::glob().
+         */
+        int add( const Pathname & pattern_r, Flags flags_r = Flags() )
+        { return add( pattern_r.c_str(), flags_r ); }
+        /** \overload */
+        int add( const std::string & pattern_r, Flags flags_r = Flags() )
+        { return add( pattern_r.c_str(), flags_r ); }
+        /** \overload */
+        int add( const char * pattern_r, Flags flags_r = Flags() );
+
+        /** Clear all results found so far. \ref defaultFlags remain active. */
+        void clear();
+
+        /** Clear all results and reset \ref defaultFlags. */
+        void reset( Flags flags_r = Flags() )
+        { clear(); setDefaultFlags( flags_r ); }
+
+
+      public:
+        /** The default flags passed to \c ::glob(). */
+        Flags defaultFlags() const
+        { return _defaultFlags; }
+
+        /** Set the default flags passed to \c ::glob(). */
+        void setDefaultFlags( Flags flags_r = Flags() )
+        { _defaultFlags = flags_r; }
+
+        /** Returns the value returned by the last call to \c ::glob().
+         * \c Zero on successful completion. Otherwise \c GLOB_NOSPACE or \c GLOB_ABORTED
+         * or \c GLOB_NOMATCH.
+         */
+        int lastGlobReturn() const
+        { return _lastGlobReturn; }
+
+      public:
+        /** Whether matches were found. */
+        bool empty() const
+        { return ! ( _result && _result->gl_pathc ); }
+
+        /** The number of matches found so far. */
+        size_type size() const
+        { return( _result ? _result->gl_pathc : 0 ); }
+
+        /** Iterator pointing to the first result. */
+        const_iterator begin() const
+        { return( _result ? const_iterator( _result->gl_pathv ) : const_iterator() ); }
+
+        /** Iterator pointing behind the last result. */
+        const_iterator end() const
+        { return const_iterator(); }
+
+      public:
+
+        /** \name Collecting Glob results to some _OutputIterator
+         * \code
+         * std::list<Pathname> p;
+         * Glob::collect( "/bin/a*.dat}", std::back_inserter(p) );
+         * Glob::collect( "/bin/a*{.xml,.xml.gz}", Glob::_BRACE, std::back_inserter(p) );
+         * \endcode
+         */
+        //@{
+        /** Write glob result to some \c OutputIterator. */
+        template<class _OutputIterator>
+        static int collect( const Pathname & pattern_r, _OutputIterator result_r )
+        { return collect( pattern_r.c_str(), Flags(), result_r ); }
+        /** \overload */
+        template<class _OutputIterator>
+        static int collect( const std::string & pattern_r, _OutputIterator result_r )
+        { return collect( pattern_r.c_str(), Flags(), result_r ); }
+        /** \overload */
+        template<class _OutputIterator>
+        static int collect( const char * pattern_r, _OutputIterator result_r )
+        { return collect( pattern_r, Flags(), result_r ); }
+
+        /** \overload With \ref Flags */
+        template<class _OutputIterator>
+        static int collect( const Pathname & pattern_r, Flags flags_r, _OutputIterator result_r )
+        { return collect( pattern_r.c_str(), flags_r, result_r ); }
+        /** \overload */
+        template<class _OutputIterator>
+        static int collect( const std::string & pattern_r, Flags flags_r, _OutputIterator result_r )
+        { return collect( pattern_r.c_str(), flags_r, result_r ); }
+        /** \overload */
+        template<class _OutputIterator>
+        static int collect( const char * pattern_r, Flags flags_r, _OutputIterator result_r )
+        {
+          Glob glob( pattern_r, flags_r );
+          if ( glob.lastGlobReturn() == 0 )
+            for_( it, glob.begin(), glob.end() )
+              (*result_r)++ = typename _OutputIterator::container_type::value_type(*it);
+          return glob.lastGlobReturn();
+        }
+        //@}
+
+      private:
+        Flags _defaultFlags;
+        scoped_ptr< ::glob_t> _result;
+        DefaultIntegral<int,0> _lastGlobReturn;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Glob Stream output */
+    std::ostream & operator<<( std::ostream & str, const Glob & obj );
+
+    /** \relates Glob::const_iterator Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Glob::const_iterator & obj )
+    { return str << *obj; }
+
+    ZYPP_DECLARE_OPERATORS_FOR_FLAGS( Glob::Flags );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_GLOB_H
diff --git a/zypp/HistoryLog.cc b/zypp/HistoryLog.cc
new file mode 100644 (file)
index 0000000..d1c96ba
--- /dev/null
@@ -0,0 +1,298 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/HistoryLog.cc
+ *
+ */
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+
+#include "zypp/ZConfig.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/Date.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/Package.h"
+#include "zypp/RepoInfo.h"
+
+#include "zypp/HistoryLog.h"
+#include "zypp/HistoryLogData.h"
+
+using std::endl;
+using std::string;
+
+namespace
+{
+  inline string timestamp()
+  { return zypp::Date::now().form( HISTORY_LOG_DATE_FORMAT ); }
+
+  inline string userAtHostname()
+  {
+    static char buf[256];
+    string result;
+    char * tmp = ::cuserid(buf);
+    if (tmp)
+    {
+      result = string(tmp);
+      if (!::gethostname(buf, 255))
+        result += "@" + string(buf);
+    }
+    return result;
+  }
+
+  static std::string pidAndAppname()
+  {
+    static std::string _val;
+    if ( _val.empty() )
+    {
+      pid_t mypid = getpid();
+      zypp::Pathname p( "/proc/"+zypp::str::numstring(mypid)+"/exe" );
+      zypp::Pathname myname( zypp::filesystem::readlink( p ) );
+
+      _val += zypp::str::numstring(mypid);
+      _val += ":";
+      _val += myname.basename();
+    }
+    return _val;
+  }
+}
+
+namespace zypp
+{
+  namespace
+  {
+    const char         _sep = '|';
+    std::ofstream      _log;
+    unsigned           _refcnt = 0;
+    Pathname           _fname;
+    Pathname           _fnameLastFail;
+
+    inline void openLog()
+    {
+      if ( _fname.empty() )
+        _fname = ZConfig::instance().historyLogFile();
+
+      _log.clear();
+      _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
+      if( !_log && _fnameLastFail != _fname )
+      {
+        ERR << "Could not open logfile '" << _fname << "'" << endl;
+       _fnameLastFail = _fname;
+      }
+    }
+
+    inline void closeLog()
+    {
+      _log.clear();
+      _log.close();
+    }
+
+    inline void refUp()
+    {
+      if ( !_refcnt )
+        openLog();
+      ++_refcnt;
+    }
+
+    inline void refDown()
+    {
+      --_refcnt;
+      if ( !_refcnt )
+        closeLog();
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : HistoryLog
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  HistoryLog::HistoryLog( const Pathname & rootdir )
+  {
+    setRoot( rootdir );
+    refUp();
+  }
+
+  HistoryLog::~HistoryLog()
+  {
+    refDown();
+  }
+
+  void HistoryLog::setRoot( const Pathname & rootdir )
+  {
+    if ( ! rootdir.absolute() )
+      return;
+
+    if ( _refcnt )
+      closeLog();
+
+    _fname = rootdir / ZConfig::instance().historyLogFile();
+    filesystem::assert_dir( _fname.dirname() );
+    MIL << "installation log file " << _fname << endl;
+
+    if ( _refcnt )
+      openLog();
+  }
+
+  const Pathname & HistoryLog::fname()
+  {
+    if ( _fname.empty() )
+      _fname = ZConfig::instance().historyLogFile();
+    return _fname;
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  void HistoryLog::comment( const string & comment, bool timestamp )
+  {
+    if (comment.empty())
+      return;
+
+    _log << "# ";
+    if ( timestamp )
+      _log << ::timestamp() << " ";
+
+    const char * s = comment.c_str();
+    const char * c = s;
+    unsigned size = comment.size();
+
+    // ignore the last newline
+    if (comment[size-1] == '\n')
+      --size;
+
+    for ( unsigned i = 0; i < size; ++i, ++c )
+      if ( *c == '\n' )
+      {
+        _log << string( s, c + 1 - s ) << "# ";
+        s = c + 1;
+      }
+
+    if ( s < c )
+      _log << std::string( s, c-s );
+
+    _log << endl;
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  void HistoryLog::install( const PoolItem & pi )
+  {
+    const Package::constPtr p = asKind<Package>(pi.resolvable());
+    if (!p)
+      return;
+
+    _log
+      << timestamp()                                   // 1 timestamp
+      << _sep << HistoryActionID::INSTALL.asString(true) // 2 action
+      << _sep << p->name()                             // 3 name
+      << _sep << p->edition()                          // 4 evr
+      << _sep << p->arch();                            // 5 arch
+
+    // ApplLow is what the solver selected on behalf of the user.
+    if (pi.status().isByUser() || pi.status().isByApplLow() )
+      _log << _sep << userAtHostname();                // 6 reqested by
+    else if (pi.status().isByApplHigh())
+      _log << _sep << pidAndAppname();
+    else
+      _log << _sep;
+
+    _log
+      << _sep << p->repoInfo().alias()                 // 7 repo alias
+      << _sep << p->checksum().checksum();             // 8 checksum
+
+    _log << endl;
+
+    //_log << pi << endl;
+  }
+
+
+  void HistoryLog::remove( const PoolItem & pi )
+  {
+    const Package::constPtr p = asKind<Package>(pi.resolvable());
+    if (!p)
+      return;
+
+    _log
+      << timestamp()                                   // 1 timestamp
+      << _sep << HistoryActionID::REMOVE.asString(true) // 2 action
+      << _sep << p->name()                             // 3 name
+      << _sep << p->edition()                          // 4 evr
+      << _sep << p->arch();                            // 5 arch
+
+    // ApplLow is what the solver selected on behalf of the user.
+    if ( pi.status().isByUser() || pi.status().isByApplLow() )
+      _log << _sep << userAtHostname();                // 6 reqested by
+    else if (pi.status().isByApplHigh())
+      _log << _sep << pidAndAppname();
+    else
+      _log << _sep;
+
+    // we don't have checksum in rpm db
+    //  << _sep << p->checksum().checksum();           // x checksum
+
+    _log << endl;
+
+    //_log << pi << endl;
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  void HistoryLog::addRepository(const RepoInfo & repo)
+  {
+    _log
+      << timestamp()                                   // 1 timestamp
+      << _sep << HistoryActionID::REPO_ADD.asString(true) // 2 action
+      << _sep << str::escape(repo.alias(), _sep)       // 3 alias
+      // what about the rest of the URLs??
+      << _sep << *repo.baseUrlsBegin()                 // 4 primary URL
+      << endl;
+  }
+
+
+  void HistoryLog::removeRepository(const RepoInfo & repo)
+  {
+    _log
+      << timestamp()                                   // 1 timestamp
+      << _sep << HistoryActionID::REPO_REMOVE.asString(true) // 2 action
+      << _sep << str::escape(repo.alias(), _sep)       // 3 alias
+      << endl;
+  }
+
+
+  void HistoryLog::modifyRepository(
+      const RepoInfo & oldrepo, const RepoInfo & newrepo)
+  {
+    if (oldrepo.alias() != newrepo.alias())
+    {
+      _log
+        << timestamp()                                    // 1 timestamp
+        << _sep << HistoryActionID::REPO_CHANGE_ALIAS.asString(true) // 2 action
+        << _sep << str::escape(oldrepo.alias(), _sep)     // 3 old alias
+        << _sep << str::escape(newrepo.alias(), _sep)     // 4 new alias
+        << endl;
+    }
+
+    if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
+    {
+      _log
+        << timestamp()                                             //1 timestamp
+        << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
+        << _sep << str::escape(oldrepo.alias(), _sep)              // 3 old url
+        << _sep << *newrepo.baseUrlsBegin()                        // 4 new url
+        << endl;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+} // namespace zypp
diff --git a/zypp/HistoryLog.h b/zypp/HistoryLog.h
new file mode 100644 (file)
index 0000000..e2c0152
--- /dev/null
@@ -0,0 +1,131 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/HistoryLog.h
+ *
+ */
+#ifndef ZYPP_TARGET_COMMITLOG_H
+#define ZYPP_TARGET_COMMITLOG_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+
+namespace zypp
+{
+  class PoolItem;
+  class RepoInfo;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : HistoryLog
+  /**
+   * Simple wrapper for progress log. Refcnt, filename and corresponding
+   * ofstream are static members. Logfile constructor raises, destructor
+   * lowers refcounter. On refcounter changing from 0->1, file is opened.
+   * Changing from 1->0 the file is closed. Thus Logfile objects should be
+   * local to those functions, writing the log, and must not be stored
+   * permanently.
+   *
+   * Usage:
+   * <code>
+   *  some method ()
+   *  {
+   *    PoolItem pi;
+   *    ...
+   *    HistoryLog().install(pi);
+   *    ...
+   *    HistoryLog().comment(someMessage);
+   *  }
+   * </code>
+   *
+   * \note Take care to set proper target root dir if needed. Either pass
+   *       it via the constructor, or set it via setRoot(Pathname) method.
+   *       The default location of the file is determined by
+   *       \ref ZConfig::historyLogPath() which defaults to
+   *       /var/log/zypp/history.
+   *
+   * \see http://en.opensuse.org/Libzypp/Package_History
+   *
+   * \todo The implementation as pseudo signleton is questionable.
+   * Use shared_ptr instead of handcrafted ref/unref. Manage multiple
+   * logs at different locations.
+   */
+  class HistoryLog
+  {
+    HistoryLog( const HistoryLog & );
+    HistoryLog & operator=( const HistoryLog & );
+  public:
+    /**
+     * Constructor with an optional root directory.
+     *
+     * \param rootdir actual target root directory
+     */
+    HistoryLog( const Pathname & rootdir = Pathname() );
+    ~HistoryLog();
+
+    /**
+     * Set new root directory to the default history log file path.
+     *
+     * This path will be prepended to the default log file path. This should
+     * be done where there is a potential that the target root has changed.
+     *
+     * \param root new root directory.
+     */
+    static void setRoot( const Pathname & root );
+
+    /**
+     * Get the current log file path.
+     */
+    static const Pathname & fname();
+
+    /**
+     * Log a comment (even multiline).
+     *
+     * \param comment the comment
+     * \param timestamp whether to include a timestamp at the start of the comment
+     */
+    void comment( const std::string & comment, bool timestamp = false );
+
+    /**
+     * Log installation (or update) of a package.
+     */
+    void install( const PoolItem & pi );
+
+    /**
+     * Log removal of a package
+     */
+    void remove( const PoolItem & pi );
+
+    /**
+     * Log a newly added repository.
+     *
+     * \param repo info about the added repository
+     */
+    void addRepository( const RepoInfo & repo );
+
+    /**
+     * Log recently removed repository.
+     *
+     * \param repo info about the removed repository
+     */
+    void removeRepository( const RepoInfo & repo );
+
+    /**
+     * Log certain modifications to a repository.
+     *
+     * \param oldrepo info about the old repository
+     * \param newrepo info about the new repository
+     */
+    void modifyRepository( const RepoInfo & oldrepo, const RepoInfo & newrepo );
+  };
+  ///////////////////////////////////////////////////////////////////
+
+} // namespace zypp
+
+#endif // ZYPP_TARGET_COMMITLOG_H
diff --git a/zypp/HistoryLogData.cc b/zypp/HistoryLogData.cc
new file mode 100644 (file)
index 0000000..5a4163e
--- /dev/null
@@ -0,0 +1,331 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+/** \file zypp/HistoryLogData.cc
+ *
+ */
+#include <sstream>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/parser/ParseException.h"
+
+#include "zypp/HistoryLogData.h"
+
+using namespace std;
+
+namespace zypp
+{
+  using parser::ParseException;
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : HistoryActionID
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  static std::map<std::string,HistoryActionID::ID> _table;
+
+  const HistoryActionID HistoryActionID::NONE(HistoryActionID::NONE_e);
+  const HistoryActionID HistoryActionID::INSTALL(HistoryActionID::INSTALL_e);
+  const HistoryActionID HistoryActionID::REMOVE(HistoryActionID::REMOVE_e);
+  const HistoryActionID HistoryActionID::REPO_ADD(HistoryActionID::REPO_ADD_e);
+  const HistoryActionID HistoryActionID::REPO_REMOVE(HistoryActionID::REPO_REMOVE_e);
+  const HistoryActionID HistoryActionID::REPO_CHANGE_ALIAS(HistoryActionID::REPO_CHANGE_ALIAS_e);
+  const HistoryActionID HistoryActionID::REPO_CHANGE_URL(HistoryActionID::REPO_CHANGE_URL_e);
+
+  HistoryActionID::HistoryActionID(const std::string & strval_r)
+    : _id(parse(strval_r))
+  {}
+
+  HistoryActionID::ID HistoryActionID::parse(const std::string & strval_r)
+  {
+    if (_table.empty())
+    {
+      // initialize it
+      _table["install"] = INSTALL_e;
+      _table["remove"]  = REMOVE_e;
+      _table["radd"]    = REPO_ADD_e;
+      _table["rremove"] = REPO_REMOVE_e;
+      _table["ralias"]  = REPO_CHANGE_ALIAS_e;
+      _table["rurl"]    = REPO_CHANGE_URL_e;
+      _table["NONE"] = _table["none"] = HistoryActionID::NONE_e;
+    }
+
+    std::map<std::string,HistoryActionID::ID>::const_iterator it =
+      _table.find(strval_r);
+
+    if (it == _table.end())
+      WAR << "Unknown history action ID '" + strval_r + "'";
+
+    return it->second;
+  }
+
+
+  const std::string & HistoryActionID::asString(bool pad) const
+  {
+    static std::map<ID, std::string> _table;
+    if ( _table.empty() )
+    {
+      // initialize it
+      _table[INSTALL_e]           = "install";
+      _table[REMOVE_e]            = "remove";
+      _table[REPO_ADD_e]          = "radd";
+      _table[REPO_REMOVE_e]       = "rremove";
+      _table[REPO_CHANGE_ALIAS_e] = "ralias";
+      _table[REPO_CHANGE_URL_e]   = "rurl";
+      _table[NONE_e]              = "NONE";
+    }
+    // add spaces so that the size of the returned string is always 7 (for now)
+    if (pad)
+      return _table[_id].append(7 - _table[_id].size(), ' ');
+    return _table[_id];
+  }
+
+  std::ostream & operator << (std::ostream & str, const HistoryActionID & id)
+  { return str << id.asString(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItem
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItem::HistoryItem(FieldVector & fields)
+  {
+    if (fields.size() <= 2)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zd, expected more than %d.",
+          fields.size(), 2)));
+
+    date = Date(fields[0], HISTORY_LOG_DATE_FORMAT);
+    action = HistoryActionID(str::trim(fields[1]));
+  }
+
+  void HistoryItem::dumpTo(ostream & str) const
+  {
+    str << date.form(HISTORY_LOG_DATE_FORMAT) << "|" << action.asString();
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItem & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemInstall
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemInstall::HistoryItemInstall(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 8)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 8)));
+
+    name      = fields[2];
+    edition   = Edition(fields[3]);
+    arch      = Arch(fields[4]);
+    reqby     = fields[5];
+    repoalias = fields[6];
+    checksum  = CheckSum::sha(fields[7]);
+  }
+
+  void HistoryItemInstall::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|"
+      << name << "|"
+      << edition << "|"
+      << arch << "|"
+      << reqby << "|"
+      << repoalias << "|"
+      << checksum;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemInstall & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRemove
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemRemove::HistoryItemRemove(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 6)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 6)));
+
+    name      = fields[2];
+    edition   = Edition(fields[3]);
+    arch      = Arch(fields[4]);
+    reqby     = fields[5];
+  }
+
+  void HistoryItemRemove::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|"
+      << name << "|"
+      << edition << "|"
+      << arch << "|"
+      << reqby;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemRemove & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoAdd
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemRepoAdd::HistoryItemRepoAdd(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 4)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 4)));
+
+    alias = fields[2];
+    url = Url(fields[3]);
+  }
+
+  void HistoryItemRepoAdd::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|"
+      << alias << "|"
+      << url;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemRepoAdd & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoRemove
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemRepoRemove::HistoryItemRepoRemove(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 3)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 3)));
+
+    alias = fields[2];
+  }
+
+  void HistoryItemRepoRemove::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|" << alias;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemRepoRemove & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoAliasChange
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemRepoAliasChange::HistoryItemRepoAliasChange(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 4)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 4)));
+
+    oldalias = fields[2];
+    newalias = fields[3];
+  }
+
+  void HistoryItemRepoAliasChange::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|" << oldalias << "|" << newalias;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemRepoAliasChange & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoUrlChange
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryItemRepoUrlChange::HistoryItemRepoUrlChange(FieldVector & fields)
+    : HistoryItem(fields)
+  {
+    if (fields.size() != 4)
+      ZYPP_THROW(ParseException(
+        str::form("Bad number of fields. Got %zu, expected %u.",
+          fields.size(), 4)));
+
+    alias = fields[2];
+    newurl = Url(fields[3]);
+  }
+
+  void HistoryItemRepoUrlChange::dumpTo(ostream & str) const
+  {
+    HistoryItem::dumpTo(str);
+    str << "|" << alias << "|" << newurl;
+  }
+
+  ostream & operator<<(ostream & str, const HistoryItemRepoUrlChange & obj)
+  {
+    obj.dumpTo(str);
+    return str;
+  }
+
+
+}
diff --git a/zypp/HistoryLogData.h b/zypp/HistoryLogData.h
new file mode 100644 (file)
index 0000000..c542ef4
--- /dev/null
@@ -0,0 +1,247 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+/** \file zypp/HistoryLogData.h
+ *
+ */
+#ifndef ZYPP_HISTORYLOGDATA_H_
+#define ZYPP_HISTORYLOGDATA_H_
+
+#include <iosfwd>
+
+#include "zypp/Date.h"
+#include "zypp/Edition.h"
+#include "zypp/Arch.h"
+#include "zypp/CheckSum.h"
+#include "zypp/Url.h"
+
+#define HISTORY_LOG_DATE_FORMAT "%Y-%m-%d %H:%M:%S"
+
+namespace zypp
+{
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : HistoryActionID
+  //
+  /**
+   * Enumeration of known history actions.
+   *
+   * \ingroup g_EnumerationClass
+   */
+  struct HistoryActionID
+  {
+    static const HistoryActionID NONE;
+
+    static const HistoryActionID INSTALL;
+    static const HistoryActionID REMOVE;
+    static const HistoryActionID REPO_ADD;
+    static const HistoryActionID REPO_REMOVE;
+    static const HistoryActionID REPO_CHANGE_ALIAS;
+    static const HistoryActionID REPO_CHANGE_URL;
+
+    enum ID
+    {
+      NONE_e,
+
+      INSTALL_e,
+      REMOVE_e,
+      REPO_ADD_e,
+      REPO_REMOVE_e,
+      REPO_CHANGE_ALIAS_e,
+      REPO_CHANGE_URL_e
+    };
+
+    HistoryActionID() : _id(NONE_e) {}
+
+    HistoryActionID(ID id) : _id(id) {}
+
+    explicit HistoryActionID(const std::string & strval_r);
+
+    ID toEnum() const { return _id; }
+
+    static HistoryActionID::ID parse(const std::string & strval_r);
+
+    const std::string & asString(bool pad = false) const;
+
+    private:
+    ID _id;
+  };
+
+  /** \relates HistoryActionID */
+  std::ostream & operator << (std::ostream & str, const HistoryActionID & id);
+  ///////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItem
+  //
+  class HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItem> Ptr;
+    typedef std::vector<std::string> FieldVector;
+
+  public:
+    HistoryItem(FieldVector & fields);
+    virtual ~HistoryItem()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    Date date;
+    HistoryActionID action;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemInstall
+  //
+  class HistoryItemInstall : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemInstall> Ptr;
+
+    HistoryItemInstall(FieldVector & fields);
+    virtual ~HistoryItemInstall()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    std::string   name;
+    Edition       edition;
+    Arch          arch;
+    std::string   reqby; // TODO make this a class ReqBy
+    std::string   repoalias;
+    CheckSum      checksum;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRemove
+  //
+  class HistoryItemRemove : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemRemove> Ptr;
+
+    HistoryItemRemove(FieldVector & fields);
+    virtual ~HistoryItemRemove()
+    {}
+
+    virtual void dumpTo(std::ostream & str)  const;
+
+  public:
+    std::string name;
+    Edition     edition;
+    Arch        arch;
+    std::string reqby;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoAdd
+  //
+  class HistoryItemRepoAdd : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemRepoAdd> Ptr;
+
+    HistoryItemRepoAdd(FieldVector & fields);
+    virtual ~HistoryItemRepoAdd()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    std::string alias;
+    Url         url;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoRemove
+  //
+  class HistoryItemRepoRemove : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemRepoRemove> Ptr;
+
+    HistoryItemRepoRemove(FieldVector & fields);
+    virtual ~HistoryItemRepoRemove()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    std::string alias;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoAliasChange
+  //
+  class HistoryItemRepoAliasChange : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemRepoAliasChange> Ptr;
+
+    HistoryItemRepoAliasChange(FieldVector & fields);
+    virtual ~HistoryItemRepoAliasChange()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    std::string oldalias;
+    std::string newalias;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryItemRepoUrlChange
+  //
+  class HistoryItemRepoUrlChange : public HistoryItem
+  {
+  public:
+    typedef shared_ptr<HistoryItemRepoUrlChange> Ptr;
+
+    HistoryItemRepoUrlChange(FieldVector & fields);
+    virtual ~HistoryItemRepoUrlChange()
+    {}
+
+    virtual void dumpTo(std::ostream & str) const;
+
+  public:
+    std::string alias;
+    Url newurl;
+  };
+  /////////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<(std::ostream & str, const HistoryItem & obj);
+
+}
+
+#endif /* ZYPP_HISTORYLOGDATA_H_ */
diff --git a/zypp/IdString.cc b/zypp/IdString.cc
new file mode 100644 (file)
index 0000000..31f48ef
--- /dev/null
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/IdString.cc
+ *
+*/
+#include <iostream>
+#include <boost/mpl/int.hpp>
+
+#include "zypp/IdString.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Pool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const IdString IdString::Null ( sat::detail::noId );
+  const IdString IdString::Empty( sat::detail::emptyId );
+
+  /////////////////////////////////////////////////////////////////
+
+  IdString::IdString( const char * str_r )
+  : _id( ::str2id( myPool().getPool(), str_r, /*create*/true ) )
+  {}
+
+  IdString::IdString( const std::string & str_r )
+  : _id( ::str2id( myPool().getPool(), str_r.c_str(), /*create*/true ) )
+  {}
+
+  unsigned IdString::size() const
+  { return ::strlen( c_str() ); }
+
+  const char * IdString::c_str() const
+  { return _id ? ::id2str( myPool().getPool(), _id ) : ""; }
+
+  int IdString::compare( const IdString & rhs ) const
+  {
+    if ( _id == rhs._id )
+      return 0;
+    // Explicitly handle IdString::Null < ""
+    if ( ! _id )
+      return -1;
+    if ( ! rhs._id )
+      return 1;
+    return ::strcmp( c_str(), rhs.c_str() );
+  }
+
+  int IdString::compare( const char * rhs ) const
+  {
+    // Explicitly handle IdString::Null == (const char *)0
+    if ( ! _id )
+      return rhs ? -1 : 0;
+    if ( ! rhs )
+      return _id ? 1 : 0;
+    return ::strcmp( c_str(), rhs );
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const IdString & obj )
+  {
+    return str << obj.c_str();
+  }
+
+  std::ostream & dumpOn( std::ostream & str, const IdString & obj )
+  {
+    return str << '(' << obj.id() << ')' << obj.c_str();
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/IdString.h b/zypp/IdString.h
new file mode 100644 (file)
index 0000000..42c1fcc
--- /dev/null
@@ -0,0 +1,226 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/IdString.h
+ *
+*/
+#ifndef ZYPP_SAT_IDSTR_H
+#define ZYPP_SAT_IDSTR_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/SafeBool.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class IdString;
+  typedef std::tr1::unordered_set<IdString> IdStringSet;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : IdString
+  //
+  /** Access to the sat-pools string space.
+   *
+   * Construction from string will place a copy of the string in the
+   * string space, if it is not already present.
+   *
+   * While comparison differs between \ref IdString::Null and \ref IdString::Empty
+   * ( \c NULL and \c "" ), both are represented by an empty string \c "".
+   */
+  class IdString : protected sat::detail::PoolMember,
+                   private base::SafeBool<IdString>
+  {
+    public:
+      typedef sat::detail::IdType IdType;
+
+    public:
+      /** Default ctor, empty string. */
+      IdString() : _id( sat::detail::emptyId ) {}
+
+      /** Ctor from id. */
+      explicit IdString( IdType id_r ) : _id( id_r ) {}
+
+      /** Ctor from string. */
+      explicit IdString( const char * str_r );
+
+      /** Ctor from string. */
+      explicit IdString( const std::string & str_r );
+
+    public:
+      /** No or Null string ( Id \c 0 ). */
+      static const IdString Null;
+
+      /** Empty string. */
+      static const IdString Empty;
+
+    public:
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( != \c Null )</tt>. */
+      using base::SafeBool<IdString>::operator bool_type;
+#endif
+      /** Whether the string is empty.
+       * This is true for \ref Null and \ref Empty.
+       */
+      bool empty() const
+      { return( _id == sat::detail::emptyId || _id == sat::detail::noId ); }
+
+      /** The strings size. */
+      unsigned size() const;
+
+    public:
+      /** Conversion to <tt>const char *</tt> */
+      const char * c_str() const;
+
+      /** Conversion to <tt>std::string</tt> */
+      std::string asString() const
+      { return c_str(); }
+
+    public:
+      /** Fast compare equal. */
+      bool compareEQ( const IdString & rhs ) const
+      { return( _id == rhs.id() ); }
+
+      /** Compare IdString returning <tt>-1,0,1</tt>. */
+      int compare( const IdString & rhs ) const;
+
+      /** \overload */
+      int compare( const char * rhs ) const;
+
+      /** \overload */
+      int compare( const std::string & rhs ) const
+      { return compare( rhs.c_str() ); }
+
+    public:
+      /** Expert backdoor. */
+      IdType id() const
+      { return _id; }
+    private:
+#ifndef SWIG // Swig treats it as syntax error
+      friend base::SafeBool<IdString>::operator bool_type() const;
+#endif
+      bool boolTest() const { return _id; }
+    private:
+      IdType _id;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates IdString Stream output */
+  std::ostream & operator<<( std::ostream & str, const IdString & obj );
+
+  /** \relates IdString Stream output */
+  std::ostream & dumpOn( std::ostream & str, const IdString & obj );
+
+  /** \relates IdString Equal */
+  inline bool operator==( const IdString & lhs, const IdString & rhs )
+  { return lhs.compareEQ( rhs ); }
+  /** \overload */
+  inline bool operator==( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  inline bool operator==( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  inline bool operator==( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) == 0; }
+  /** \overload */
+  inline bool operator==( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) == 0; }
+
+  /** \relates IdString NotEqual */
+  inline bool operator!=( const IdString & lhs, const IdString & rhs )
+  { return ! lhs.compareEQ( rhs ); }
+  /** \overload */
+  inline bool operator!=( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  inline bool operator!=( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  inline bool operator!=( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) != 0; }
+  /** \overload */
+  inline bool operator!=( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) != 0; }
+
+  /** \relates IdString Less */
+  inline bool operator<( const IdString & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  inline bool operator<( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  inline bool operator<( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  inline bool operator<( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) >= 0; }
+  /** \overload */
+  inline bool operator<( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) >= 0; }
+
+  /** \relates IdString LessEqual*/
+  inline bool operator<=( const IdString & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  inline bool operator<=( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  inline bool operator<=( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  inline bool operator<=( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) > 0; }
+  /** \overload */
+  inline bool operator<=( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) > 0; }
+
+  /** \relates IdString Greater */
+  inline bool operator>( const IdString & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  inline bool operator>( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  inline bool operator>( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  inline bool operator>( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) <= 0; }
+  /** \overload */
+  inline bool operator>( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) <= 0; }
+
+  /** \relates IdString GreaterEqual */
+  inline bool operator>=( const IdString & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  inline bool operator>=( const IdString & lhs, const char * rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  inline bool operator>=( const IdString & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  inline bool operator>=( const char * lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) < 0; }
+  /** \overload */
+  inline bool operator>=( const std::string & lhs, const IdString & rhs )
+  { return rhs.compare( lhs ) < 0; }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+ZYPP_DEFINE_ID_HASHABLE( ::zypp::IdString );
+
+#endif // ZYPP_SAT_IDSTR_H
diff --git a/zypp/IdStringType.h b/zypp/IdStringType.h
new file mode 100644 (file)
index 0000000..f75cbbf
--- /dev/null
@@ -0,0 +1,346 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/IdStringType.h
+ *
+*/
+#ifndef ZYPP_IDSTRINGTYPE_H
+#define ZYPP_IDSTRINGTYPE_H
+
+#include "zypp/IdString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : IdStringType<Derived>
+  //
+  /** Base class for creating \ref IdString based types.
+   *
+   * Just by deriving from \ref IdStringType a class provides all
+   * the operations an \ref IdString does. (incl. conversion to string types,
+   * comparison of string types and stream output).
+   *
+   * To disable any comparison, declare (but do not define) \ref _doCompare
+   * in your class.
+   * \code
+   * class NoCompare : public IdStringType<NoCompare>
+   * {
+   *   private:
+   *   static int _doCompare( const char * lhs,  const char * rhs );
+   *
+   * };
+   * \endcode
+   *
+   * If you need a different than the default lexicographical
+   * order, write your own \ref _doCompare.
+   *
+   * \code
+   *    // uses default lexicographical order
+   *    class CaseCmp : public IdStringType<CaseCmp>
+   *    {
+   *      public:
+   *        CaseCmp() {}
+   *        explicit CaseCmp( const char * cstr_r ) : _str( cstr_r )  {}
+   *      private:
+   *        friend class IdStringType<CaseCmp>;
+   *        IdString _str;
+   *    };
+   *
+   *    // uses case insensitive comparison order
+   *    class NoCaseCmp : public IdStringType<NoCaseCmp>
+   *    {
+   *      public:
+   *        NoCaseCmp() {}
+   *        explicit NoCaseCmp( const char * cstr_r ) : _str( cstr_r )  {}
+   *      private:
+   *        static int _doCompare( const char * lhs,  const char * rhs )
+   *        {
+   *          if ( lhs == rhs ) return 0;
+   *          if ( lhs && rhs ) return ::strcasecmp( lhs, rhs );
+   *          return( lhs ? 1 : -1 );
+   *        }
+   *      private:
+   *        friend class IdStringType<NoCaseCmp>;
+   *        IdString _str;
+   *    };
+   *
+   *    CaseCmp   ca( "a" );
+   *    NoCaseCmp na( "a" );
+   *    DBG << "ca == a ? " << (ca == "a") << endl;   // ca == a ? 1
+   *    DBG << "ca == A ? " << (ca == "A") << endl;   // ca == A ? 0
+   *    DBG << "na == a ? " << (na == "a") << endl;   // na == a ? 1
+   *    DBG << "na == A ? " << (na == "A") << endl;   // na == A ? 1
+   * \endcode
+   * \todo allow redefinition of order vis _doCompare not only for char* but on any level
+   * \ingroup g_CRTP
+   */
+  template <class Derived>
+  class IdStringType : protected sat::detail::PoolMember,
+                       private base::SafeBool<Derived>
+  {
+    typedef typename base::SafeBool<Derived>::bool_type bool_type;
+    public:
+      typedef IdString::IdType IdType;
+
+    protected:
+      IdStringType() {}
+      IdStringType(const IdStringType &) {}
+      void operator=(const IdStringType &) {}
+      ~IdStringType() {}
+
+    private:
+      const Derived & self() const { return *static_cast<const Derived*>( this ); }
+
+    public:
+      const IdString & idStr()    const { return self()._str; }
+
+      bool          empty()       const { return idStr().empty(); }
+      unsigned      size()        const { return idStr().size(); }
+      const char *  c_str()       const { return idStr().c_str(); }
+      std::string   asString()    const { return idStr().asString(); }
+
+      IdType        id()          const { return idStr().id(); }
+
+    public:
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( ! empty() )</tt>. */
+      using base::SafeBool<Derived>::operator bool_type;
+#endif
+    public:
+      // - break it down to idString/const char* <=> idString/cont char*
+      // - handle idString(0)/NULL being the least value
+      // - everything else goes to _doCompare (no NULL)
+      static int compare( const Derived & lhs,     const Derived & rhs )     { return compare( lhs.idStr(), rhs.idStr() ); }
+      static int compare( const Derived & lhs,     const IdString & rhs )    { return compare( lhs.idStr(), rhs ); }
+      static int compare( const Derived & lhs,     const std::string & rhs ) { return compare( lhs.idStr(), rhs.c_str() ); }
+      static int compare( const Derived & lhs,     const char * rhs )        { return compare( lhs.idStr(), rhs );}
+
+      static int compare( const IdString & lhs,    const Derived & rhs )     { return compare( lhs, rhs.idStr() ); }
+      static int compare( const IdString & lhs,    const IdString & rhs )    { return lhs == rhs ? 0 : Derived::_doCompare( (lhs ? lhs.c_str() : (const char *)0 ),
+                                                                                                                           (rhs ? rhs.c_str() : (const char *)0 ) ); }
+      static int compare( const IdString & lhs,    const std::string & rhs ) { return compare( lhs, rhs.c_str() ); }
+      static int compare( const IdString & lhs,    const char * rhs )        { return Derived::_doCompare( (lhs ? lhs.c_str() : (const char *)0 ), rhs ); }
+
+      static int compare( const std::string & lhs, const Derived & rhs )     { return compare( lhs.c_str(), rhs.idStr() ); }
+      static int compare( const std::string & lhs, const IdString & rhs )    { return compare( lhs.c_str(), rhs ); }
+      static int compare( const std::string & lhs, const std::string & rhs ) { return compare( lhs.c_str(), rhs.c_str() ); }
+      static int compare( const std::string & lhs, const char * rhs )        { return compare( lhs.c_str(), rhs ); }
+
+      static int compare( const char * lhs,        const Derived & rhs )     { return compare( lhs, rhs.idStr() ); }
+      static int compare( const char * lhs,        const IdString & rhs )    { return Derived::_doCompare( lhs, (rhs ? rhs.c_str() : (const char *)0 ) ); }
+      static int compare( const char * lhs,        const std::string & rhs ) { return compare( lhs, rhs.c_str() ); }
+      static int compare( const char * lhs,        const char * rhs )        { return Derived::_doCompare( lhs, rhs ); }
+
+    public:
+      int compare( const Derived & rhs )      const { return compare( idStr(), rhs.idStr() ); }
+      int compare( const IdStringType & rhs ) const { return compare( idStr(), rhs.idStr() ); }
+      int compare( const IdString & rhs )     const { return compare( idStr(), rhs ); }
+      int compare( const std::string & rhs )  const { return compare( idStr(), rhs.c_str() ); }
+      int compare( const char * rhs )         const { return compare( idStr(), rhs ); }
+
+    private:
+      static int _doCompare( const char * lhs,  const char * rhs )
+      {
+       if ( ! lhs ) return rhs ? -1 : 0;
+       return rhs ? ::strcmp( lhs, rhs ) : 1;
+      }
+
+    private:
+#ifndef SWIG // Swig treats it as syntax error
+      friend base::SafeBool<Derived>::operator bool_type() const;
+#endif
+      bool boolTest() const { return ! empty(); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates IdStringType Stream output */
+  template <class Derived>
+  inline std::ostream & operator<<( std::ostream & str, const IdStringType<Derived> & obj )
+  { return str << obj.c_str(); }
+
+  /** \relates IdStringType Equal */
+  template <class Derived>
+  inline bool operator==( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) == 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator==( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) == 0; }
+
+  /** \relates IdStringType NotEqual */
+  template <class Derived>
+  inline bool operator!=( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) != 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator!=( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) != 0; }
+
+  /** \relates IdStringType Less */
+  template <class Derived>
+  inline bool operator<( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) >= 0; }
+
+  /** \relates IdStringType LessEqual */
+  template <class Derived>
+  inline bool operator<=( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator<=( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) > 0; }
+
+  /** \relates IdStringType Greater */
+  template <class Derived>
+  inline bool operator>( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) > 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) <= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) <= 0; }
+
+  /** \relates IdStringType GreaterEqual */
+  template <class Derived>
+  inline bool operator>=( const IdStringType<Derived> & lhs, const IdStringType<Derived> & rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const IdStringType<Derived> & lhs, const IdString & rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const IdStringType<Derived> & lhs, const char * rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const IdStringType<Derived> & lhs, const std::string & rhs )
+  { return lhs.compare( rhs ) >= 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const IdString & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const char * lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) < 0; }
+  /** \overload */
+  template <class Derived>
+  inline bool operator>=( const std::string & lhs, const IdStringType<Derived> & rhs )
+  { return rhs.compare( lhs ) < 0; }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_IDSTRINGTYPE_H
diff --git a/zypp/InstanceId.cc b/zypp/InstanceId.cc
new file mode 100644 (file)
index 0000000..348b0de
--- /dev/null
@@ -0,0 +1,114 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/InstanceId.cc
+ *
+*/
+//#include <iostream>
+//#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+#include "zypp/InstanceId.h"
+#include "zypp/ResPool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  std::string InstanceId::getIdFor( sat::Solvable slv_r ) const
+  {
+    if ( ! slv_r )
+      return std::string();
+
+    std::string ret( _namespace );
+    if ( ! ret.empty() )
+      ret += ':';
+
+    if ( slv_r.isKind<SrcPackage>() ) // satsolver uses no namespace in SrcPackage ident!
+    {
+      ret += ResKind::srcpackage.c_str();
+      ret += ':';
+    }
+
+    ret += str::form( "%s-%s-%s.%s@%s",
+                      slv_r.ident().c_str(),
+                      slv_r.edition().version().c_str(),
+                      slv_r.edition().release().c_str(),
+                      slv_r.arch().c_str(),
+                      slv_r.repository().alias().c_str() );
+    return ret;
+  }
+
+  PoolItem InstanceId::findPoolItem( const std::string str_r ) const
+  {
+    // [namespace:]<name>-<version>-<release>.<arch>@<repoalias>
+    std::string::size_type namespaceOff( _namespace.size() );
+
+    if ( namespaceOff )
+    {
+      if ( ! str::hasPrefix( str_r, _namespace ) || str_r[namespaceOff] != ':' )
+        return PoolItem();
+      ++namespaceOff; // for the ':'
+    }
+
+    // check repo
+    std::string::size_type rdelim( str_r.find( "@" ) );
+    if ( rdelim == std::string::npos )
+      return PoolItem();
+
+    Repository repo( sat::Pool::instance().reposFind( str_r.substr( rdelim+1) ) );
+    if ( ! repo )
+      return PoolItem();
+
+    // check n-v-r.a from behind
+    std::string::size_type delim = str_r.rfind( ".", rdelim );
+    if ( delim == std::string::npos )
+      return PoolItem();
+
+    Arch arch( str_r.substr( delim+1, rdelim-delim-1 ) );
+
+    // v-r starts at one but last '-'
+    rdelim = delim;
+    delim = str_r.rfind( "-", rdelim );
+    if ( delim == std::string::npos )
+      return PoolItem();
+
+    if ( delim == rdelim-1 ) // supress an empty release
+      rdelim = delim;
+
+    delim = str_r.rfind( "-", delim-1 );
+    if ( delim == std::string::npos )
+      return PoolItem();
+
+    Edition ed( str_r.substr( delim+1, rdelim-delim-1 ) );
+
+    // eveythig before is name (except the leading "<namespace>:")
+    std::string identstring( str_r.substr( namespaceOff, delim-namespaceOff ) );
+
+    // now lookup in pool..
+    sat::Solvable::SplitIdent ident( (IdString(identstring)) );
+    ResPool pool( ResPool::instance() );
+    for_( it, pool.byIdentBegin( ident.kind(), ident.name() ), pool.byIdentEnd( ident.kind(), ident.name() ) )
+    {
+      sat::Solvable solv( (*it).satSolvable() );
+      if ( solv.repository() == repo && solv.arch() == arch && solv.edition() == ed )
+      {
+        return *it;
+      }
+    }
+    return PoolItem();
+  }
+
+  bool InstanceId::isSystemId( const std::string str_r ) const
+  { return str::hasSuffix( str_r, Repository::systemRepoAlias() ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/InstanceId.h b/zypp/InstanceId.h
new file mode 100644 (file)
index 0000000..c7b745c
--- /dev/null
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/InstanceId.h
+ *
+*/
+#ifndef ZYPP_INSTANCEID_H
+#define ZYPP_INSTANCEID_H
+
+#include <string>
+
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : InstanceId
+  //
+  /**
+   * Build string to identify/retrieve a specific \a Solvable.
+   *
+   * <tt>"[<namespace>:]<name>-<version>-<release>.<arch>@<repoalias>"</tt>
+   *
+   * Any namespace that prepends the InstanceIds must be
+   * passed to the ctor. Conversion to/from instanceId can
+   * be done via function call \c operator().
+   *
+   * \code
+   *   InstanceId instanceId( "SUSE:" ); // using a namespace
+   *
+   *   ResPool pool( ResPool::instance() );
+   *   for_( it, pool.begin(), pool.end() )
+   *   {
+   *     std::cout << instanceId(*it) << endl;
+   *   }
+   * \endcode
+   */
+  class InstanceId
+  {
+    public:
+      /** Default ctor empty empty namespace */
+      InstanceId()
+      {}
+
+      /** Ctor taking namespace */
+      InstanceId( const std::string & namespace_r )
+      : _namespace( namespace_r )
+      {}
+
+    public:
+      /** \ref Solvable to \ref InstanceId string. */
+      std::string getIdFor( sat::Solvable slv_r ) const;
+      /** \ref PoolItem to \ref InstanceId string. */
+      std::string getIdFor( const PoolItem & pi_r ) const
+      { return getIdFor( pi_r.satSolvable() ); }
+
+      /** \ref InstanceId string to \ref Solvable. */
+      sat::Solvable findSolvable( const std::string str_r ) const
+      { return findPoolItem( str_r ).satSolvable(); }
+      /** \ref InstanceId string to \ref PoolItem. */
+      PoolItem findPoolItem( const std::string str_r ) const;
+
+    public:
+      /** \ref Solvable to \ref InstanceId string. */
+      std::string operator()( sat::Solvable slv_r ) const
+      { return getIdFor( slv_r ); }
+
+      /** \ref PoolItem to \ref InstanceId string. */
+      std::string operator()( const PoolItem & pi_r ) const
+      { return getIdFor( pi_r ); }
+
+      /** \ref InstanceId string to \ref PoolItem. */
+      PoolItem operator()( const std::string str_r ) const
+      { return findPoolItem( str_r ); }
+
+      /** Quick test whether the InstanceId string would refer
+       * to a system (installed) Solvable. */
+      bool isSystemId( const std::string str_r ) const;
+
+    public:
+      /** The namespace in use. */
+      const std::string & getNamespace() const
+      { return _namespace; }
+
+      /** Set a new namespace. */
+      void setNamespace( const std::string & namespace_r )
+      { _namespace = namespace_r; }
+
+      /** Set no (empty) namespace. */
+      void unsetNamespace()
+      { _namespace.clear(); }
+
+   private:
+      std::string _namespace;
+  };
+  /////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_INSTANCEID_H
diff --git a/zypp/KVMap.h b/zypp/KVMap.h
new file mode 100644 (file)
index 0000000..4846e9b
--- /dev/null
@@ -0,0 +1,207 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                               core system                            |
+|                                                    (C) SuSE Linux AG |
+\----------------------------------------------------------------------/
+
+  File:       KVMap.h
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose: Convenience stuff for handling (key,value) pairs
+
+/-*/
+#ifndef KVMap_h
+#define KVMap_h
+
+#include <iosfwd>
+#include <vector>
+#include <map>
+
+#include "zypp/base/String.h"
+
+namespace zypp {
+  namespace kvmap {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : KVMapBase::KVMapPolicy
+    /**
+     * @short KVMapPolicy for conversion of KVMaps to/from string.
+     *
+     * <b>_kvsplit</b>: The string separating key from value
+     *
+     * <b>_fsplit</b>:  (key,value) pairs are separated by any nonempty
+     * sequence of characers occurring in _fsplit
+     *
+     * <b>_kvjoin</b>: The string used to join key and value.
+     *
+     * <b>_fjoin</b>: The string used to separate (key,value) pairs.
+     *
+     * TODO: Maybe options for exact _fsplit handling and timming of values.
+     *
+     **/
+    struct KVMapPolicy {
+      std::string _kvsplit;
+      std::string _fsplit;
+      std::string _kvjoin;
+      std::string _fjoin;
+      KVMapPolicy( const std::string & kvsplit_r, const std::string & fsplit_r )
+        : _kvsplit( kvsplit_r )
+        , _fsplit ( fsplit_r )
+        , _kvjoin ( _kvsplit )
+        , _fjoin  ( _fsplit )
+      {}
+      KVMapPolicy( const std::string & kvsplit_r, const std::string & fsplit_r,
+            const std::string & kvjoin_r )
+        : _kvsplit( kvsplit_r )
+        , _fsplit ( fsplit_r )
+        , _kvjoin ( kvjoin_r )
+        , _fjoin  ( _fsplit )
+      {}
+      KVMapPolicy( const std::string & kvsplit_r, const std::string & fsplit_r,
+            const std::string & kvjoin_r, const std::string & fjoin_r )
+        : _kvsplit( kvsplit_r )
+        , _fsplit ( fsplit_r )
+        , _kvjoin ( kvjoin_r )
+        , _fjoin  ( fjoin_r )
+      {}
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : KVMapBase
+    /**
+     * @short Base class for KVMaps, (key,value) pairs
+     **/
+    struct KVMapBase : public std::map<std::string,std::string> {
+    
+      /**
+       * (key,value) map type
+       **/
+      typedef std::map<std::string,std::string> map_type;
+    
+      KVMapBase()
+      {}
+      KVMapBase( const map_type & kvmap_r )
+        : std::map<std::string,std::string>( kvmap_r )
+      {}
+    
+      /**
+       * Test whether key is set.
+       **/
+      bool has( const std::string & key_r ) const {
+        return( find( key_r ) != end() );
+      }
+    
+      /**
+       * @short KVMapPolicy for KVMaps using a single char as separator (e.g. mount options).
+       **/
+      template<char kv, char f>
+      struct CharSep : public KVMapPolicy { CharSep() : KVMapPolicy( std::string(1,kv), std::string(1,f) ) {} };
+    
+      ///////////////////////////////////////////////////////////////////
+    
+      /**
+       * Split str_r into (key,value) map, using the separators defined
+       * by opts_r.
+       **/
+      static map_type split( const std::string & str_r,
+                        const KVMapPolicy & opts_r ) {
+        map_type ret;
+        std::vector<std::string> fields;
+        str::split( str_r, std::back_inserter(fields), opts_r._fsplit );
+    
+        for ( unsigned i = 0; i < fields.size(); ++i ) {
+          std::string::size_type pos = fields[i].find( opts_r._kvsplit );
+          if ( pos == std::string::npos ) {
+       ret[fields[i]] = "";
+          } else {
+       ret[fields[i].substr( 0, pos )] = fields[i].substr( pos+1 );
+          }
+        }
+    
+        return ret;
+      }
+    
+      /**
+       * Join (key,value) map into string, using the separators defined
+       * by opts_r.
+       **/
+      static std::string join( const map_type & kvmap_r,
+                          const KVMapPolicy & opts_r ) {
+        std::string ret;
+    
+        for ( map_type::const_iterator it = kvmap_r.begin(); it != kvmap_r.end(); ++it ) {
+          if ( ! ret.empty() ) {
+       ret += opts_r._fjoin;
+          }
+          ret += it->first;
+          if ( !it->second.empty() ) {
+       ret += opts_r._kvjoin + it->second;
+          }
+        }
+    
+        return ret;
+      }
+    
+    };
+
+
+
+  } // namespace kvmap
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : KVMap<KVMapOpts>
+  /**
+   * @short A map of (key,value) strings.
+   *
+   * The template argument defines the @ref kvmap::Options for
+   * split and join.
+   *
+   * E.g. mount options (a comma separated list of key[=value] pairs)
+   * could be handled by KVMap<kvmap::KVMapBase::Comma>.
+   **/
+  template<typename KVMapOpts>
+  struct KVMap : public kvmap::KVMapBase {
+  
+    KVMap()
+    {}
+    KVMap( const char * str_r )
+      : kvmap::KVMapBase( split( (str_r?str_r:""), KVMapOpts() ) )
+    {}
+    KVMap( const std::string & str_r )
+      : kvmap::KVMapBase( split( str_r, KVMapOpts() ) )
+    {}
+    KVMap( const map_type & map_r )
+      : kvmap::KVMapBase( map_r )
+    {}
+  
+    ~KVMap() {}
+  
+    std::string asString() const {
+      return join( *this, KVMapOpts() );
+    }
+  
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  template<typename KVMapOpts>
+  std::ostream & operator<<( std::ostream & str, const KVMap<KVMapOpts> & obj )
+  { return str << obj.asString(); }
+
+///////////////////////////////////////////////////////////////////
+} // namespace zypp
+
+#endif // KVMap_h
diff --git a/zypp/KeyContext.h b/zypp/KeyContext.h
new file mode 100644 (file)
index 0000000..e0c6332
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef KEYCONTEXT_H_
+#define KEYCONTEXT_H_
+
+#include "zypp/RepoInfo.h"
+
+namespace zypp {
+
+  struct KeyContext
+  {
+  public:
+    /** Is the context unknown? */
+    bool empty() const { return _repoInfo.alias().empty(); }
+    
+  public:
+    const RepoInfo repoInfo() const { return _repoInfo; }
+    void setRepoInfo(const RepoInfo & repoinfo) { _repoInfo = repoinfo; }
+  
+  private:
+    RepoInfo _repoInfo;
+  };
+
+}
+
+#endif /*KEYCONTEXT_H_*/
diff --git a/zypp/KeyRing.cc b/zypp/KeyRing.cc
new file mode 100644 (file)
index 0000000..e4f99b4
--- /dev/null
@@ -0,0 +1,799 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/KeyRing.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <sys/file.h>
+#include <cstdio>
+#include <unistd.h>
+
+#include <boost/format.hpp>
+
+#include "zypp/TmpPath.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZYpp.h"
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/PathInfo.h"
+#include "zypp/KeyRing.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/TmpPath.h"
+
+using namespace std;
+using namespace zypp::filesystem;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
+
+#define GPG_BINARY "/usr/bin/gpg2"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(KeyRing);
+
+  namespace
+  {
+    KeyRing::DefaultAccept _keyRingDefaultAccept( KeyRing::ACCEPT_NOTHING );
+  }
+
+  KeyRing::DefaultAccept KeyRing::defaultAccept()
+  { return _keyRingDefaultAccept; }
+
+  void KeyRing::setDefaultAccept( DefaultAccept value_r )
+  {
+    MIL << "Set new KeyRing::DefaultAccept: " << value_r << endl;
+    _keyRingDefaultAccept = value_r;
+  }
+
+  bool KeyRingReport::askUserToAcceptUnsignedFile( const string &file, const KeyContext &keycontext )
+  { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNSIGNED_FILE ); }
+
+  KeyRingReport::KeyTrust
+  KeyRingReport::askUserToAcceptKey( const PublicKey &key, const KeyContext &keycontext )
+  {
+    if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_KEY_TEMPORARILY ) )
+      return KEY_TRUST_TEMPORARILY;
+    if ( _keyRingDefaultAccept.testFlag( KeyRing::TRUST_AND_IMPORT_KEY ) )
+      return KEY_TRUST_AND_IMPORT;
+    return KEY_DONT_TRUST;
+  }
+
+  bool KeyRingReport::askUserToAcceptUnknownKey( const string &file, const string &id, const KeyContext &keycontext )
+  { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_UNKNOWNKEY ); }
+
+  bool KeyRingReport::askUserToAcceptVerificationFailed( const string &file, const PublicKey &key, const KeyContext &keycontext )
+  { return _keyRingDefaultAccept.testFlag( KeyRing::ACCEPT_VERIFICATION_FAILED ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : KeyRing::Impl
+  //
+  /** KeyRing implementation. */
+  struct KeyRing::Impl
+  {
+    Impl( const Pathname & baseTmpDir )
+    : _trusted_tmp_dir( baseTmpDir, "zypp-trusted-kr" )
+    , _general_tmp_dir( baseTmpDir, "zypp-general-kr" )
+    , _base_dir( baseTmpDir )
+    {
+      MIL << "Current KeyRing::DefaultAccept: " << _keyRingDefaultAccept << endl;
+    }
+
+    void importKey( const PublicKey &key, bool trusted = false);
+    void multiKeyImport( const Pathname & keyfile_r, bool trusted_r = false);
+    void deleteKey( const string &id, bool trusted );
+
+    string readSignatureKeyId( const Pathname &signature );
+
+    bool isKeyTrusted( const string &id);
+    bool isKeyKnown( const string &id );
+
+    list<PublicKey> trustedPublicKeys();
+    list<PublicKey> publicKeys();
+
+    list<string> trustedPublicKeyIds();
+    list<string> publicKeyIds();
+
+    void dumpPublicKey( const string &id, bool trusted, ostream &stream );
+
+    bool verifyFileSignatureWorkflow(
+        const Pathname &file,
+        const string filedesc,
+        const Pathname &signature,
+        const KeyContext &keycontext = KeyContext());
+
+    bool verifyFileSignature( const Pathname &file, const Pathname &signature);
+    bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
+  private:
+    //mutable map<Locale, string> translations;
+    bool verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring);
+    void importKey( const Pathname &keyfile, const Pathname &keyring);
+    PublicKey exportKey( string id, const Pathname &keyring);
+    void dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream );
+    void deleteKey( const string &id, const Pathname &keyring );
+
+    list<PublicKey> publicKeys(const Pathname &keyring);
+    list<string> publicKeyIds(const Pathname &keyring);
+
+    bool publicKeyExists( string id, const Pathname &keyring);
+
+    const Pathname generalKeyRing() const;
+    const Pathname trustedKeyRing() const;
+
+    // Used for trusted and untrusted keyrings
+    TmpDir _trusted_tmp_dir;
+    TmpDir _general_tmp_dir;
+    Pathname _base_dir;
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl( TmpPath::defaultLocation() ) );
+      return _nullimpl;
+    }
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+
+
+  const Pathname KeyRing::Impl::generalKeyRing() const
+  {
+    return _general_tmp_dir.path();
+  }
+
+  const Pathname KeyRing::Impl::trustedKeyRing() const
+  {
+    return _trusted_tmp_dir.path();
+  }
+
+  void KeyRing::Impl::importKey( const PublicKey &key, bool trusted)
+  {
+    callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
+    callback::SendReport<KeyRingSignals> emitSignal;
+
+    importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
+
+    if ( trusted )
+    {
+      rpmdbEmitSignal->trustedKeyAdded( key );
+      emitSignal->trustedKeyAdded( key );
+    }
+  }
+
+  void KeyRing::Impl::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
+  {
+    importKey( keyfile_r, trusted_r ? trustedKeyRing() : generalKeyRing() );
+  }
+
+  void KeyRing::Impl::deleteKey( const string &id, bool trusted)
+  {
+    PublicKey key;
+
+    if (trusted)
+    {
+       key = exportKey(id, trustedKeyRing());
+    }
+
+    deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
+
+    if ( trusted )
+    {
+      callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
+      callback::SendReport<KeyRingSignals> emitSignal;
+
+      rpmdbEmitSignal->trustedKeyRemoved( key );
+      emitSignal->trustedKeyRemoved( key );
+    }
+  }
+
+  list<PublicKey> KeyRing::Impl::publicKeys()
+  {
+    return publicKeys( generalKeyRing() );
+  }
+
+  list<PublicKey> KeyRing::Impl::trustedPublicKeys()
+  {
+    return publicKeys( trustedKeyRing() );
+  }
+
+  list<string> KeyRing::Impl::publicKeyIds()
+  {
+    return publicKeyIds( generalKeyRing() );
+  }
+
+  list<string> KeyRing::Impl::trustedPublicKeyIds()
+  {
+    return publicKeyIds( trustedKeyRing() );
+  }
+
+  bool KeyRing::Impl::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
+  {
+    return verifyFile( file, signature, trustedKeyRing() );
+  }
+
+  bool KeyRing::Impl::verifyFileSignature( const Pathname &file, const Pathname &signature)
+  {
+    return verifyFile( file, signature, generalKeyRing() );
+  }
+
+  bool KeyRing::Impl::isKeyTrusted( const string &id)
+  {
+    return publicKeyExists( id, trustedKeyRing() );
+  }
+
+  bool KeyRing::Impl::isKeyKnown( const string &id )
+  {
+    MIL << endl;
+    if ( publicKeyExists( id, trustedKeyRing() ) )
+      return true;
+    else
+      return publicKeyExists( id, generalKeyRing() );
+  }
+
+  bool KeyRing::Impl::publicKeyExists( string id, const Pathname &keyring)
+  {
+    MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
+    list<PublicKey> keys = publicKeys(keyring);
+    for (list<PublicKey>::const_iterator it = keys.begin(); it != keys.end(); it++)
+    {
+      if ( id == (*it).id() )
+
+        return true;
+    }
+    return false;
+  }
+
+  PublicKey KeyRing::Impl::exportKey( string id, const Pathname &keyring)
+  {
+    TmpFile tmp_file( _base_dir, "pubkey-"+id+"-" );
+    MIL << "Going to export key " << id << " from " << keyring << " to " << tmp_file.path() << endl;
+
+    try {
+      ofstream os(tmp_file.path().c_str());
+      dumpPublicKey( id, keyring, os );
+      os.close();
+      return PublicKey( tmp_file );
+    }
+    catch (BadKeyException &e)
+    {
+      ERR << "Cannot create public key " << id << " from " << keyring << " keyring  to file " << e.keyFile() << endl;
+      // TranslatorExplanation first %s is key name, second is keyring name
+      // and third is keyfile name
+      ZYPP_THROW(Exception(boost::str(boost::format(
+          _("Cannot create public key %s from %s keyring to file %s"))
+          % id % keyring.asString() % e.keyFile().asString())));
+    }
+    catch (exception &e)
+    {
+      ERR << "Cannot export key " << id << " from " << keyring << " keyring  to file " << tmp_file.path() << endl;
+    }
+    return PublicKey();
+  }
+
+  void KeyRing::Impl::dumpPublicKey( const string &id, bool trusted, ostream &stream )
+  {
+     dumpPublicKey( id, ( trusted ? trustedKeyRing() : generalKeyRing() ), stream );
+  }
+
+  void KeyRing::Impl::dumpPublicKey( const string &id, const Pathname &keyring, ostream &stream )
+  {
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--quiet",
+      "--no-tty",
+      "--no-greeting",
+      "--no-permission-warning",
+      "--batch",
+      "--homedir",
+      keyring.asString().c_str(),
+      "-a",
+      "--export",
+      id.c_str(),
+      NULL
+    };
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+    string line;
+    int count;
+    for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
+    {
+      stream << line;
+    }
+    prog.close();
+  }
+
+
+  bool KeyRing::Impl::verifyFileSignatureWorkflow(
+      const Pathname &file,
+      const string filedesc,
+      const Pathname &signature,
+      const KeyContext &context)
+  {
+    callback::SendReport<KeyRingReport> report;
+    //callback::SendReport<KeyRingSignals> emitSignal;
+    MIL << "Going to verify signature for " << filedesc << " ( " << file << " ) with " << signature << endl;
+
+    // if signature does not exists, ask user if he wants to accept unsigned file.
+    if( signature.empty() || (!PathInfo(signature).isExist()) )
+    {
+      bool res = report->askUserToAcceptUnsignedFile( filedesc, context );
+      MIL << "User decision on unsigned file: " << res << endl;
+      return res;
+    }
+
+    // get the id of the signature
+    string id = readSignatureKeyId(signature);
+
+    // doeskey exists in trusted keyring
+    if ( publicKeyExists( id, trustedKeyRing() ) )
+    {
+      PublicKey key = exportKey( id, trustedKeyRing() );
+
+      // lets look if there is an updated key in the
+      // general keyring
+      if ( publicKeyExists( id, generalKeyRing() ) )
+      {
+        // bnc #393160: Comment #30: Compare at least the fingerprint
+        // in case an attacker created a key the the same id.
+        PublicKey untkey = exportKey( id, generalKeyRing() );
+        if ( untkey.fingerprint() == key.fingerprint()
+             && untkey.created() > key.created() )
+        {
+          MIL << "Key " << key << " was updated. Saving new version into trusted keyring." << endl;
+          importKey( untkey, true );
+          key = untkey;
+        }
+      }
+
+      MIL << "Key " << id << " " << key.name() << " is trusted" << endl;
+      // it exists, is trusted, does it validates?
+      if ( verifyFile( file, signature, trustedKeyRing() ) )
+        return true;
+      else
+        return report->askUserToAcceptVerificationFailed( filedesc, key, context );
+    }
+    else
+    {
+      if ( publicKeyExists( id, generalKeyRing() ) )
+      {
+        PublicKey key =  exportKey( id, generalKeyRing());
+        MIL << "Exported key " << id << " to " << key.path() << endl;
+        MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
+
+        // ok the key is not trusted, ask the user to trust it or not
+        KeyRingReport::KeyTrust reply = report->askUserToAcceptKey(key, context);
+        if (reply == KeyRingReport::KEY_TRUST_TEMPORARILY ||
+            reply == KeyRingReport::KEY_TRUST_AND_IMPORT)
+        {
+          MIL << "User wants to trust key " << id << " " << key.name() << endl;
+          //dumpFile(unKey.path());
+
+          Pathname which_keyring;
+          if (reply == KeyRingReport::KEY_TRUST_AND_IMPORT)
+          {
+            MIL << "User wants to import key " << id << " " << key.name() << endl;
+            importKey( key, true );
+            which_keyring = trustedKeyRing();
+          }
+          else
+            which_keyring = generalKeyRing();
+
+          // emit key added
+          if ( verifyFile( file, signature, which_keyring ) )
+          {
+            MIL << "File signature is verified" << endl;
+            return true;
+          }
+          else
+          {
+            MIL << "File signature check fails" << endl;
+            if ( report->askUserToAcceptVerificationFailed( filedesc, key, context ) )
+            {
+              MIL << "User continues anyway." << endl;
+              return true;
+            }
+            else
+            {
+              MIL << "User does not want to continue" << endl;
+              return false;
+            }
+          }
+        }
+        else
+        {
+          MIL << "User does not want to trust key " << id << " " << key.name() << endl;
+          return false;
+        }
+      }
+      else
+      {
+        // unknown key...
+        MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
+        if ( report->askUserToAcceptUnknownKey( filedesc, id, context ) )
+        {
+          MIL << "User wants to accept unknown key " << id << endl;
+          return true;
+        }
+        else
+        {
+          MIL << "User does not want to accept unknown key " << id << endl;
+          return false;
+        }
+      }
+    }
+    return false;
+  }
+
+  list<string> KeyRing::Impl::publicKeyIds(const Pathname &keyring)
+  {
+    static str::regex rxColons("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
+    static str::regex rxColonsFpr("^([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):\n$");
+
+    list<string> ids;
+
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--quiet",
+      "--list-public-keys",
+      "--with-colons",
+      "--with-fingerprint",
+      "--no-tty",
+      "--no-greeting",
+      "--batch",
+      "--status-fd",
+      "1",
+      "--homedir",
+      keyring.asString().c_str(),
+      NULL
+    };
+
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+    string line;
+    int count = 0;
+
+    for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
+    {
+      //MIL << line << endl;
+      str::smatch what;
+      if(str::regex_match(line, what, rxColons))
+      {
+        string id;
+        string fingerprint;
+        if ( what[1] == "pub" )
+        {
+          id = what[5];
+
+          string line2;
+          for(line2 = prog.receiveLine(); !line2.empty(); line2 = prog.receiveLine(), count++ )
+          {
+            str::smatch what2;
+            if (str::regex_match(line2, what2, rxColonsFpr))
+            {
+              if ( (what2[1] == "fpr") && (what2[1] != "pub") && (what2[1] !="sub"))
+              {
+                fingerprint = what2[10];
+                break;
+              }
+            }
+          }
+
+          ids.push_back(id);
+          MIL << "Found key " << "[" << id << "]" << endl;
+        }
+        //dumpRegexpResults(what);
+      }
+    }
+    prog.close();
+    return ids;
+  }
+
+  list<PublicKey> KeyRing::Impl::publicKeys(const Pathname &keyring)
+  {
+
+    list<PublicKey> keys;
+
+    list<string> ids = publicKeyIds(keyring);
+
+    for ( list<string>::const_iterator it = ids.begin(); it != ids.end(); ++it )
+    {
+      PublicKey key(exportKey( *it, keyring ));
+      keys.push_back(key);
+      MIL << "Found key " << "[" << key.id() << "]" << " [" << key.name() << "]" << " [" << key.fingerprint() << "]" << endl;
+    }
+    return keys;
+  }
+
+  void KeyRing::Impl::importKey( const Pathname &keyfile, const Pathname &keyring)
+  {
+    if ( ! PathInfo(keyfile).isExist() )
+      // TranslatorExplanation first %s is key name, second is keyring name
+      ZYPP_THROW(KeyRingException(boost::str(boost::format(
+          _("Tried to import not existant key %s into keyring %s"))
+          % keyfile.asString() % keyring.asString())));
+
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--quiet",
+      "--no-tty",
+      "--no-greeting",
+      "--no-permission-warning",
+      "--status-fd",
+      "1",
+      "--homedir",
+      keyring.asString().c_str(),
+      "--import",
+      keyfile.asString().c_str(),
+      NULL
+    };
+
+    int code;
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+    code = prog.close();
+
+    //if ( code != 0 )
+    //  ZYPP_THROW(Exception("failed to import key"));
+  }
+
+  void KeyRing::Impl::deleteKey( const string &id, const Pathname &keyring )
+  {
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--yes",
+      "--quiet",
+      "--no-tty",
+      "--batch",
+      "--status-fd",
+      "1",
+      "--homedir",
+      keyring.asString().c_str(),
+      "--delete-keys",
+      id.c_str(),
+      NULL
+    };
+
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+    int code = prog.close();
+    if ( code )
+      ZYPP_THROW(Exception(_("Failed to delete key.")));
+    else
+      MIL << "Deleted key " << id << " from keyring " << keyring << endl;
+  }
+
+
+  string KeyRing::Impl::readSignatureKeyId(const Pathname &signature )
+  {
+    if ( ! PathInfo(signature).isFile() )
+      ZYPP_THROW(Exception(boost::str(boost::format(
+          _("Signature file %s not found"))% signature.asString())));
+
+    MIL << "Determining key id if signature " << signature << endl;
+    // HACK create a tmp keyring with no keys
+    TmpDir dir(_base_dir, "fake-keyring");
+
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--quiet",
+      "--no-tty",
+      "--no-greeting",
+      "--batch",
+      "--status-fd",
+      "1",
+      "--homedir",
+      dir.path().asString().c_str(),
+      signature.asString().c_str(),
+      NULL
+    };
+
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+    string line;
+    int count = 0;
+
+    str::regex rxNoKey("^\\[GNUPG:\\] NO_PUBKEY (.+)\n$");
+    string id;
+    for(line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
+    {
+      //MIL << "[" << line << "]" << endl;
+      str::smatch what;
+      if(str::regex_match(line, what, rxNoKey))
+      {
+        if ( what.size() >= 1 )
+       {
+          id = what[1];
+         break;
+       }
+        //dumpRegexpResults(what);
+      }
+    }
+
+    if ( count == 0 )
+    {
+      MIL << "no output" << endl;
+    }
+
+    MIL << "Determined key id [" << id << "] for signature " << signature << endl;
+    prog.close();
+    return id;
+  }
+
+  bool KeyRing::Impl::verifyFile( const Pathname &file, const Pathname &signature, const Pathname &keyring)
+  {
+    const char* argv[] =
+    {
+      GPG_BINARY,
+      "--no-default-keyring",
+      "--quiet",
+      "--no-tty",
+      "--batch",
+      "--no-greeting",
+      "--status-fd",
+      "1",
+      "--homedir",
+      keyring.asString().c_str(),
+      "--verify",
+      signature.asString().c_str(),
+      file.asString().c_str(),
+      NULL
+    };
+
+    // no need to parse output for now
+    //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
+    //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
+    //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
+    //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
+    //     [GNUPG:] TRUST_UNDEFINED
+
+    //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
+    //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
+
+    ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+    return (prog.close() == 0) ? true : false;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : KeyRing
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : KeyRing::KeyRing
+  //   METHOD TYPE : Ctor
+  //
+  KeyRing::KeyRing(const Pathname &baseTmpDir)
+  : _pimpl( new Impl(baseTmpDir) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : KeyRing::KeyRing
+  //   METHOD TYPE : Ctor
+  //
+  //KeyRing::KeyRing( const Pathname &general_kr, const Pathname &trusted_kr )
+  //: _pimpl( new Impl(general_kr, trusted_kr) )
+  //{}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : KeyRing::~KeyRing
+  //   METHOD TYPE : Dtor
+  //
+  KeyRing::~KeyRing()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to implementation:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+
+  void KeyRing::importKey( const PublicKey &key, bool trusted )
+  {
+    _pimpl->importKey( key, trusted );
+  }
+
+  void KeyRing::multiKeyImport( const Pathname & keyfile_r, bool trusted_r )
+  {
+    _pimpl->multiKeyImport( keyfile_r, trusted_r );
+  }
+
+  string KeyRing::readSignatureKeyId( const Pathname &signature )
+  {
+    return _pimpl->readSignatureKeyId(signature);
+  }
+
+  void KeyRing::deleteKey( const string &id, bool trusted )
+  {
+    _pimpl->deleteKey(id, trusted);
+  }
+
+  list<PublicKey> KeyRing::publicKeys()
+  {
+    return _pimpl->publicKeys();
+  }
+
+  list<PublicKey> KeyRing::trustedPublicKeys()
+  {
+    return _pimpl->trustedPublicKeys();
+  }
+
+  list<string> KeyRing::publicKeyIds()
+  {
+    return _pimpl->publicKeyIds();
+  }
+
+  list<string> KeyRing::trustedPublicKeyIds()
+  {
+    return _pimpl->trustedPublicKeyIds();
+  }
+
+  bool KeyRing::verifyFileSignatureWorkflow(
+      const Pathname &file,
+      const string filedesc,
+      const Pathname &signature,
+      const KeyContext &keycontext)
+  {
+    return _pimpl->verifyFileSignatureWorkflow(file, filedesc, signature, keycontext);
+  }
+
+  bool KeyRing::verifyFileSignature( const Pathname &file, const Pathname &signature)
+  {
+    return _pimpl->verifyFileSignature(file, signature);
+  }
+
+  bool KeyRing::verifyFileTrustedSignature( const Pathname &file, const Pathname &signature)
+  {
+    return _pimpl->verifyFileTrustedSignature(file, signature);
+  }
+
+  void KeyRing::dumpPublicKey( const string &id, bool trusted, ostream &stream )
+  {
+    _pimpl->dumpPublicKey( id, trusted, stream);
+  }
+
+  bool KeyRing::isKeyTrusted( const string &id )
+  {
+    return _pimpl->isKeyTrusted(id);
+  }
+
+  bool KeyRing::isKeyKnown( const string &id )
+  {
+    return _pimpl->isKeyKnown(id);
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/KeyRing.h b/zypp/KeyRing.h
new file mode 100644 (file)
index 0000000..c5ce8a7
--- /dev/null
@@ -0,0 +1,308 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/KeyRing.h
+ *
+*/
+#ifndef ZYPP_KEYRING_H
+#define ZYPP_KEYRING_H
+
+#include <iosfwd>
+#include <map>
+#include <list>
+#include <set>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/Flags.h"
+#include "zypp/Callback.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Locale.h"
+#include "zypp/PublicKey.h"
+#include "zypp/KeyContext.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  DEFINE_PTR_TYPE(KeyRing);
+
+  /** Callbacks from signature verification workflow.
+   *
+   * Per default all methods answer \c false. This may be canged
+   * by calling \ref KeyRing::setDefaultAccept.
+   * \code
+   *  KeyRing::setDefaultAccept( KeyRing::ACCEPT_UNSIGNED_FILE | KeyRing::ACCEPT_VERIFICATION_FAILED );
+   * \endcode
+   * \see \ref KeyRing
+  */
+  struct KeyRingReport : public callback::ReportBase
+  {
+    /**
+     * User reply options for the askUserToTrustKey callback.
+     *
+     * \param filedes Name of the file (repo alias) or filename if not available
+     */
+    enum KeyTrust
+    {
+      /**
+       * User has chosen not to trust the key.
+       */
+      KEY_DONT_TRUST = 0,
+      /**
+       * This basically means, we knew the key, but it was not trusted. User
+       * has chosen to continue, but not import the key.
+       */
+      KEY_TRUST_TEMPORARILY,
+      /**
+       * Import the key.
+       * This means saving the key in the trusted database so next run it will appear as trusted.
+       * Nothing to do with KEY_TRUST_TEMPORARILY, as you CAN trust a key without importing it,
+       * basically you will be asked every time again.
+       * There are programs who prefer to manage the trust keyring on their own and use trustKey
+       * without importing it into rpm.
+       */
+      KEY_TRUST_AND_IMPORT
+    };
+
+    /**
+     * Ask user to trust and/or import the key to trusted keyring.
+     * \see KeyTrust
+     */
+    virtual KeyTrust askUserToAcceptKey( const PublicKey &key, const KeyContext &keycontext = KeyContext() );
+
+    virtual bool askUserToAcceptUnsignedFile( const std::string &file, const KeyContext &keycontext = KeyContext() );
+
+    /**
+     * we DONT know the key, only its id, but we have never seen it, the difference
+     * with trust key is that if you dont have it, you can't import it later.
+     * The answer means continue yes or no?
+     *
+     */
+    virtual bool askUserToAcceptUnknownKey( const std::string &file, const std::string &id, const KeyContext &keycontext = KeyContext() );
+
+    /**
+     * The file \ref filedesc is signed but the verification failed
+     *
+     * \param filedesc Filename or its description.
+     */
+    virtual bool askUserToAcceptVerificationFailed( const std::string &file, const PublicKey &key, const KeyContext &keycontext = KeyContext() );
+
+  };
+
+  struct KeyRingSignals : public callback::ReportBase
+  {
+    virtual void trustedKeyAdded( const PublicKey &/*key*/ )
+    {}
+    virtual void trustedKeyRemoved( const PublicKey &/*key*/ )
+    {}
+  };
+
+  class KeyRingException : public Exception
+   {
+     public:
+       /** Ctor taking message.
+      * Use \ref ZYPP_THROW to throw exceptions.
+        */
+       KeyRingException()
+       : Exception( "Bad Key Exception" )
+       {}
+       /** Ctor taking message.
+        * Use \ref ZYPP_THROW to throw exceptions.
+        */
+       KeyRingException( const std::string & msg_r )
+       : Exception( msg_r )
+       {}
+       /** Dtor. */
+       virtual ~KeyRingException() throw() {};
+   };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : KeyRing
+  //
+  /** Gpg key handling.
+   *
+  */
+  class KeyRing : public base::ReferenceCounted, private base::NonCopyable
+  {
+    friend std::ostream & operator<<( std::ostream & str, const KeyRing & obj );
+
+    public:
+      /** \name Default answers in verification workflow.
+       * Per default all answers are \c false.
+       */
+      //@{
+      /** \ref DefaultAccept flags (\see \ref base::Flags) are used to
+       *  define the default callback answers during signature verification.
+       * \code
+       *  KeyRing::setDefaultAccept( KeyRing::ACCEPT_UNSIGNED_FILE | ACCEPT_VERIFICATION_FAILED );
+       * \endcode
+       * \see \ref KeyRingReport.
+       */
+      enum DefaultAcceptBits
+      {
+        ACCEPT_NOTHING             = 0x0000,
+        ACCEPT_UNSIGNED_FILE       = 0x0001,
+        ACCEPT_UNKNOWNKEY          = 0x0002,
+        TRUST_KEY_TEMPORARILY      = 0x0004,
+        TRUST_AND_IMPORT_KEY       = 0x0008,
+        ACCEPT_VERIFICATION_FAILED = 0x0010,
+      };
+      ZYPP_DECLARE_FLAGS( DefaultAccept, DefaultAcceptBits );
+
+      /** Get the active accept bits. */
+      static DefaultAccept defaultAccept();
+
+      /** Set the active accept bits. */
+      static void setDefaultAccept( DefaultAccept value_r );
+     //@}
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor */
+    KeyRing(const Pathname &baseTmpDir);
+
+    /**
+     * imports a key from a file.
+     * throw if key was not imported
+     */
+    void importKey( const PublicKey &key, bool trusted = false);
+
+    /** Initial import from \ref RpmDb. */
+    void multiKeyImport( const Pathname & keyfile_r, bool trusted_r = false );
+
+    void dumpTrustedPublicKey( const std::string &id, std::ostream &stream )
+    { dumpPublicKey(id, true, stream); }
+
+    void dumpUntrustedPublicKey( const std::string &id, std::ostream &stream )
+    { dumpPublicKey(id, false, stream); }
+
+    void dumpPublicKey( const std::string &id, bool trusted, std::ostream &stream );
+
+    /**
+     * reads the public key id from a signature
+     */
+    std::string readSignatureKeyId( const Pathname &signature );
+
+    /**
+     * true if the key id is trusted
+     */
+    bool isKeyTrusted( const std::string &id);
+
+    /**
+     * true if the key id is knows, that means
+     * at least exist on the untrusted keyring
+     */
+    bool isKeyKnown( const std::string &id );
+
+    /**
+     * removes a key from the keyring.
+     * If trusted is true, Remove it from trusted keyring too.
+     */
+    void deleteKey( const std::string &id, bool trusted =  false);
+
+    /**
+     * Get a list of public keys in the keyring
+     */
+    std::list<PublicKey> publicKeys();
+
+    /**
+     * Get a list of trusted public keys in the keyring
+     */
+    std::list<PublicKey> trustedPublicKeys();
+
+    /**
+     * Get a list of public key ids in the keyring
+     */
+    std::list<std::string> publicKeyIds();
+
+    /**
+     * Get a list of trusted public key ids in the keyring
+     */
+    std::list<std::string> trustedPublicKeyIds();
+
+    /**
+     * Follows a signature verification interacting with the user.
+     * The bool returned depends on user decision to trust or not.
+     *
+     * To propagate user decisions, either connect to the \ref KeyRingReport
+     * or use its static methods to set the desired defaults.
+     *
+     * \code
+     * struct KeyRingReportReceive : public callback::ReceiveReport<KeyRingReport>
+     * {
+     *   KeyRingReportReceive() { connect(); }
+     *
+     *   // Overload the virtual methods to return the appropriate values.
+     *   virtual bool askUserToAcceptUnsignedFile( const std::string &file );
+     *   ...
+     * };
+     * \endcode
+     *
+     * \param file Path of the file to be verified
+     * \param filedesc Description of the file (to give the user some context)
+     * \param signature Signature to verify the file against
+     *
+     * \see \ref KeyRingReport
+     */
+    bool verifyFileSignatureWorkflow(
+        const Pathname &file,
+        const std::string filedesc,
+        const Pathname &signature,
+        const KeyContext &keycontext = KeyContext());
+
+
+    /**
+     * Verifies a file against a signature, with no user interaction
+     *
+     * \param file Path of the file to be verified
+     * \param signature Signature to verify the file against
+     */
+    bool verifyFileSignature( const Pathname &file, const Pathname &signature);
+
+    bool verifyFileTrustedSignature( const Pathname &file, const Pathname &signature);
+
+    /** Dtor */
+    ~KeyRing();
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates KeyRing Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const KeyRing & /*obj*/ )
+  {
+    //return str << obj.asString();
+    return str;
+  }
+
+  /** \relates KeyRing::DefaultAccept  */
+  ZYPP_DECLARE_OPERATORS_FOR_FLAGS( KeyRing::DefaultAccept );
+
+  ///////////////////////////////////////////////////////////////////
+
+  namespace target
+  {
+    namespace rpm
+    {
+      /** Internal connection to rpm database. Not for public use. */
+      struct KeyRingSignals : public ::zypp::KeyRingSignals
+      {};
+    }
+  }
+
+ /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_KEYRING_H
diff --git a/zypp/LanguageCode.cc b/zypp/LanguageCode.cc
new file mode 100644 (file)
index 0000000..ab368b5
--- /dev/null
@@ -0,0 +1,1214 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/LanguageCode.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/LanguageCode.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /** Wrap static codemap data. */
+    struct CodeMaps // singleton
+    {
+      typedef std::tr1::unordered_map<std::string,std::string> CodeMap;
+      typedef CodeMap::const_iterator Index;
+
+      /** Return the CodeMap Index for \a code_r. */
+      static Index getIndex( const std::string & code_r )
+      {
+        static CodeMaps _maps; // the singleton instance
+        return _maps.lookup( code_r );
+      }
+
+    private:
+      /** Ctor initializes the code maps.
+       * http://www.loc.gov/standards/iso639-2/ISO-639-2_values_8bits.txt
+      */
+      CodeMaps();
+
+      /** Make shure the code is in the code maps and return it's index. */
+      inline Index lookup( const std::string & code_r );
+
+    private:
+      /** All the codes. */
+      CodeMap codes;
+    };
+
+    inline CodeMaps::Index CodeMaps::lookup( const std::string & code_r )
+    {
+      Index it = codes.find( code_r );
+      if ( it != codes.end() )
+        return it;
+
+      // not found: Remember a new code
+      CodeMap::value_type nval( code_r, std::string() );
+
+      if ( code_r.size() > 3 || code_r.size() < 2 )
+        WAR << "Malformed LanguageCode '" << code_r << "' (expect 2 or 3-letter)" << endl;
+
+      std::string lcode( str::toLower( code_r ) );
+      if ( lcode != code_r )
+        {
+          WAR << "Malformed LanguageCode '" << code_r << "' (not lower case)" << endl;
+          // but maybe we're lucky with the lower case code
+          // and find a language name.
+          it = codes.find( lcode );
+          if ( it != codes.end() )
+            nval.second = it->second;
+        }
+
+      MIL << "Remember LanguageCode '" << code_r << "': '" << nval.second << "'" << endl;
+      return codes.insert( nval ).first;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : LanguageCode::Impl
+  //
+  /** LanguageCode implementation.
+   * \note CodeMaps contain the untranslated language names.
+   * Translation is done in \ref name.
+  */
+  struct LanguageCode::Impl
+  {
+    Impl()
+    : _index( CodeMaps::getIndex( std::string() ) )
+    {}
+
+    Impl( const std::string & code_r )
+    : _index( CodeMaps::getIndex( code_r ) )
+    {}
+
+    std::string code() const
+    { return _index->first; }
+
+    std::string name() const {
+      if ( _index->second.empty() )
+        {
+          std::string ret( _("Unknown language: ") );
+          ret += "'";
+          ret += _index->first;
+          ret += "'";
+          return ret;
+        }
+      return _( _index->second.c_str() );
+    }
+
+  private:
+    /** index into code map. */
+    CodeMaps::Index _index;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : LanguageCode
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const LanguageCode LanguageCode::noCode;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : LanguageCode::LanguageCode
+  //   METHOD TYPE : Ctor
+  //
+  LanguageCode::LanguageCode()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : LanguageCode::LanguageCode
+  //   METHOD TYPE : Ctor
+  //
+  LanguageCode::LanguageCode( const std::string & code_r )
+  : _pimpl( new Impl( code_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : LanguageCode::~LanguageCode
+  //   METHOD TYPE : Dtor
+  //
+  LanguageCode::~LanguageCode()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : LanguageCode::code
+  //   METHOD TYPE : std::string
+  //
+  std::string LanguageCode::code() const
+  { return _pimpl->code(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : LanguageCode::name
+  //   METHOD TYPE : std::string
+  //
+  std::string LanguageCode::name() const
+  { return _pimpl->name(); }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    CodeMaps::CodeMaps()
+    {
+      // Defined LanguageCode constants
+      codes[""]        = N_("No Code");
+
+      struct LangInit
+      {
+         const char *iso639_2;
+         const char *iso639_1;
+         const char *name;
+      };
+
+      // some languages have more than one iso639_2 code
+      // so there are items with duplicate names
+      const LangInit langInit[] = {
+         // language code: aar aa
+         { "aar", "aa", N_( "Afar" ) },
+         // language code: abk ab
+         { "abk", "ab", N_( "Abkhazian" ) },
+         // language code: ace
+         { "ace", NULL, N_( "Achinese" ) },
+         // language code: ach
+         { "ach", NULL, N_( "Acoli" ) },
+         // language code: ada
+         { "ada", NULL, N_( "Adangme" ) },
+         // language code: ady
+         { "ady", NULL, N_( "Adyghe" ) },
+         // language code: afa
+         { "afa", NULL, N_( "Afro-Asiatic (Other)" ) },
+         // language code: afh
+         { "afh", NULL, N_( "Afrihili" ) },
+         // language code: afr af
+         { "afr", "af", N_( "Afrikaans" ) },
+         // language code: ain
+         { "ain", NULL, N_( "Ainu" ) },
+         // language code: aka ak
+         { "aka", "ak", N_( "Akan" ) },
+         // language code: akk
+         { "akk", NULL, N_( "Akkadian" ) },
+         // language code: alb sqi sq
+         { "alb", "sq", N_( "Albanian" ) },
+         // language code: alb sqi sq
+         { "sqi", NULL, N_( "Albanian" ) },
+         // language code: ale
+         { "ale", NULL, N_( "Aleut" ) },
+         // language code: alg
+         { "alg", NULL, N_( "Algonquian Languages" ) },
+         // language code: alt
+         { "alt", NULL, N_( "Southern Altai" ) },
+         // language code: amh am
+         { "amh", "am", N_( "Amharic" ) },
+         // language code: ang
+         { "ang", NULL, N_( "English, Old (ca.450-1100)" ) },
+         // language code: apa
+         { "apa", NULL, N_( "Apache Languages" ) },
+         // language code: ara ar
+         { "ara", "ar", N_( "Arabic" ) },
+         // language code: arc
+         { "arc", NULL, N_( "Aramaic" ) },
+         // language code: arg an
+         { "arg", "an", N_( "Aragonese" ) },
+         // language code: arm hye hy
+         { "arm", "hy", N_( "Armenian" ) },
+         // language code: arm hye hy
+         { "hye", NULL, N_( "Armenian" ) },
+         // language code: arn
+         { "arn", NULL, N_( "Araucanian" ) },
+         // language code: arp
+         { "arp", NULL, N_( "Arapaho" ) },
+         // language code: art
+         { "art", NULL, N_( "Artificial (Other)" ) },
+         // language code: arw
+         { "arw", NULL, N_( "Arawak" ) },
+         // language code: asm as
+         { "asm", "as", N_( "Assamese" ) },
+         // language code: ast
+         { "ast", NULL, N_( "Asturian" ) },
+         // language code: ath
+         { "ath", NULL, N_( "Athapascan Languages" ) },
+         // language code: aus
+         { "aus", NULL, N_( "Australian Languages" ) },
+         // language code: ava av
+         { "ava", "av", N_( "Avaric" ) },
+         // language code: ave ae
+         { "ave", "ae", N_( "Avestan" ) },
+         // language code: awa
+         { "awa", NULL, N_( "Awadhi" ) },
+         // language code: aym ay
+         { "aym", "ay", N_( "Aymara" ) },
+         // language code: aze az
+         { "aze", "az", N_( "Azerbaijani" ) },
+         // language code: bad
+         { "bad", NULL, N_( "Banda" ) },
+         // language code: bai
+         { "bai", NULL, N_( "Bamileke Languages" ) },
+         // language code: bak ba
+         { "bak", "ba", N_( "Bashkir" ) },
+         // language code: bal
+         { "bal", NULL, N_( "Baluchi" ) },
+         // language code: bam bm
+         { "bam", "bm", N_( "Bambara" ) },
+         // language code: ban
+         { "ban", NULL, N_( "Balinese" ) },
+         // language code: baq eus eu
+         { "baq", "eu", N_( "Basque" ) },
+         // language code: baq eus eu
+         { "eus", NULL, N_( "Basque" ) },
+         // language code: bas
+         { "bas", NULL, N_( "Basa" ) },
+         // language code: bat
+         { "bat", NULL, N_( "Baltic (Other)" ) },
+         // language code: bej
+         { "bej", NULL, N_( "Beja" ) },
+         // language code: bel be
+         { "bel", "be", N_( "Belarusian" ) },
+         // language code: bem
+         { "bem", NULL, N_( "Bemba" ) },
+         // language code: ben bn
+         { "ben", "bn", N_( "Bengali" ) },
+         // language code: ber
+         { "ber", NULL, N_( "Berber (Other)" ) },
+         // language code: bho
+         { "bho", NULL, N_( "Bhojpuri" ) },
+         // language code: bih bh
+         { "bih", "bh", N_( "Bihari" ) },
+         // language code: bik
+         { "bik", NULL, N_( "Bikol" ) },
+         // language code: bin
+         { "bin", NULL, N_( "Bini" ) },
+         // language code: bis bi
+         { "bis", "bi", N_( "Bislama" ) },
+         // language code: bla
+         { "bla", NULL, N_( "Siksika" ) },
+         // language code: bnt
+         { "bnt", NULL, N_( "Bantu (Other)" ) },
+         // language code: bos bs
+         { "bos", "bs", N_( "Bosnian" ) },
+         // language code: bra
+         { "bra", NULL, N_( "Braj" ) },
+         // language code: bre br
+         { "bre", "br", N_( "Breton" ) },
+         // language code: btk
+         { "btk", NULL, N_( "Batak (Indonesia)" ) },
+         // language code: bua
+         { "bua", NULL, N_( "Buriat" ) },
+         // language code: bug
+         { "bug", NULL, N_( "Buginese" ) },
+         // language code: bul bg
+         { "bul", "bg", N_( "Bulgarian" ) },
+         // language code: bur mya my
+         { "bur", "my", N_( "Burmese" ) },
+         // language code: bur mya my
+         { "mya", NULL, N_( "Burmese" ) },
+         // language code: byn
+         { "byn", NULL, N_( "Blin" ) },
+         // language code: cad
+         { "cad", NULL, N_( "Caddo" ) },
+         // language code: cai
+         { "cai", NULL, N_( "Central American Indian (Other)" ) },
+         // language code: car
+         { "car", NULL, N_( "Carib" ) },
+         // language code: cat ca
+         { "cat", "ca", N_( "Catalan" ) },
+         // language code: cau
+         { "cau", NULL, N_( "Caucasian (Other)" ) },
+         // language code: ceb
+         { "ceb", NULL, N_( "Cebuano" ) },
+         // language code: cel
+         { "cel", NULL, N_( "Celtic (Other)" ) },
+         // language code: cha ch
+         { "cha", "ch", N_( "Chamorro" ) },
+         // language code: chb
+         { "chb", NULL, N_( "Chibcha" ) },
+         // language code: che ce
+         { "che", "ce", N_( "Chechen" ) },
+         // language code: chg
+         { "chg", NULL, N_( "Chagatai" ) },
+         // language code: chi zho zh
+         { "chi", "zh", N_( "Chinese" ) },
+         // language code: chi zho zh
+         { "zho", NULL, N_( "Chinese" ) },
+         // language code: chk
+         { "chk", NULL, N_( "Chuukese" ) },
+         // language code: chm
+         { "chm", NULL, N_( "Mari" ) },
+         // language code: chn
+         { "chn", NULL, N_( "Chinook Jargon" ) },
+         // language code: cho
+         { "cho", NULL, N_( "Choctaw" ) },
+         // language code: chp
+         { "chp", NULL, N_( "Chipewyan" ) },
+         // language code: chr
+         { "chr", NULL, N_( "Cherokee" ) },
+         // language code: chu cu
+         { "chu", "cu", N_( "Church Slavic" ) },
+         // language code: chv cv
+         { "chv", "cv", N_( "Chuvash" ) },
+         // language code: chy
+         { "chy", NULL, N_( "Cheyenne" ) },
+         // language code: cmc
+         { "cmc", NULL, N_( "Chamic Languages" ) },
+         // language code: cop
+         { "cop", NULL, N_( "Coptic" ) },
+         // language code: cor kw
+         { "cor", "kw", N_( "Cornish" ) },
+         // language code: cos co
+         { "cos", "co", N_( "Corsican" ) },
+         // language code: cpe
+         { "cpe", NULL, N_( "Creoles and Pidgins, English-Based (Other)" ) },
+         // language code: cpf
+         { "cpf", NULL, N_( "Creoles and Pidgins, French-Based (Other)" ) },
+         // language code: cpp
+         { "cpp", NULL, N_( "Creoles and Pidgins, Portuguese-Based (Other)" ) },
+         // language code: cre cr
+         { "cre", "cr", N_( "Cree" ) },
+         // language code: crh
+         { "crh", NULL, N_( "Crimean Tatar" ) },
+         // language code: crp
+         { "crp", NULL, N_( "Creoles and Pidgins (Other)" ) },
+         // language code: csb
+         { "csb", NULL, N_( "Kashubian" ) },
+         // language code: cus
+         { "cus", NULL, N_( "Cushitic (Other)" ) },
+         // language code: cze ces cs
+         { "cze", "cs", N_( "Czech" ) },
+         // language code: cze ces cs
+         { "ces", NULL, N_( "Czech" ) },
+         // language code: dak
+         { "dak", NULL, N_( "Dakota" ) },
+         // language code: dan da
+         { "dan", "da", N_( "Danish" ) },
+         // language code: dar
+         { "dar", NULL, N_( "Dargwa" ) },
+         // language code: day
+         { "day", NULL, N_( "Dayak" ) },
+         // language code: del
+         { "del", NULL, N_( "Delaware" ) },
+         // language code: den
+         { "den", NULL, N_( "Slave (Athapascan)" ) },
+         // language code: dgr
+         { "dgr", NULL, N_( "Dogrib" ) },
+         // language code: din
+         { "din", NULL, N_( "Dinka" ) },
+         // language code: div dv
+         { "div", "dv", N_( "Divehi" ) },
+         // language code: doi
+         { "doi", NULL, N_( "Dogri" ) },
+         // language code: dra
+         { "dra", NULL, N_( "Dravidian (Other)" ) },
+         // language code: dsb
+         { "dsb", NULL, N_( "Lower Sorbian" ) },
+         // language code: dua
+         { "dua", NULL, N_( "Duala" ) },
+         // language code: dum
+         { "dum", NULL, N_( "Dutch, Middle (ca.1050-1350)" ) },
+         // language code: dut nld nl
+         { "dut", "nl", N_( "Dutch" ) },
+         // language code: dut nld nl
+         { "nld", NULL, N_( "Dutch" ) },
+         // language code: dyu
+         { "dyu", NULL, N_( "Dyula" ) },
+         // language code: dzo dz
+         { "dzo", "dz", N_( "Dzongkha" ) },
+         // language code: efi
+         { "efi", NULL, N_( "Efik" ) },
+         // language code: egy
+         { "egy", NULL, N_( "Egyptian (Ancient)" ) },
+         // language code: eka
+         { "eka", NULL, N_( "Ekajuk" ) },
+         // language code: elx
+         { "elx", NULL, N_( "Elamite" ) },
+         // language code: eng en
+         { "eng", "en", N_( "English" ) },
+         // language code: enm
+         { "enm", NULL, N_( "English, Middle (1100-1500)" ) },
+         // language code: epo eo
+         { "epo", "eo", N_( "Esperanto" ) },
+         // language code: est et
+         { "est", "et", N_( "Estonian" ) },
+         // language code: ewe ee
+         { "ewe", "ee", N_( "Ewe" ) },
+         // language code: ewo
+         { "ewo", NULL, N_( "Ewondo" ) },
+         // language code: fan
+         { "fan", NULL, N_( "Fang" ) },
+         // language code: fao fo
+         { "fao", "fo", N_( "Faroese" ) },
+         // language code: fat
+         { "fat", NULL, N_( "Fanti" ) },
+         // language code: fij fj
+         { "fij", "fj", N_( "Fijian" ) },
+         // language code: fil
+         { "fil", NULL, N_( "Filipino" ) },
+         // language code: fin fi
+         { "fin", "fi", N_( "Finnish" ) },
+         // language code: fiu
+         { "fiu", NULL, N_( "Finno-Ugrian (Other)" ) },
+         // language code: fon
+         { "fon", NULL, N_( "Fon" ) },
+         // language code: fre fra fr
+         { "fre", "fr", N_( "French" ) },
+         // language code: fre fra fr
+         { "fra", NULL, N_( "French" ) },
+         // language code: frm
+         { "frm", NULL, N_( "French, Middle (ca.1400-1600)" ) },
+         // language code: fro
+         { "fro", NULL, N_( "French, Old (842-ca.1400)" ) },
+         // language code: fry fy
+         { "fry", "fy", N_( "Frisian" ) },
+         // language code: ful ff
+         { "ful", "ff", N_( "Fulah" ) },
+         // language code: fur
+         { "fur", NULL, N_( "Friulian" ) },
+         // language code: gaa
+         { "gaa", NULL, N_( "Ga" ) },
+         // language code: gay
+         { "gay", NULL, N_( "Gayo" ) },
+         // language code: gba
+         { "gba", NULL, N_( "Gbaya" ) },
+         // language code: gem
+         { "gem", NULL, N_( "Germanic (Other)" ) },
+         // language code: geo kat ka
+         { "geo", "ka", N_( "Georgian" ) },
+         // language code: geo kat ka
+         { "kat", NULL, N_( "Georgian" ) },
+         // language code: ger deu de
+         { "ger", "de", N_( "German" ) },
+         // language code: ger deu de
+         { "deu", NULL, N_( "German" ) },
+         // language code: gez
+         { "gez", NULL, N_( "Geez" ) },
+         // language code: gil
+         { "gil", NULL, N_( "Gilbertese" ) },
+         // language code: gla gd
+         { "gla", "gd", N_( "Gaelic" ) },
+         // language code: gle ga
+         { "gle", "ga", N_( "Irish" ) },
+         // language code: glg gl
+         { "glg", "gl", N_( "Galician" ) },
+         // language code: glv gv
+         { "glv", "gv", N_( "Manx" ) },
+         // language code: gmh
+         { "gmh", NULL, N_( "German, Middle High (ca.1050-1500)" ) },
+         // language code: goh
+         { "goh", NULL, N_( "German, Old High (ca.750-1050)" ) },
+         // language code: gon
+         { "gon", NULL, N_( "Gondi" ) },
+         // language code: gor
+         { "gor", NULL, N_( "Gorontalo" ) },
+         // language code: got
+         { "got", NULL, N_( "Gothic" ) },
+         // language code: grb
+         { "grb", NULL, N_( "Grebo" ) },
+         // language code: grc
+         { "grc", NULL, N_( "Greek, Ancient (to 1453)" ) },
+         // language code: gre ell el
+         { "gre", "el", N_( "Greek, Modern (1453-)" ) },
+         // language code: gre ell el
+         { "ell", NULL, N_( "Greek, Modern (1453-)" ) },
+         // language code: grn gn
+         { "grn", "gn", N_( "Guarani" ) },
+         // language code: guj gu
+         { "guj", "gu", N_( "Gujarati" ) },
+         // language code: gwi
+         { "gwi", NULL, N_( "Gwich'in" ) },
+         // language code: hai
+         { "hai", NULL, N_( "Haida" ) },
+         // language code: hat ht
+         { "hat", "ht", N_( "Haitian" ) },
+         // language code: hau ha
+         { "hau", "ha", N_( "Hausa" ) },
+         // language code: haw
+         { "haw", NULL, N_( "Hawaiian" ) },
+         // language code: heb he
+         { "heb", "he", N_( "Hebrew" ) },
+         // language code: her hz
+         { "her", "hz", N_( "Herero" ) },
+         // language code: hil
+         { "hil", NULL, N_( "Hiligaynon" ) },
+         // language code: him
+         { "him", NULL, N_( "Himachali" ) },
+         // language code: hin hi
+         { "hin", "hi", N_( "Hindi" ) },
+         // language code: hit
+         { "hit", NULL, N_( "Hittite" ) },
+         // language code: hmn
+         { "hmn", NULL, N_( "Hmong" ) },
+         // language code: hmo ho
+         { "hmo", "ho", N_( "Hiri Motu" ) },
+         // language code: hsb
+         { "hsb", NULL, N_( "Upper Sorbian" ) },
+         // language code: hun hu
+         { "hun", "hu", N_( "Hungarian" ) },
+         // language code: hup
+         { "hup", NULL, N_( "Hupa" ) },
+         // language code: iba
+         { "iba", NULL, N_( "Iban" ) },
+         // language code: ibo ig
+         { "ibo", "ig", N_( "Igbo" ) },
+         // language code: ice isl is
+         { "ice", "is", N_( "Icelandic" ) },
+         // language code: ice isl is
+         { "isl", NULL, N_( "Icelandic" ) },
+         // language code: ido io
+         { "ido", "io", N_( "Ido" ) },
+         // language code: iii ii
+         { "iii", "ii", N_( "Sichuan Yi" ) },
+         // language code: ijo
+         { "ijo", NULL, N_( "Ijo" ) },
+         // language code: iku iu
+         { "iku", "iu", N_( "Inuktitut" ) },
+         // language code: ile ie
+         { "ile", "ie", N_( "Interlingue" ) },
+         // language code: ilo
+         { "ilo", NULL, N_( "Iloko" ) },
+         // language code: ina ia
+         { "ina", "ia", N_( "Interlingua (International Auxiliary Language Association)" ) },
+         // language code: inc
+         { "inc", NULL, N_( "Indic (Other)" ) },
+         // language code: ind id
+         { "ind", "id", N_( "Indonesian" ) },
+         // language code: ine
+         { "ine", NULL, N_( "Indo-European (Other)" ) },
+         // language code: inh
+         { "inh", NULL, N_( "Ingush" ) },
+         // language code: ipk ik
+         { "ipk", "ik", N_( "Inupiaq" ) },
+         // language code: ira
+         { "ira", NULL, N_( "Iranian (Other)" ) },
+         // language code: iro
+         { "iro", NULL, N_( "Iroquoian Languages" ) },
+         // language code: ita it
+         { "ita", "it", N_( "Italian" ) },
+         // language code: jav jv
+         { "jav", "jv", N_( "Javanese" ) },
+         // language code: jbo
+         { "jbo", NULL, N_( "Lojban" ) },
+         // language code: jpn ja
+         { "jpn", "ja", N_( "Japanese" ) },
+         // language code: jpr
+         { "jpr", NULL, N_( "Judeo-Persian" ) },
+         // language code: jrb
+         { "jrb", NULL, N_( "Judeo-Arabic" ) },
+         // language code: kaa
+         { "kaa", NULL, N_( "Kara-Kalpak" ) },
+         // language code: kab
+         { "kab", NULL, N_( "Kabyle" ) },
+         // language code: kac
+         { "kac", NULL, N_( "Kachin" ) },
+         // language code: kal kl
+         { "kal", "kl", N_( "Kalaallisut" ) },
+         // language code: kam
+         { "kam", NULL, N_( "Kamba" ) },
+         // language code: kan kn
+         { "kan", "kn", N_( "Kannada" ) },
+         // language code: kar
+         { "kar", NULL, N_( "Karen" ) },
+         // language code: kas ks
+         { "kas", "ks", N_( "Kashmiri" ) },
+         // language code: kau kr
+         { "kau", "kr", N_( "Kanuri" ) },
+         // language code: kaw
+         { "kaw", NULL, N_( "Kawi" ) },
+         // language code: kaz kk
+         { "kaz", "kk", N_( "Kazakh" ) },
+         // language code: kbd
+         { "kbd", NULL, N_( "Kabardian" ) },
+         // language code: kha
+         { "kha", NULL, N_( "Khasi" ) },
+         // language code: khi
+         { "khi", NULL, N_( "Khoisan (Other)" ) },
+         // language code: khm km
+         { "khm", "km", N_( "Khmer" ) },
+         // language code: kho
+         { "kho", NULL, N_( "Khotanese" ) },
+         // language code: kik ki
+         { "kik", "ki", N_( "Kikuyu" ) },
+         // language code: kin rw
+         { "kin", "rw", N_( "Kinyarwanda" ) },
+         // language code: kir ky
+         { "kir", "ky", N_( "Kirghiz" ) },
+         // language code: kmb
+         { "kmb", NULL, N_( "Kimbundu" ) },
+         // language code: kok
+         { "kok", NULL, N_( "Konkani" ) },
+         // language code: kom kv
+         { "kom", "kv", N_( "Komi" ) },
+         // language code: kon kg
+         { "kon", "kg", N_( "Kongo" ) },
+         // language code: kor ko
+         { "kor", "ko", N_( "Korean" ) },
+         // language code: kos
+         { "kos", NULL, N_( "Kosraean" ) },
+         // language code: kpe
+         { "kpe", NULL, N_( "Kpelle" ) },
+         // language code: krc
+         { "krc", NULL, N_( "Karachay-Balkar" ) },
+         // language code: kro
+         { "kro", NULL, N_( "Kru" ) },
+         // language code: kru
+         { "kru", NULL, N_( "Kurukh" ) },
+         // language code: kua kj
+         { "kua", "kj", N_( "Kuanyama" ) },
+         // language code: kum
+         { "kum", NULL, N_( "Kumyk" ) },
+         // language code: kur ku
+         { "kur", "ku", N_( "Kurdish" ) },
+         // language code: kut
+         { "kut", NULL, N_( "Kutenai" ) },
+         // language code: lad
+         { "lad", NULL, N_( "Ladino" ) },
+         // language code: lah
+         { "lah", NULL, N_( "Lahnda" ) },
+         // language code: lam
+         { "lam", NULL, N_( "Lamba" ) },
+         // language code: lao lo
+         { "lao", "lo", N_( "Lao" ) },
+         // language code: lat la
+         { "lat", "la", N_( "Latin" ) },
+         // language code: lav lv
+         { "lav", "lv", N_( "Latvian" ) },
+         // language code: lez
+         { "lez", NULL, N_( "Lezghian" ) },
+         // language code: lim li
+         { "lim", "li", N_( "Limburgan" ) },
+         // language code: lin ln
+         { "lin", "ln", N_( "Lingala" ) },
+         // language code: lit lt
+         { "lit", "lt", N_( "Lithuanian" ) },
+         // language code: lol
+         { "lol", NULL, N_( "Mongo" ) },
+         // language code: loz
+         { "loz", NULL, N_( "Lozi" ) },
+         // language code: ltz lb
+         { "ltz", "lb", N_( "Luxembourgish" ) },
+         // language code: lua
+         { "lua", NULL, N_( "Luba-Lulua" ) },
+         // language code: lub lu
+         { "lub", "lu", N_( "Luba-Katanga" ) },
+         // language code: lug lg
+         { "lug", "lg", N_( "Ganda" ) },
+         // language code: lui
+         { "lui", NULL, N_( "Luiseno" ) },
+         // language code: lun
+         { "lun", NULL, N_( "Lunda" ) },
+         // language code: luo
+         { "luo", NULL, N_( "Luo (Kenya and Tanzania)" ) },
+         // language code: lus
+         { "lus", NULL, N_( "Lushai" ) },
+         // language code: mac mkd mk
+         { "mac", "mk", N_( "Macedonian" ) },
+         // language code: mac mkd mk
+         { "mkd", NULL, N_( "Macedonian" ) },
+         // language code: mad
+         { "mad", NULL, N_( "Madurese" ) },
+         // language code: mag
+         { "mag", NULL, N_( "Magahi" ) },
+         // language code: mah mh
+         { "mah", "mh", N_( "Marshallese" ) },
+         // language code: mai
+         { "mai", NULL, N_( "Maithili" ) },
+         // language code: mak
+         { "mak", NULL, N_( "Makasar" ) },
+         // language code: mal ml
+         { "mal", "ml", N_( "Malayalam" ) },
+         // language code: man
+         { "man", NULL, N_( "Mandingo" ) },
+         // language code: mao mri mi
+         { "mao", "mi", N_( "Maori" ) },
+         // language code: mao mri mi
+         { "mri", NULL, N_( "Maori" ) },
+         // language code: map
+         { "map", NULL, N_( "Austronesian (Other)" ) },
+         // language code: mar mr
+         { "mar", "mr", N_( "Marathi" ) },
+         // language code: mas
+         { "mas", NULL, N_( "Masai" ) },
+         // language code: may msa ms
+         { "may", "ms", N_( "Malay" ) },
+         // language code: may msa ms
+         { "msa", NULL, N_( "Malay" ) },
+         // language code: mdf
+         { "mdf", NULL, N_( "Moksha" ) },
+         // language code: mdr
+         { "mdr", NULL, N_( "Mandar" ) },
+         // language code: men
+         { "men", NULL, N_( "Mende" ) },
+         // language code: mga
+         { "mga", NULL, N_( "Irish, Middle (900-1200)" ) },
+         // language code: mic
+         { "mic", NULL, N_( "Mi'kmaq" ) },
+         // language code: min
+         { "min", NULL, N_( "Minangkabau" ) },
+         // language code: mis
+         { "mis", NULL, N_( "Miscellaneous Languages" ) },
+         // language code: mkh
+         { "mkh", NULL, N_( "Mon-Khmer (Other)" ) },
+         // language code: mlg mg
+         { "mlg", "mg", N_( "Malagasy" ) },
+         // language code: mlt mt
+         { "mlt", "mt", N_( "Maltese" ) },
+         // language code: mnc
+         { "mnc", NULL, N_( "Manchu" ) },
+         // language code: mni
+         { "mni", NULL, N_( "Manipuri" ) },
+         // language code: mno
+         { "mno", NULL, N_( "Manobo Languages" ) },
+         // language code: moh
+         { "moh", NULL, N_( "Mohawk" ) },
+         // language code: mol mo
+         { "mol", "mo", N_( "Moldavian" ) },
+         // language code: mon mn
+         { "mon", "mn", N_( "Mongolian" ) },
+         // language code: mos
+         { "mos", NULL, N_( "Mossi" ) },
+         // language code: mul
+         { "mul", NULL, N_( "Multiple Languages" ) },
+         // language code: mun
+         { "mun", NULL, N_( "Munda languages" ) },
+         // language code: mus
+         { "mus", NULL, N_( "Creek" ) },
+         // language code: mwl
+         { "mwl", NULL, N_( "Mirandese" ) },
+         // language code: mwr
+         { "mwr", NULL, N_( "Marwari" ) },
+         // language code: myn
+         { "myn", NULL, N_( "Mayan Languages" ) },
+         // language code: myv
+         { "myv", NULL, N_( "Erzya" ) },
+         // language code: nah
+         { "nah", NULL, N_( "Nahuatl" ) },
+         // language code: nai
+         { "nai", NULL, N_( "North American Indian" ) },
+         // language code: nap
+         { "nap", NULL, N_( "Neapolitan" ) },
+         // language code: nau na
+         { "nau", "na", N_( "Nauru" ) },
+         // language code: nav nv
+         { "nav", "nv", N_( "Navajo" ) },
+         // language code: nbl nr
+         { "nbl", "nr", N_( "Ndebele, South" ) },
+         // language code: nde nd
+         { "nde", "nd", N_( "Ndebele, North" ) },
+         // language code: ndo ng
+         { "ndo", "ng", N_( "Ndonga" ) },
+         // language code: nds
+         { "nds", NULL, N_( "Low German" ) },
+         // language code: nep ne
+         { "nep", "ne", N_( "Nepali" ) },
+         // language code: new
+         { "new", NULL, N_( "Nepal Bhasa" ) },
+         // language code: nia
+         { "nia", NULL, N_( "Nias" ) },
+         // language code: nic
+         { "nic", NULL, N_( "Niger-Kordofanian (Other)" ) },
+         // language code: niu
+         { "niu", NULL, N_( "Niuean" ) },
+         // language code: nno nn
+         { "nno", "nn", N_( "Norwegian Nynorsk" ) },
+         // language code: nob nb
+         { "nob", "nb", N_( "Norwegian Bokmal" ) },
+         // language code: nog
+         { "nog", NULL, N_( "Nogai" ) },
+         // language code: non
+         { "non", NULL, N_( "Norse, Old" ) },
+         // language code: nor no
+         { "nor", "no", N_( "Norwegian" ) },
+         // language code: nso
+         { "nso", NULL, N_( "Northern Sotho" ) },
+         // language code: nub
+         { "nub", NULL, N_( "Nubian Languages" ) },
+         // language code: nwc
+         { "nwc", NULL, N_( "Classical Newari" ) },
+         // language code: nya ny
+         { "nya", "ny", N_( "Chichewa" ) },
+         // language code: nym
+         { "nym", NULL, N_( "Nyamwezi" ) },
+         // language code: nyn
+         { "nyn", NULL, N_( "Nyankole" ) },
+         // language code: nyo
+         { "nyo", NULL, N_( "Nyoro" ) },
+         // language code: nzi
+         { "nzi", NULL, N_( "Nzima" ) },
+         // language code: oci oc
+         { "oci", "oc", N_( "Occitan (post 1500)" ) },
+         // language code: oji oj
+         { "oji", "oj", N_( "Ojibwa" ) },
+         // language code: ori or
+         { "ori", "or", N_( "Oriya" ) },
+         // language code: orm om
+         { "orm", "om", N_( "Oromo" ) },
+         // language code: osa
+         { "osa", NULL, N_( "Osage" ) },
+         // language code: oss os
+         { "oss", "os", N_( "Ossetian" ) },
+         // language code: ota
+         { "ota", NULL, N_( "Turkish, Ottoman (1500-1928)" ) },
+         // language code: oto
+         { "oto", NULL, N_( "Otomian Languages" ) },
+         // language code: paa
+         { "paa", NULL, N_( "Papuan (Other)" ) },
+         // language code: pag
+         { "pag", NULL, N_( "Pangasinan" ) },
+         // language code: pal
+         { "pal", NULL, N_( "Pahlavi" ) },
+         // language code: pam
+         { "pam", NULL, N_( "Pampanga" ) },
+         // language code: pan pa
+         { "pan", "pa", N_( "Panjabi" ) },
+         // language code: pap
+         { "pap", NULL, N_( "Papiamento" ) },
+         // language code: pau
+         { "pau", NULL, N_( "Palauan" ) },
+         // language code: peo
+         { "peo", NULL, N_( "Persian, Old (ca.600-400 B.C.)" ) },
+         // language code: per fas fa
+         { "per", "fa", N_( "Persian" ) },
+         // language code: per fas fa
+         { "fas", NULL, N_( "Persian" ) },
+         // language code: phi
+         { "phi", NULL, N_( "Philippine (Other)" ) },
+         // language code: phn
+         { "phn", NULL, N_( "Phoenician" ) },
+         // language code: pli pi
+         { "pli", "pi", N_( "Pali" ) },
+         // language code: pol pl
+         { "pol", "pl", N_( "Polish" ) },
+         // language code: pon
+         { "pon", NULL, N_( "Pohnpeian" ) },
+         // language code: por pt
+         { "por", "pt", N_( "Portuguese" ) },
+         // language code: pra
+         { "pra", NULL, N_( "Prakrit Languages" ) },
+         // language code: pro
+         { "pro", NULL, N_( "Provencal, Old (to 1500)" ) },
+         // language code: pus ps
+         { "pus", "ps", N_( "Pushto" ) },
+         // language code: que qu
+         { "que", "qu", N_( "Quechua" ) },
+         // language code: raj
+         { "raj", NULL, N_( "Rajasthani" ) },
+         // language code: rap
+         { "rap", NULL, N_( "Rapanui" ) },
+         // language code: rar
+         { "rar", NULL, N_( "Rarotongan" ) },
+         // language code: roa
+         { "roa", NULL, N_( "Romance (Other)" ) },
+         // language code: roh rm
+         { "roh", "rm", N_( "Raeto-Romance" ) },
+         // language code: rom
+         { "rom", NULL, N_( "Romany" ) },
+         // language code: rum ron ro
+         { "rum", "ro", N_( "Romanian" ) },
+         // language code: rum ron ro
+         { "ron", NULL, N_( "Romanian" ) },
+         // language code: run rn
+         { "run", "rn", N_( "Rundi" ) },
+         // language code: rus ru
+         { "rus", "ru", N_( "Russian" ) },
+         // language code: sad
+         { "sad", NULL, N_( "Sandawe" ) },
+         // language code: sag sg
+         { "sag", "sg", N_( "Sango" ) },
+         // language code: sah
+         { "sah", NULL, N_( "Yakut" ) },
+         // language code: sai
+         { "sai", NULL, N_( "South American Indian (Other)" ) },
+         // language code: sal
+         { "sal", NULL, N_( "Salishan Languages" ) },
+         // language code: sam
+         { "sam", NULL, N_( "Samaritan Aramaic" ) },
+         // language code: san sa
+         { "san", "sa", N_( "Sanskrit" ) },
+         // language code: sas
+         { "sas", NULL, N_( "Sasak" ) },
+         // language code: sat
+         { "sat", NULL, N_( "Santali" ) },
+         // language code: scc srp sr
+         { "scc", "sr", N_( "Serbian" ) },
+         // language code: scc srp sr
+         { "srp", NULL, N_( "Serbian" ) },
+         // language code: scn
+         { "scn", NULL, N_( "Sicilian" ) },
+         // language code: sco
+         { "sco", NULL, N_( "Scots" ) },
+         // language code: scr hrv hr
+         { "scr", "hr", N_( "Croatian" ) },
+         // language code: scr hrv hr
+         { "hrv", NULL, N_( "Croatian" ) },
+         // language code: sel
+         { "sel", NULL, N_( "Selkup" ) },
+         // language code: sem
+         { "sem", NULL, N_( "Semitic (Other)" ) },
+         // language code: sga
+         { "sga", NULL, N_( "Irish, Old (to 900)" ) },
+         // language code: sgn
+         { "sgn", NULL, N_( "Sign Languages" ) },
+         // language code: shn
+         { "shn", NULL, N_( "Shan" ) },
+         // language code: sid
+         { "sid", NULL, N_( "Sidamo" ) },
+         // language code: sin si
+         { "sin", "si", N_( "Sinhala" ) },
+         // language code: sio
+         { "sio", NULL, N_( "Siouan Languages" ) },
+         // language code: sit
+         { "sit", NULL, N_( "Sino-Tibetan (Other)" ) },
+         // language code: sla
+         { "sla", NULL, N_( "Slavic (Other)" ) },
+         // language code: slo slk sk
+         { "slo", "sk", N_( "Slovak" ) },
+         // language code: slo slk sk
+         { "slk", NULL, N_( "Slovak" ) },
+         // language code: slv sl
+         { "slv", "sl", N_( "Slovenian" ) },
+         // language code: sma
+         { "sma", NULL, N_( "Southern Sami" ) },
+         // language code: sme se
+         { "sme", "se", N_( "Northern Sami" ) },
+         // language code: smi
+         { "smi", NULL, N_( "Sami Languages (Other)" ) },
+         // language code: smj
+         { "smj", NULL, N_( "Lule Sami" ) },
+         // language code: smn
+         { "smn", NULL, N_( "Inari Sami" ) },
+         // language code: smo sm
+         { "smo", "sm", N_( "Samoan" ) },
+         // language code: sms
+         { "sms", NULL, N_( "Skolt Sami" ) },
+         // language code: sna sn
+         { "sna", "sn", N_( "Shona" ) },
+         // language code: snd sd
+         { "snd", "sd", N_( "Sindhi" ) },
+         // language code: snk
+         { "snk", NULL, N_( "Soninke" ) },
+         // language code: sog
+         { "sog", NULL, N_( "Sogdian" ) },
+         // language code: som so
+         { "som", "so", N_( "Somali" ) },
+         // language code: son
+         { "son", NULL, N_( "Songhai" ) },
+         // language code: sot st
+         { "sot", "st", N_( "Sotho, Southern" ) },
+         // language code: spa es
+         { "spa", "es", N_( "Spanish" ) },
+         // language code: srd sc
+         { "srd", "sc", N_( "Sardinian" ) },
+         // language code: srr
+         { "srr", NULL, N_( "Serer" ) },
+         // language code: ssa
+         { "ssa", NULL, N_( "Nilo-Saharan (Other)" ) },
+         // language code: ssw ss
+         { "ssw", "ss", N_( "Swati" ) },
+         // language code: suk
+         { "suk", NULL, N_( "Sukuma" ) },
+         // language code: sun su
+         { "sun", "su", N_( "Sundanese" ) },
+         // language code: sus
+         { "sus", NULL, N_( "Susu" ) },
+         // language code: sux
+         { "sux", NULL, N_( "Sumerian" ) },
+         // language code: swa sw
+         { "swa", "sw", N_( "Swahili" ) },
+         // language code: swe sv
+         { "swe", "sv", N_( "Swedish" ) },
+         // language code: syr
+         { "syr", NULL, N_( "Syriac" ) },
+         // language code: tah ty
+         { "tah", "ty", N_( "Tahitian" ) },
+         // language code: tai
+         { "tai", NULL, N_( "Tai (Other)" ) },
+         // language code: tam ta
+         { "tam", "ta", N_( "Tamil" ) },
+         // language code: tat tt
+         { "tat", "tt", N_( "Tatar" ) },
+         // language code: tel te
+         { "tel", "te", N_( "Telugu" ) },
+         // language code: tem
+         { "tem", NULL, N_( "Timne" ) },
+         // language code: ter
+         { "ter", NULL, N_( "Tereno" ) },
+         // language code: tet
+         { "tet", NULL, N_( "Tetum" ) },
+         // language code: tgk tg
+         { "tgk", "tg", N_( "Tajik" ) },
+         // language code: tgl tl
+         { "tgl", "tl", N_( "Tagalog" ) },
+         // language code: tha th
+         { "tha", "th", N_( "Thai" ) },
+         // language code: tib bod bo
+         { "tib", "bo", N_( "Tibetan" ) },
+         // language code: tib bod bo
+         { "bod", NULL, N_( "Tibetan" ) },
+         // language code: tig
+         { "tig", NULL, N_( "Tigre" ) },
+         // language code: tir ti
+         { "tir", "ti", N_( "Tigrinya" ) },
+         // language code: tiv
+         { "tiv", NULL, N_( "Tiv" ) },
+         // language code: tkl
+         { "tkl", NULL, N_( "Tokelau" ) },
+         // language code: tlh
+         { "tlh", NULL, N_( "Klingon" ) },
+         // language code: tli
+         { "tli", NULL, N_( "Tlingit" ) },
+         // language code: tmh
+         { "tmh", NULL, N_( "Tamashek" ) },
+         // language code: tog
+         { "tog", NULL, N_( "Tonga (Nyasa)" ) },
+         // language code: ton to
+         { "ton", "to", N_( "Tonga (Tonga Islands)" ) },
+         // language code: tpi
+         { "tpi", NULL, N_( "Tok Pisin" ) },
+         // language code: tsi
+         { "tsi", NULL, N_( "Tsimshian" ) },
+         // language code: tsn tn
+         { "tsn", "tn", N_( "Tswana" ) },
+         // language code: tso ts
+         { "tso", "ts", N_( "Tsonga" ) },
+         // language code: tuk tk
+         { "tuk", "tk", N_( "Turkmen" ) },
+         // language code: tum
+         { "tum", NULL, N_( "Tumbuka" ) },
+         // language code: tup
+         { "tup", NULL, N_( "Tupi Languages" ) },
+         // language code: tur tr
+         { "tur", "tr", N_( "Turkish" ) },
+         // language code: tut
+         { "tut", NULL, N_( "Altaic (Other)" ) },
+         // language code: tvl
+         { "tvl", NULL, N_( "Tuvalu" ) },
+         // language code: twi tw
+         { "twi", "tw", N_( "Twi" ) },
+         // language code: tyv
+         { "tyv", NULL, N_( "Tuvinian" ) },
+         // language code: udm
+         { "udm", NULL, N_( "Udmurt" ) },
+         // language code: uga
+         { "uga", NULL, N_( "Ugaritic" ) },
+         // language code: uig ug
+         { "uig", "ug", N_( "Uighur" ) },
+         // language code: ukr uk
+         { "ukr", "uk", N_( "Ukrainian" ) },
+         // language code: umb
+         { "umb", NULL, N_( "Umbundu" ) },
+         // language code: und
+         { "und", NULL, N_( "Undetermined" ) },
+         // language code: urd ur
+         { "urd", "ur", N_( "Urdu" ) },
+         // language code: uzb uz
+         { "uzb", "uz", N_( "Uzbek" ) },
+         // language code: vai
+         { "vai", NULL, N_( "Vai" ) },
+         // language code: ven ve
+         { "ven", "ve", N_( "Venda" ) },
+         // language code: vie vi
+         { "vie", "vi", N_( "Vietnamese" ) },
+         // language code: vol vo
+         { "vol", "vo", N_( "Volapuk" ) },
+         // language code: vot
+         { "vot", NULL, N_( "Votic" ) },
+         // language code: wak
+         { "wak", NULL, N_( "Wakashan Languages" ) },
+         // language code: wal
+         { "wal", NULL, N_( "Walamo" ) },
+         // language code: war
+         { "war", NULL, N_( "Waray" ) },
+         // language code: was
+         { "was", NULL, N_( "Washo" ) },
+         // language code: wel cym cy
+         { "wel", "cy", N_( "Welsh" ) },
+         // language code: wel cym cy
+         { "cym", NULL, N_( "Welsh" ) },
+         // language code: wen
+         { "wen", NULL, N_( "Sorbian Languages" ) },
+         // language code: wln wa
+         { "wln", "wa", N_( "Walloon" ) },
+         // language code: wol wo
+         { "wol", "wo", N_( "Wolof" ) },
+         // language code: xal
+         { "xal", NULL, N_( "Kalmyk" ) },
+         // language code: xho xh
+         { "xho", "xh", N_( "Xhosa" ) },
+         // language code: yao
+         { "yao", NULL, N_( "Yao" ) },
+         // language code: yap
+         { "yap", NULL, N_( "Yapese" ) },
+         // language code: yid yi
+         { "yid", "yi", N_( "Yiddish" ) },
+         // language code: yor yo
+         { "yor", "yo", N_( "Yoruba" ) },
+         // language code: ypk
+         { "ypk", NULL, N_( "Yupik Languages" ) },
+         // language code: zap
+         { "zap", NULL, N_( "Zapotec" ) },
+         // language code: zen
+         { "zen", NULL, N_( "Zenaga" ) },
+         // language code: zha za
+         { "zha", "za", N_( "Zhuang" ) },
+         // language code: znd
+         { "znd", NULL, N_( "Zande" ) },
+         // language code: zul zu
+         { "zul", "zu", N_( "Zulu" ) },
+         // language code: zun
+         { "zun", NULL, N_( "Zuni" ) },
+
+         { NULL, NULL, NULL }
+      };
+
+      for (const LangInit * i = langInit; i->iso639_2 != NULL; ++i)
+      {
+         std::string name( i->name );
+         codes[i->iso639_2] = name;
+         if (i->iso639_1 != NULL)
+             codes[i->iso639_1] = name;
+      }
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/LanguageCode.h b/zypp/LanguageCode.h
new file mode 100644 (file)
index 0000000..d9f984d
--- /dev/null
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/LanguageCode.h
+ *
+*/
+#ifndef ZYPP_LANGUAGECODE_H
+#define ZYPP_LANGUAGECODE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class LanguageCode;
+  inline bool operator==( const LanguageCode & lhs, const LanguageCode & rhs );
+  inline bool operator!=( const LanguageCode & lhs, const LanguageCode & rhs );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : LanguageCode
+  //
+  /** Language codes (iso639_2/iso639_1).
+   *
+   * In fact the class will not prevent to use a non iso language code.
+   * Just a warning will appear in the log.
+  */
+  class LanguageCode
+  {
+    friend std::ostream & operator<<( std::ostream & str, const LanguageCode & obj );
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor */
+    LanguageCode();
+
+    /** Ctor taking a string. */
+    explicit
+    LanguageCode( const std::string & code_r );
+
+    /** Dtor */
+    ~LanguageCode();
+
+  public:
+    /** \name LanguageCode constants. */
+    //@{
+    /** No or empty code. */
+    static const LanguageCode noCode;
+    //@}
+
+  public:
+    /** Return the language code. */
+    std::string code() const;
+
+    /** Return the language name; if not available the language code. */
+    std::string name() const;
+
+    /** <tt>*this != noCode</tt>. */
+    inline bool hasCode() const
+    { return *this != noCode; }
+
+  private:
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates LanguageCode Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const LanguageCode & obj )
+  { return str << obj.code(); }
+
+  /** Comparison based on string value. */
+  //@{
+  /** \relates LanguageCode */
+  inline bool operator==( const LanguageCode & lhs, const LanguageCode & rhs ) {
+    return( lhs.code() == rhs.code() );
+  }
+  /** \relates LanguageCode */
+  inline bool operator==( const std::string & lhs, const LanguageCode & rhs ) {
+    return( lhs == rhs.code() );
+  }
+  /** \relates LanguageCode */
+  inline bool operator==( const LanguageCode & lhs, const std::string & rhs ) {
+    return( lhs.code() == rhs );
+  }
+
+  /** \relates LanguageCode */
+  inline bool operator!=( const LanguageCode & lhs, const LanguageCode & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates LanguageCode */
+  inline bool operator!=( const std::string & lhs, const LanguageCode & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates LanguageCode */
+  inline bool operator!=( const LanguageCode & lhs, const std::string & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  //@}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+  /** \relates zypp::LanguageCode Default order for std::container based on code string value.*/
+  template<>
+    inline bool less<zypp::LanguageCode>::operator()( const zypp::LanguageCode & lhs, const zypp::LanguageCode & rhs ) const
+    { return lhs.code() < rhs.code(); }
+  /////////////////////////////////////////////////////////////////
+} // namespace std
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_LANGUAGECODE_H
diff --git a/zypp/Locale.cc b/zypp/Locale.cc
new file mode 100644 (file)
index 0000000..d564ee1
--- /dev/null
@@ -0,0 +1,246 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Locale.cc
+ *
+*/
+#include <iostream>
+#include <map>
+
+#include "zypp/Locale.h"
+#include "zypp/ZConfig.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  typedef std::map<std::string, std::string> OtherDefaultLanguage;
+  static OtherDefaultLanguage otherDefaultLanguage;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Locale::Impl
+  //
+  /** Locale implementation. */
+  struct Locale::Impl
+  {
+    Impl()
+    {}
+
+    Impl( const std::string & code_r )
+    {
+      std::string t;
+      std::string::size_type sep = code_r.find_first_of( "@." );
+      if ( sep == std::string::npos ) {
+        t = code_r;
+      } else {
+        t = code_r.substr( 0, sep );
+      }
+
+      sep = t.find( '_' );
+      if ( sep == std::string::npos ) {
+        _language = LanguageCode( t );
+      } else {
+        _language = LanguageCode( t.substr( 0, sep ) );
+        _country = CountryCode( t.substr( sep+1 ) );
+      }
+    }
+
+    Impl( const LanguageCode & language_r,
+          const CountryCode & country_r )
+    : _language( language_r )
+    , _country( country_r )
+    {}
+
+    const LanguageCode & language() const
+    { return _language; }
+
+    const CountryCode & country() const
+    { return _country; }
+
+    std::string code() const
+    {
+      std::string ret( _language.code() );
+      if ( _country.hasCode() )
+        ret += "_" + _country.code();
+      return ret;
+    }
+
+    std::string name() const
+    {
+      std::string ret( _language.name() );
+      if ( _country.hasCode() )
+        ret += " (" + _country.name() + ")";
+      return ret;
+    }
+
+    Locale fallback() const
+    {
+      if (otherDefaultLanguage.size() == 0) {
+         // initial inserting map
+         otherDefaultLanguage["pt_BR"] = "en";
+      }
+
+      if (otherDefaultLanguage.find(code()) != otherDefaultLanguage.end())
+         return LanguageCode(otherDefaultLanguage[code()]);
+
+      if ( _country.hasCode() )
+        return _language;
+
+      if ( _language.hasCode() && _language != LanguageCode("en") )
+        return LanguageCode("en");
+
+      return Locale();
+    }
+
+  private:
+
+    LanguageCode _language;
+    CountryCode _country;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Locale::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Locale::Impl & obj )
+  {
+    return str << "Locale::Impl";
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Locale
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const Locale Locale::noCode;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::Locale
+  //   METHOD TYPE : Ctor
+  //
+  Locale::Locale()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::Locale
+  //   METHOD TYPE : Ctor
+  //
+  Locale::Locale( IdString code_r )
+  : _pimpl( new Impl( code_r.asString() ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::Locale
+  //   METHOD TYPE : Ctor
+  //
+  Locale::Locale( const std::string & code_r )
+  : _pimpl( new Impl( code_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::Locale
+  //   METHOD TYPE : Ctor
+  //
+  Locale::Locale( const char * code_r )
+  : _pimpl( new Impl( C_Str(code_r).c_str() ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::Locale
+  //   METHOD TYPE : Ctor
+  //
+  Locale::Locale( const LanguageCode & language_r,
+                  const CountryCode & country_r )
+  : _pimpl( new Impl( language_r, country_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::~Locale
+  //   METHOD TYPE : Dtor
+  //
+  Locale::~Locale()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::
+  //   METHOD TYPE :
+  //
+  const LanguageCode & Locale::language() const
+  { return _pimpl->language(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::
+  //   METHOD TYPE :
+  //
+  const CountryCode & Locale::country() const
+  { return _pimpl->country(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::
+  //   METHOD TYPE :
+  //
+  std::string Locale::code() const
+  { return _pimpl->code(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Locale::
+  //   METHOD TYPE :
+  //
+  std::string Locale::name() const
+  { return _pimpl->name(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    METHOD NAME : Locale::
+  //    METHOD TYPE :
+  //
+  Locale Locale::fallback() const
+  { return _pimpl->fallback(); }
+
+
+  ///////////////////////////////////////////////////////////////////
+
+  Locale Locale::bestMatch( const LocaleSet & avLocales_r, const Locale & requested_r )
+  {
+    if ( ! avLocales_r.empty() )
+    {
+      for ( Locale check( requested_r == noCode ? ZConfig::instance().textLocale() : requested_r );
+            check != noCode; check = check.fallback() )
+      {
+        if ( avLocales_r.find( check ) != avLocales_r.end() )
+          return check;
+      }
+    }
+    return noCode;
+  }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Locale.h b/zypp/Locale.h
new file mode 100644 (file)
index 0000000..bcc46bc
--- /dev/null
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Locale.h
+ *
+*/
+#ifndef ZYPP_LOCALE_H
+#define ZYPP_LOCALE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/IdString.h"
+#include "zypp/LanguageCode.h"
+#include "zypp/CountryCode.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class Locale;
+  typedef std::tr1::unordered_set<Locale> LocaleSet;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Locale
+  //
+  /**
+   * \todo migrate to IdString
+  */
+  class Locale
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Locale & obj );
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor */
+    Locale();
+
+    /** Ctor taking a string. */
+    explicit
+    Locale( IdString code_r );
+
+    explicit
+    Locale( const std::string & code_r );
+
+    explicit
+    Locale( const char * code_r );
+
+    /** Ctor taking LanguageCode and optional CountryCode. */
+    Locale( const LanguageCode & language_r,
+            const CountryCode & country_r = CountryCode() );
+
+    /** Dtor */
+    ~Locale();
+
+  public:
+    /** \name Locale constants. */
+    //@{
+    /** No or empty code. */
+    static const Locale noCode;
+    //@}
+
+  public:
+    /** */
+    const LanguageCode & language() const;
+    /** */
+    const CountryCode & country() const;
+
+    /** Return the locale code. */
+    std::string code() const;
+
+    /** Return the name made of language and country name. */
+    std::string name() const;
+
+    /** Return a fallback locale for this locale, when giving up, returns empty Locale() */
+    Locale fallback() const;
+
+  public:
+
+    /** Return the best match for \ref Locale \c requested_r within the available \c avLocales_r.
+     *
+     * If \c requested_r is nor specified or equals \ref Locale::noCode,
+     * \ref ZConfig::textLocale is assumed.
+     *
+     * If neither \c requested_r nor any of it's \ref fallback locales
+     * are available, \ref Locale::noCode is returned.
+    */
+    static Locale bestMatch( const LocaleSet & avLocales_r, const Locale & requested_r = Locale() );
+
+  private:
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Locale Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Locale & obj )
+  { return str << obj.code(); }
+
+  /** Comparison based on string value. */
+  //@{
+  /** \relates Locale */
+  inline bool operator==( const Locale & lhs, const Locale & rhs ) {
+    return( lhs.code() == rhs.code() );
+  }
+  /** \relates Locale */
+  inline bool operator==( const std::string & lhs, const Locale & rhs ) {
+    return( lhs == rhs.code() );
+  }
+  /** \relates Locale */
+  inline bool operator==( const Locale & lhs, const std::string & rhs ) {
+    return( lhs.code() == rhs );
+  }
+
+  /** \relates Locale */
+  inline bool operator!=( const Locale & lhs, const Locale & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates Locale */
+  inline bool operator!=( const std::string & lhs, const Locale & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  /** \relates Locale */
+  inline bool operator!=( const Locale & lhs, const std::string & rhs ) {
+    return( ! operator==( lhs, rhs ) );
+  }
+  //@}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+namespace std { namespace tr1 {
+  /** \relates ::zypp::Locale hash function */
+  template<> struct hash< ::zypp::Locale>
+  {
+    size_t operator()( const ::zypp::Locale & __s ) const
+    { return hash<std::string>()(__s.code()); }
+  };
+}}
+
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+  /** \relates zypp::Locale Default order for std::container based on code string value.*/
+  template<>
+    inline bool less<zypp::Locale>::operator()( const zypp::Locale & lhs, const zypp::Locale & rhs ) const
+    { return lhs.code() < rhs.code(); }
+  /////////////////////////////////////////////////////////////////
+} // namespace std
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_LOCALE_H
diff --git a/zypp/Locks.cc b/zypp/Locks.cc
new file mode 100644 (file)
index 0000000..721a1cc
--- /dev/null
@@ -0,0 +1,474 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <set>
+#include <fstream>
+#include <boost/function.hpp>
+#include <boost/function_output_iterator.hpp>
+#include <algorithm>
+
+#include "zypp/base/Regex.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/PoolItem.h"
+#include "zypp/PoolQueryUtil.tcc"
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/sat/SolvAttr.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/PathInfo.h"
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "locks"
+
+#include "zypp/Locks.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::str;
+
+namespace zypp
+{
+
+Locks& Locks::instance()
+{
+  static Locks _instance;
+  return _instance;
+}
+
+class Locks::Impl
+{
+public:
+  LockList locks;
+  LockList toAdd;
+  LockList toRemove;
+  bool     locksDirty;
+
+  bool mergeList(callback::SendReport<SavingLocksReport>& report);
+  
+  Impl():locksDirty(false){}
+};
+
+Locks::Locks() : _pimpl(new Impl){}
+
+Locks::const_iterator Locks::begin() const
+{ return _pimpl->locks.begin(); }
+
+Locks::const_iterator Locks::end() const
+{ return _pimpl->locks.end(); }
+
+Locks::LockList::size_type Locks::size() const
+{ return _pimpl->locks.size(); }
+
+bool Locks::empty() const
+{ return _pimpl->locks.empty(); }
+
+struct ApplyLock
+{
+  void operator()(const PoolQuery& query) const
+  {
+    for_( it,query.begin(),query.end() )
+    {
+      PoolItem item(*it);
+      item.status().setLock(true,ResStatus::USER);
+      DBG << "lock "<< item.resolvable()->name();
+    }
+  }
+};
+
+/**
+ * iterator that takes lock, lock all solvables from query 
+ * and send query to output iterator
+ */
+template <class OutputIterator>
+struct LockingOutputIterator
+{
+  LockingOutputIterator(OutputIterator& out_)
+    : out(out_)
+    {}
+
+  void operator()(const PoolQuery& query) const
+  {
+    ApplyLock a;a(query);
+    *out++ = query;
+  }
+  
+  private:
+  OutputIterator& out;
+};
+
+void Locks::readAndApply( const Pathname& file )
+{
+  MIL << "read and apply locks from "<<file << endl;
+  PathInfo pinfo(file);
+  if ( pinfo.isExist() )
+  {
+    insert_iterator<LockList> ii( _pimpl->locks,
+        _pimpl->locks.end() );
+    LockingOutputIterator<insert_iterator<LockList> > lout(ii);
+    readPoolQueriesFromFile( file, boost::make_function_output_iterator(lout) );
+  }
+  else
+    MIL << "file not exist(or cannot be stat), no lock added." << endl;
+
+}
+
+void Locks::read( const Pathname& file )
+{
+  MIL << "read locks from "<<file << endl;
+  PathInfo pinfo(file);
+  if ( pinfo.isExist() )
+    readPoolQueriesFromFile(
+       file, insert_iterator<LockList>(_pimpl->locks, _pimpl->locks.end()) );
+  else 
+    MIL << "file not exist(or cannot be stat), no lock added." << endl;
+}
+
+
+void Locks::apply() const
+{ 
+  DBG << "apply locks" << endl;
+  for_each(begin(), end(), ApplyLock());
+}
+
+
+void Locks::addLock( const PoolQuery& query )
+{
+  MIL << "add new lock" << endl;
+  for_( it,query.begin(),query.end() )
+  {
+    PoolItem item(*it);
+    item.status().setLock(true,ResStatus::USER);
+  }
+  LockList::iterator i = find(_pimpl->toRemove.begin(),
+    _pimpl->toRemove.end(), query);
+  if ( i != _pimpl->toRemove.end() )
+  {
+    DBG << "query removed from toRemove" << endl;
+    _pimpl->toRemove.erase(i);
+  }
+  else
+  {
+    DBG << "query added as new" << endl;
+    _pimpl->toAdd.push_back( query );
+  }
+}
+
+void Locks::addLock( const IdString& ident_r )
+{
+  sat::Solvable::SplitIdent id(ident_r);
+  addLock(id.kind(),id.name());
+}
+
+void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
+{
+  addLock(kind_r,IdString(name_r));
+}
+
+void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
+{
+  PoolQuery q;
+  q.addAttribute( sat::SolvAttr::name,name_r.asString() );
+  q.addKind( kind_r );
+  q.setMatchExact();
+  q.setCaseSensitive(true);
+  DBG << "add lock by identifier" << endl;
+  addLock( q );
+}
+
+void Locks::removeLock( const PoolQuery& query )
+{
+  MIL << "remove lock" << endl;
+  for_( it,query.begin(),query.end() )
+  {
+    PoolItem item(*it);
+    item.status().setLock(false,ResStatus::USER);
+  }
+  
+  LockList::iterator i = find(_pimpl->toAdd.begin(),
+    _pimpl->toAdd.end(), query);
+  if ( i != _pimpl->toAdd.end() )
+  {
+    DBG << "query removed from added" << endl;
+    _pimpl->toAdd.erase(i);
+  }
+  else
+  {
+    DBG << "needed remove some old lock" << endl;
+    _pimpl->toRemove.push_back( query );
+  }
+}
+
+void Locks::removeLock( const IdString& ident_r )
+{
+  sat::Solvable::SplitIdent id(ident_r);
+  removeLock(id.kind(),id.name());
+}
+
+void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
+{
+  removeLock(kind_r,IdString(name_r));
+}
+
+void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
+{
+  PoolQuery q;
+  q.addAttribute( sat::SolvAttr::name,name_r.asString() );
+  q.addKind( kind_r );
+  q.setMatchExact();
+  q.setCaseSensitive(true);
+  q.requireAll();
+  DBG << "remove lock by selectactable" << endl;
+  removeLock(q);
+}
+
+bool Locks::existEmpty() const
+{
+  for_( it, _pimpl->locks.begin(), _pimpl->locks.end() )
+  {
+    if( it->empty() )
+      return true;
+  }
+
+  return false;
+}
+
+//handle locks during removing
+class LocksCleanPredicate{
+private:
+  bool skip_rest;
+  size_t searched;
+  size_t all;
+  callback::SendReport<CleanEmptyLocksReport> &report;
+
+public:
+  LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
+
+  bool aborted(){ return skip_rest; }
+
+  bool operator()(PoolQuery& q)
+  {
+    if( skip_rest )
+      return false;
+    searched++;
+    if( !q.empty() )
+      return false;
+
+    if (!report->progress((100*searched)/all))
+    {
+      skip_rest = true;
+      return false;
+    }
+
+    switch (report->execute(q))
+    {
+    case CleanEmptyLocksReport::ABORT:
+      report->finish(CleanEmptyLocksReport::ABORTED);
+      skip_rest = true;
+      return false;
+    case CleanEmptyLocksReport::DELETE:
+      return true;
+    case CleanEmptyLocksReport::IGNORE:
+      return false;
+    default:
+      WAR << "Unknown returned value. Callback have more value then"
+          << " this switch. Need correct handle all enum values." << std::endl;
+    }
+
+    return false;
+  }
+
+};
+
+void Locks::removeEmpty()
+{
+  MIL << "cleaning of locks" << endl;
+  callback::SendReport<CleanEmptyLocksReport> report;
+  report->start();
+  size_t sum = _pimpl->locks.size();
+  LocksCleanPredicate p(sum, report);
+
+  _pimpl->locks.remove_if(p);
+
+  if( p.aborted() )
+  {
+    MIL << "cleaning aborted" << endl;
+    report->finish(CleanEmptyLocksReport::ABORTED);
+  }
+  else 
+  {
+    report->finish(CleanEmptyLocksReport::NO_ERROR);
+
+  }
+
+  if ( sum != _pimpl->locks.size() ) //some locks has been removed
+    _pimpl->locksDirty = true;
+}
+
+class LocksRemovePredicate
+{
+private:
+  std::set<sat::Solvable>& solvs;
+  const PoolQuery& query;
+  callback::SendReport<SavingLocksReport>& report;
+  bool aborted_;
+
+  //1 for subset of set, 2 only intersect, 0 for not intersect
+  int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
+  {
+    bool intersect = false;
+    for_( it,q.begin(),q.end() )
+    {
+      if ( s.find(*it)!=s.end() )
+      {
+        intersect = true;
+      }
+      else
+      {
+        if (intersect)
+          return 2;
+      }
+    }
+    return intersect ? 1 : 0;
+  }
+
+public:
+  LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
+      callback::SendReport<SavingLocksReport>& r)
+      : solvs(s), query(q),report(r),aborted_(false) {}
+
+  bool operator()(const PoolQuery& q)
+  {
+    if (aborted())
+      return false;
+    if( q==query )
+    {//identical
+      DBG << "identical queries" << endl;
+      return true;
+    }
+
+    SavingLocksReport::ConflictState cs;
+    switch( contains(q,solvs) )
+    {
+    case 0:
+      return false; //another lock
+    case 1:
+      cs = SavingLocksReport::SAME_RESULTS;
+      break;
+    case 2:
+      cs = SavingLocksReport::INTERSECT;
+      break;
+    default:
+      return true;
+    }
+    MIL << "find conflict: " << cs << endl;
+    switch (report->conflict(q,cs))
+    {
+    case SavingLocksReport::ABORT:
+      aborted_ = true;
+      DBG << "abort merging" << endl;
+      return false;
+    case SavingLocksReport::DELETE:
+      DBG << "force delete" << endl;
+      return true;
+    case SavingLocksReport::IGNORE:
+      DBG << "skip lock" << endl;
+      return false;
+    }
+    WAR << "should not reached, some state is missing" << endl;
+    return false;
+  }
+
+  bool aborted(){ return aborted_; }
+};
+
+bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
+{
+  MIL << "merging list old: " << locks.size()
+    << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
+  for_(it,toRemove.begin(),toRemove.end())
+  {
+    std::set<sat::Solvable> s(it->begin(),it->end());
+    locks.remove_if(LocksRemovePredicate(s,*it, report));
+  }
+
+  if (!report->progress())
+    return false;
+
+  for_( it, toAdd.begin(), toAdd.end() )
+  {
+    if( std::find( locks.begin(), locks.end(), *it ) == locks.end() )
+      locks.push_back( *it );
+  }
+
+  toAdd.clear();
+  toRemove.clear();
+
+  return true;
+}
+
+void Locks::merge()
+{
+  if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
+  {
+    return; //nothing to merge
+  }
+
+  callback::SendReport<SavingLocksReport> report;
+  report->start();
+  if (!_pimpl->mergeList(report))
+  {
+    report->finish(SavingLocksReport::ABORTED);
+    return;
+  }
+  DBG << "locks merged" << endl;
+  report->finish(SavingLocksReport::NO_ERROR);
+  _pimpl->locksDirty = true;
+}
+
+void Locks::save( const Pathname& file )
+{
+  if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
+      && !_pimpl->locksDirty )
+  {
+    DBG << "nothing changed in locks - no write to file" << endl;
+    return;
+  }
+
+  callback::SendReport<SavingLocksReport> report;
+  report->start();
+
+  if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
+  {
+    if (!_pimpl->mergeList(report))
+    {
+      report->finish(SavingLocksReport::ABORTED);
+      return;
+    }
+  }
+
+  DBG << "writed "<< _pimpl->locks.size() << "locks" << endl;
+  writePoolQueriesToFile( file, _pimpl->locks.begin(), _pimpl->locks.end() );
+  report->finish(SavingLocksReport::NO_ERROR);
+}
+
+void Locks::removeDuplicates()
+{
+  size_type sum = size();
+  for_(it,_pimpl->locks.begin(),_pimpl->locks.end())
+  {
+    if ( find(_pimpl->locks.begin(),it,*it) != it )
+      _pimpl->locks.erase(it--); //-- to avoid using break iterator
+  }
+  
+  if (sum!=size())
+    _pimpl->locksDirty = true;
+}
+
+} // ns zypp
diff --git a/zypp/Locks.h b/zypp/Locks.h
new file mode 100644 (file)
index 0000000..1e18b00
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef ZYPP_LOCKS_H
+#define ZYPP_LOCKS_H
+
+#include "zypp/ResPool.h"
+#include "zypp/Pathname.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/ZConfig.h"
+namespace zypp
+{
+  /** \name Locks */
+  //@{
+  /**
+   * Singleton class which manipulate with locks file and apply locks on pool.
+   * for user information about locksfile and its format see 
+   * <a>http://en.opensuse.org/Libzypp/Locksfile</a>
+   */
+  class Locks
+  {
+  public:
+    typedef std::list<PoolQuery> LockList;
+    typedef LockList::const_iterator const_iterator;
+    typedef LockList::size_type size_type;
+  public:
+    class Impl;
+
+    /**
+     * Gets instance of this class.
+     * Singleton method.
+     */
+    static Locks& instance();
+
+    const_iterator begin() const;
+    const_iterator end() const;
+    LockList::size_type size() const;
+    bool empty() const;
+
+    /**
+     * TODO add:
+     * toBeAdded{Begin,End,Size,Empty}
+     * toBeRemoved{Begin,End,Size,Empty}
+     */
+
+    /**
+     * locks result of query and add this lock as toAdd
+     */
+    void addLock( const PoolQuery& query );
+
+    /**
+     * add lock by identifier (e.g. Selectable->ident()
+     * and add this lock as toAdd
+     */
+    void addLock( const IdString& ident_r );
+
+    /**
+     * add lock by name and kind and
+     * add this lock as toAdd
+     */ 
+    void addLock( const ResKind& kind_r, const IdString& name_r );
+
+    /**
+     * add lock by name and kind and
+     * add this lock as toAdd
+     */ 
+    void addLock( const ResKind& kind_r, const C_Str& name_r );
+
+    /**
+     * unlocks by result of query and add to toRemove.
+     *
+     * If unlock non-saved lock (so he is in toAdd list) then both is deleted 
+     * and nathing happen during save
+     */
+    void removeLock( const PoolQuery& query );
+
+    /**
+     * remove lock by identifier (e.g. Selectable->ident()
+     *
+     * If unlock non-saved lock (so he is in toAdd list) then both is deleted 
+     * and nathing happen during save
+     */
+    void removeLock( const IdString& ident_r );
+
+    /**
+     * remove lock by name and kind
+     *
+     * If unlock non-saved lock (so he is in toAdd list) then both is deleted 
+     * and nathing happen during save
+     */
+    void removeLock( const ResKind& kind_r, const IdString& name_r );
+    void removeLock( const ResKind& kind_r, const C_Str & name_r );
+
+    /**
+     * Optimalized version of read and apply.
+     * \see read
+     * \see apply
+     */
+    void readAndApply( const Pathname& file = ZConfig::instance().locksFile() );
+
+    /**
+     * Read locks from file to list of stable locks (locks which is not changed
+     * during session)
+     */
+    void read( const Pathname& file = ZConfig::instance().locksFile() );
+
+    /**
+     * Applies locks in stable list (locks which is not changed during session).
+     */
+    void apply() const;
+
+    /**
+     * Merges toAdd and ToRemove list to stable list and
+     * save that stable list to file.
+     * \see SavingLocksReport
+     */
+    void save( const Pathname& file = ZConfig::instance().locksFile() );
+
+    /**
+     * Merges toAdd and ToRemove list to stable list.
+     * \note Can call callback if problem during merging occure
+     * \see SavingLocksReport
+     */
+    void merge();
+    
+    /**
+     * Gets true if some lock doesn't lock any object in pool
+     * This can happen e.g. if package is removed or
+     * due to user bad definition of lock
+     */
+    bool existEmpty() const;
+
+    /**
+     * Call callback for each empty lock.
+     * \see existEmpty
+     * \see CleanEmptyLocksReport
+     * \note you must call \a save to write cleaned locks to file
+     */
+    void removeEmpty();
+
+    /**
+     * Delete all query duplicate in loaded locks.
+     * \note you must call \a save to write cleaned locks to file
+     */
+    void removeDuplicates();
+    //@}
+  private:
+    Locks();
+    
+    RW_pointer<Impl, rw_pointer::Scoped<Impl> > _pimpl;
+
+  };
+}
+    
+#endif
diff --git a/zypp/ManagedFile.h b/zypp/ManagedFile.h
new file mode 100644 (file)
index 0000000..95e4828
--- /dev/null
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ManagedFile.h
+ *
+*/
+#ifndef ZYPP_MANAGEDFILE_H
+#define ZYPP_MANAGEDFILE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/AutoDispose.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** A Pathname plus associated cleanup code to be executed when
+   *  path is no longer needed.
+   */
+  typedef AutoDispose<const Pathname> ManagedFile;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MANAGEDFILE_H
diff --git a/zypp/MediaProducts.cc b/zypp/MediaProducts.cc
new file mode 100644 (file)
index 0000000..29d9826
--- /dev/null
@@ -0,0 +1,27 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp"
+
+#include "zypp/MediaProducts.h"
+
+using namespace std;
+
+namespace zypp
+{
+
+// template<class _OutputIterator>
+// void productsInMedia( const Url & url_r, _OutputIterator result )
+
+
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/MediaProducts.h b/zypp/MediaProducts.h
new file mode 100644 (file)
index 0000000..2a6bd9b
--- /dev/null
@@ -0,0 +1,125 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/MediaProducts.h
+ * Functions to find out products in media
+ */
+#ifndef ZYPP_MEDIAPRODUCTS_H_
+#define ZYPP_MEDIAPRODUCTS_H_
+
+#include <iterator>
+#include <iostream>
+#include <fstream>
+#include "zypp/ZConfig.h"
+#include "zypp/base/Logger.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/ProgressData.h"
+
+namespace zypp
+{
+  /**
+   * \short Represents an available product in media
+   */
+  struct MediaProductEntry
+  {
+    Pathname    _dir;
+    std::string _name;
+
+    /**
+     * \short Ctor
+     */
+    MediaProductEntry( const Pathname & dir_r = "/", const std::string & name_r = std::string() )
+      : _dir(dir_r), _name(name_r)
+    {
+    }
+
+    bool operator<( const MediaProductEntry &rhs ) const
+    {
+      return ( _name < rhs._name );
+    }
+  };
+
+  /**
+   * A set of available products in media
+   */
+  typedef std::set<MediaProductEntry> MediaProductSet;
+
+  /**
+   * FIXME: add a comment here...
+   */
+  template <class _OutputIterator>
+  static void scanProductsFile( const Pathname & file_r, _OutputIterator result )
+  {
+    std::ifstream pfile( file_r.asString().c_str() );
+    while ( pfile.good() ) {
+
+      std::string value = str::getline( pfile, str::TRIM );
+      if ( pfile.bad() ) {
+        ERR << "Error parsing " << file_r << std::endl;
+        ZYPP_THROW(Exception("Error parsing " + file_r.asString()));
+      }
+      if ( pfile.fail() ) {
+        break; // no data on last line
+      }
+      std::string tag = str::stripFirstWord( value, true );
+
+      if ( tag.size() ) {
+        *result = MediaProductEntry( tag, value );
+      }
+    }
+  }
+
+  /**
+   * \short Available products in a url location
+   *
+   * \param url_r url to inspect
+   * \param result output iterator where \ref MediaProductEntry
+   * items will be inserted.
+   * \throws MediaException If accessng the media fails
+   */
+  template <class _OutputIterator>
+  void productsInMedia( const Url & url_r, _OutputIterator result )
+  {
+    media::MediaManager media_mgr;
+    // open the media
+    media::MediaId id = media_mgr.open(url_r);
+    media_mgr.attach(id);
+    Pathname products_file = Pathname("media.1/products");
+
+    try  {
+      media_mgr.provideFile (id, products_file);
+      products_file = media_mgr.localPath (id, products_file);
+      scanProductsFile (products_file, result);
+    }
+    catch ( const Exception & excpt ) {
+      ZYPP_CAUGHT(excpt);
+      MIL << "No products description found on the Url" << std::endl;
+    }
+    media_mgr.release(id, "");
+ }
+
+ /**
+   * \short Available products in a url location
+   *
+   * \param url_r url to inspect
+   * \param set ef MediaProductEntry set where
+   * items will be inserted.
+   * \throws MediaException If accessng the media fails
+   */
+  void productsInMedia( const Url & url_r, MediaProductSet &set )
+  {
+    productsInMedia(url_r, std::inserter(set, set.end()));
+  }
+
+} // ns zypp
+
+#endif
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/MediaSetAccess.cc b/zypp/MediaSetAccess.cc
new file mode 100644 (file)
index 0000000..c063563
--- /dev/null
@@ -0,0 +1,445 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/PathInfo.h"
+//#include "zypp/source/MediaSetAccessReportReceivers.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(MediaSetAccess);
+
+///////////////////////////////////////////////////////////////////
+
+  MediaSetAccess::MediaSetAccess(const Url &url,
+                                 const Pathname & prefered_attach_point)
+      : _url(url)
+      , _prefAttachPoint(prefered_attach_point)
+  {}
+
+  MediaSetAccess::MediaSetAccess(const std::string & label_r,
+                                 const Url &url,
+                                 const Pathname & prefered_attach_point)
+      : _url(url)
+      , _prefAttachPoint(prefered_attach_point)
+      , _label( label_r )
+  {}
+
+  MediaSetAccess::~MediaSetAccess()
+  {
+    try
+    {
+      release();
+    }
+    catch(...) {} // don't let exception escape a dtor.
+  }
+
+
+  void MediaSetAccess::setVerifier( unsigned media_nr, media::MediaVerifierRef verifier )
+  {
+    if (_medias.find(media_nr) != _medias.end())
+    {
+      // the media already exists, set theverifier
+      media::MediaAccessId id = _medias[media_nr];
+      media::MediaManager media_mgr;
+      media_mgr.addVerifier( id, verifier );
+      // remove any saved verifier for this media
+      _verifiers.erase(media_nr);
+    }
+    else
+    {
+      // save the verifier in the map, and set it when
+      // the media number is first attached
+      _verifiers[media_nr] = verifier;
+    }
+  }
+
+  void MediaSetAccess::releaseFile( const OnMediaLocation & on_media_file )
+  {
+    releaseFile( on_media_file.filename(), on_media_file.medianr() );
+  }
+
+  void MediaSetAccess::releaseFile( const Pathname & file, unsigned media_nr)
+  {
+    media::MediaManager media_mgr;
+    media::MediaAccessId media;
+
+    media = getMediaAccessId( media_nr);
+    DBG << "Going to release file " << file
+        << " from media number " << media_nr << endl;
+
+    if ( ! media_mgr.isAttached(media) )
+      return; //disattached media is free
+
+    media_mgr.releaseFile (media, file);
+  }
+
+  void MediaSetAccess::dirInfo( filesystem::DirContent &retlist, const Pathname &dirname,
+                                bool dots, unsigned media_nr )
+  {
+    media::MediaManager media_mgr;
+    media::MediaAccessId media;
+    media = getMediaAccessId(media_nr);
+
+    // try to attach the media
+    if ( ! media_mgr.isAttached(media) )
+        media_mgr.attach(media);
+
+    media_mgr.dirInfo(media, retlist, dirname, dots);
+  }
+
+  struct ProvideFileOperation
+  {
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      media_mgr.provideFile(media, file);
+      result = media_mgr.localPath(media, file);
+    }
+  };
+
+  struct ProvideDirTreeOperation
+  {
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      media_mgr.provideDirTree(media, file);
+      result = media_mgr.localPath(media, file);
+    }
+  };
+
+  struct ProvideDirOperation
+  {
+    Pathname result;
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      media_mgr.provideDir(media, file);
+      result = media_mgr.localPath(media, file);
+    }
+  };
+
+  struct ProvideFileExistenceOperation
+  {
+    bool result;
+    ProvideFileExistenceOperation()
+        : result(false)
+    {}
+
+    void operator()( media::MediaAccessId media, const Pathname &file )
+    {
+      media::MediaManager media_mgr;
+      result = media_mgr.doesFileExist(media, file);
+    }
+  };
+
+
+
+  Pathname MediaSetAccess::provideFile( const OnMediaLocation & resource, ProvideFileOptions options, const Pathname &deltafile )
+  {
+    ProvideFileOperation op;
+    provide( boost::ref(op), resource, options, deltafile );
+    return op.result;
+  }
+
+  Pathname MediaSetAccess::provideFile(const Pathname & file, unsigned media_nr, ProvideFileOptions options )
+  {
+    OnMediaLocation resource;
+    ProvideFileOperation op;
+    resource.setLocation(file, media_nr);
+    provide( boost::ref(op), resource, options, Pathname() );
+    return op.result;
+  }
+
+  bool MediaSetAccess::doesFileExist(const Pathname & file, unsigned media_nr )
+  {
+    ProvideFileExistenceOperation op;
+    OnMediaLocation resource;
+    resource.setLocation(file, media_nr);
+    provide( boost::ref(op), resource, PROVIDE_DEFAULT, Pathname());
+    return op.result;
+  }
+
+  void MediaSetAccess::provide( ProvideOperation op,
+                                const OnMediaLocation &resource,
+                                ProvideFileOptions options,
+                                const Pathname &deltafile )
+  {
+    Pathname file(resource.filename());
+    unsigned media_nr(resource.medianr());
+
+    callback::SendReport<media::MediaChangeReport> report;
+    media::MediaManager media_mgr;
+
+    media::MediaAccessId media;
+
+    do
+    {
+      // get the mediaId, but don't try to attach it here
+      media = getMediaAccessId( media_nr);
+      bool deltafileset = false;
+
+      try
+      {
+        DBG << "Going to try to provide " << (resource.optional() ? "optional" : "") << " file " << file
+            << " from media number " << media_nr << endl;
+        // try to attach the media
+        if ( ! media_mgr.isAttached(media) )
+          media_mgr.attach(media);
+       media_mgr.setDeltafile(media, deltafile);
+       deltafileset = true;
+        op(media, file);
+       media_mgr.setDeltafile(media, Pathname());
+        break;
+      }
+      catch ( media::MediaException & excp )
+      {
+        ZYPP_CAUGHT(excp);
+       if (deltafileset)
+         media_mgr.setDeltafile(media, Pathname());
+        media::MediaChangeReport::Action user = media::MediaChangeReport::ABORT;
+        unsigned int devindex = 0;
+        vector<string> devices;
+        media_mgr.getDetectedDevices(media, devices, devindex);
+
+        do
+        {
+          if (user != media::MediaChangeReport::EJECT) // no use in calling this again
+          {
+            DBG << "Media couldn't provide file " << file << " , releasing." << endl;
+            try
+            {
+              media_mgr.release(media);
+            }
+            catch (const Exception & excpt_r)
+            {
+                ZYPP_CAUGHT(excpt_r);
+                MIL << "Failed to release media " << media << endl;
+            }
+          }
+
+          // set up the reason
+          media::MediaChangeReport::Error reason = media::MediaChangeReport::INVALID;
+
+          if( typeid(excp) == typeid( media::MediaFileNotFoundException )  ||
+              typeid(excp) == typeid( media::MediaNotAFileException ) )
+          {
+            reason = media::MediaChangeReport::NOT_FOUND;
+          }
+          else if( typeid(excp) == typeid( media::MediaNotDesiredException)  ||
+              typeid(excp) == typeid( media::MediaNotAttachedException) )
+          {
+            reason = media::MediaChangeReport::WRONG;
+          }
+          else if( typeid(excp) == typeid( media::MediaTimeoutException) ||
+                   typeid(excp) == typeid( media::MediaTemporaryProblemException))
+          {
+            reason = media::MediaChangeReport::IO_SOFT;
+          }
+
+          // non interactive only bother the user if wrong medium is in the drive
+          // otherwise propagate the error
+          if ( ( options & PROVIDE_NON_INTERACTIVE ) && reason != media::MediaChangeReport::WRONG)
+          {
+              MIL << "Can't provide file. Non-Interactive mode." << endl;
+              ZYPP_RETHROW(excp);
+          }
+          else
+          {
+            // release all media before requesting another (#336881)
+            media_mgr.releaseAll();
+
+            user = report->requestMedia (
+              _url,
+              media_nr,
+              _label,
+              reason,
+              excp.asUserString(),
+              devices,
+              devindex
+            );
+          }
+
+          MIL << "ProvideFile exception caught, callback answer: " << user << endl;
+
+          if( user == media::MediaChangeReport::ABORT )
+          {
+            DBG << "Aborting" << endl;
+            ZYPP_RETHROW ( excp );
+          }
+          else if ( user == media::MediaChangeReport::IGNORE )
+          {
+            DBG << "Skipping" << endl;
+           SkipRequestException nexcp("User-requested skipping of a file");
+           nexcp.remember(excp);
+           ZYPP_THROW(nexcp);
+         }
+          else if ( user == media::MediaChangeReport::EJECT )
+          {
+            DBG << "Eject: try to release" << endl;
+            media_mgr.releaseAll();
+            // eject
+            media_mgr.release (media,
+              devindex < devices.size() ? devices[devindex] : "");
+          }
+          else if ( user == media::MediaChangeReport::RETRY  ||
+            user == media::MediaChangeReport::CHANGE_URL )
+          {
+            // retry
+            DBG << "Going to try again" << endl;
+            // invalidate current media access id
+            media_mgr.close(media);
+            _medias.erase(media_nr);
+
+            // not attaching, media set will do that for us
+            // this could generate uncaught exception (#158620)
+            break;
+          }
+          else
+          {
+            DBG << "Don't know, let's ABORT" << endl;
+            ZYPP_RETHROW ( excp );
+          }
+        } while( user == media::MediaChangeReport::EJECT );
+      }
+
+      // retry or change URL
+    } while( true );
+  }
+
+  Pathname MediaSetAccess::provideDir(const Pathname & dir,
+                                      bool recursive,
+                                      unsigned media_nr,
+                                      ProvideFileOptions options )
+  {
+    OnMediaLocation resource;
+    resource.setLocation(dir, media_nr);
+    if ( recursive )
+    {
+        ProvideDirTreeOperation op;
+        provide( boost::ref(op), resource, options, Pathname());
+        return op.result;
+    }
+    ProvideDirOperation op;
+    provide( boost::ref(op), resource, options, Pathname());
+    return op.result;
+  }
+
+  media::MediaAccessId MediaSetAccess::getMediaAccessId (media::MediaNr medianr)
+  {
+    media::MediaManager media_mgr;
+
+    if (_medias.find(medianr) != _medias.end())
+    {
+      media::MediaAccessId id = _medias[medianr];
+      return id;
+    }
+    Url url;
+    url = rewriteUrl (_url, medianr);
+    media::MediaAccessId id = media_mgr.open(url, _prefAttachPoint);
+    _medias[medianr] = id;
+
+    try
+    {
+      if (_verifiers.find(medianr) != _verifiers.end())
+      {
+        // a verifier is set for this media
+        // FIXME check the case where the verifier exists
+        // but we have no access id for the media
+        media::MediaAccessId id = _medias[medianr];
+        media::MediaManager media_mgr;
+        media_mgr.delVerifier(id);
+        media_mgr.addVerifier( id, _verifiers[medianr] );
+        // remove any saved verifier for this media
+        _verifiers.erase(medianr);
+      }
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      WAR << "Verifier not found" << endl;
+    }
+
+    return id;
+  }
+
+
+  Url MediaSetAccess::rewriteUrl (const Url & url_r, const media::MediaNr medianr)
+  {
+    std::string scheme = url_r.getScheme();
+    if (scheme == "cd" || scheme == "dvd")
+      return url_r;
+
+    DBG << "Rewriting url " << url_r << endl;
+
+    if( scheme == "iso")
+    {
+      // TODO the iso parameter will not be required in the future, this
+      // code has to be adapted together with the MediaISO change.
+      // maybe some MediaISOURL interface should be used.
+      std::string isofile = url_r.getQueryParam("iso");
+      str::regex e("^(.*)(cd|dvd|media)[0-9]+\\.iso$", str::regex::icase);
+
+      str::smatch what;
+      if(str::regex_match(isofile, what, e))
+      {
+        Url url( url_r);
+        isofile = what[1] + what[2] + str::numstring(medianr) + ".iso";
+        url.setQueryParam("iso", isofile);
+        DBG << "Url rewrite result: " << url << endl;
+        return url;
+      }
+    }
+    else
+    {
+      std::string pathname = url_r.getPathName();
+      str::regex e("^(.*)(cd|dvd|media)[0-9]+(/)?$", str::regex::icase);
+      str::smatch what;
+      if(str::regex_match(pathname, what, e))
+      {
+        Url url( url_r);
+        pathname = what[1] + what[2] + str::numstring(medianr) + what[3];
+        url.setPathName(pathname);
+        DBG << "Url rewrite result: " << url << endl;
+        return url;
+      }
+    }
+    return url_r;
+  }
+
+  void MediaSetAccess::release()
+  {
+    DBG << "Releasing all media IDs held by this MediaSetAccess" << endl;
+    media::MediaManager manager;
+    for (MediaMap::const_iterator m = _medias.begin(); m != _medias.end(); ++m)
+      manager.release(m->second, "");
+  }
+
+  std::ostream & MediaSetAccess::dumpOn( std::ostream & str ) const
+  {
+    str << "MediaSetAccess (URL='" << _url << "', attach_point_hint='" << _prefAttachPoint << "')";
+    return str;
+  }
+
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/MediaSetAccess.h b/zypp/MediaSetAccess.h
new file mode 100644 (file)
index 0000000..bd9fb45
--- /dev/null
@@ -0,0 +1,311 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_MediaSetAccess_H
+#define ZYPP_MediaSetAccess_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <zypp/base/Function.h>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Flags.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/Pathname.h"
+#include "zypp/CheckSum.h"
+#include "zypp/OnMediaLocation.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    DEFINE_PTR_TYPE(MediaSetAccess);
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaSetAccess
+    //
+    /**
+     * Media access layer responsible for handling files distributed on a set
+     * of media with media change and abort/retry/ingore user callback handling.
+     *
+     * This is provided as a means to handle CD or DVD sets accessible through
+     * dir, iso, nfs or other URL schemes other than cd/dvd (see
+     * \ref MediaManager for info on different implemented media backends).
+     * Currently it handles URLs containing cdN, CDN, dvdN, and DVDN strings,
+     * where N is the number of particular media in the set.
+     *
+     * Examples:
+     * \code
+     * "iso:/?iso=/path/to/iso/images/openSUSE-10.3-Alpha2plus-DVD-x86_64-DVD1.iso"
+     * "dir:/path/to/cdset/sources/openSUSE-10.3/Alpha2plus/CD1"
+     * \endcode
+     *
+     * MediaSetAccess accesses files on desired media by rewriting
+     * the original URL, replacing the digit (usually) 1 with requested media
+     * number and uses \ref MediaManager to get the files from the new URL.
+     *
+     * Additionaly, each media number can be assined a media verifier which
+     * checks if the media we are trying to access is the desired one. See
+     * \ref MediaVerifierBase for more info.
+     *
+     * Code example:
+     * \code
+     * Url url("dir:/path/to/cdset/sources/openSUSE-10.3/Alpha2plus/CD1");
+     *
+     * MediaSetAccess access(url);
+     *
+     * access.setVerifier(1, media1VerifierRef);
+     * access.setVerifier(2, media2VerifierRef);
+     *
+     * Pathname file1 = "/some/file/on/media1";
+     * Pathname providedfile1 = access.provideFile(file1, 1);
+     * Pathname file2 = "/some/file/on/media2";
+     * Pathname providedfile2 = access.provideFile(file1, 2);
+     *
+     * \endcode
+     */
+    class MediaSetAccess : public base::ReferenceCounted, private base::NonCopyable
+    {
+      friend std::ostream & operator<<( std::ostream & str, const MediaSetAccess & obj );
+
+    public:
+      /**
+       * Creates a callback enabled media access for specified \a url.
+       *
+       * \param url
+       * \param prefered_attach_point Prefered attach (mount) point. Use, if
+       *        you want to mount the media to a specific directory.
+       */
+      MediaSetAccess( const Url &url, const Pathname & prefered_attach_point = "" );
+      /** \overload Also taking a \ref label. */
+      MediaSetAccess( const std::string & label_r, const Url &url, const Pathname & prefered_attach_point = "" );
+      ~MediaSetAccess();
+
+      /**
+       * Sets a \ref MediaVerifier verifier for given media number.
+       */
+      void setVerifier( unsigned media_nr, media::MediaVerifierRef verifier );
+
+      /**
+       * The label identifing this media set and to be sent in a media change request.
+       */
+      const std::string & label() const
+      { return _label; }
+
+      /**
+       * Set the label identifing this media set and to be sent in a media change request.
+       */
+      void setLabel( const std::string & label_r )
+      { _label = label_r; }
+
+      enum ProvideFileOption
+      {
+        /**
+         * The user is not asked anything, and the error
+         * exception is just propagated */
+        PROVIDE_DEFAULT = 0x0,
+        PROVIDE_NON_INTERACTIVE = 0x1
+      };
+      ZYPP_DECLARE_FLAGS(ProvideFileOptions,ProvideFileOption);
+
+      /**
+       * Provides a file from a media location.
+       *
+       * \param resource location of the file on media
+       * \return local pathname of the requested file
+       *
+       * \throws MediaException if a problem occured and user has chosen to
+       *         abort the operation. The calling code should take care
+       *         to quit the current operation.
+       * \throws SkipRequestException if a problem occured and user has chosen
+       *         to skip the current operation. The calling code should continue
+       *         with the next one, if possible.
+       *
+       *
+       * If the resource is marked as optional, no Exception is thrown
+       * and Pathname() is returned
+       *
+       * the optional deltafile argument describes a file that can
+       * be used for delta download algorithms.
+       *
+       * \note interaction with the user does not ocurr if
+       * \ref ProvideFileOptions::NON_INTERACTIVE is set.
+       *
+       * \note OnMediaLocation::optional() hint has no effect on the transfer.
+       *
+       * \see zypp::media::MediaManager::provideFile()
+       */
+      Pathname provideFile( const OnMediaLocation & resource, ProvideFileOptions options = PROVIDE_DEFAULT, const Pathname &deltafile = Pathname() );
+
+      /**
+       * Provides \a file from media \a media_nr.
+       *
+       * \param file path to the file relative to media URL
+       * \param media_nr the media number in the media set
+       * \return local pathname of the requested file
+       *
+       * \note interaction with the user does not ocurr if
+       * \ref ProvideFileOptions::NON_INTERACTIVE is set.
+       *
+       * \note OnMediaLocation::optional() hint has no effect on the transfer.
+       *
+       * \throws MediaException if a problem occured and user has chosen to
+       *         abort the operation. The calling code should take care
+       *         to quit the current operation.
+       * \throws SkipRequestException if a problem occured and user has chosen
+       *         to skip the current operation. The calling code should continue
+       *         with the next one, if possible.
+       * \see zypp::media::MediaManager::provideFile()
+       */
+      Pathname provideFile(const Pathname & file, unsigned media_nr = 1, ProvideFileOptions options = PROVIDE_DEFAULT );
+
+      /**
+       * Release file from media.
+       * This signal that file is not needed anymore.
+       *
+       * \param resource location of the file on media
+       */
+      void releaseFile( const OnMediaLocation &resource );
+
+
+      /**
+       * Release file from media.
+       * This signal that file is not needed anymore.
+       *
+       * \param file path to the file relative to media URL
+       * \param media_nr the media number in the media set
+       */
+      void releaseFile(const Pathname & file, unsigned media_nr = 1 );
+
+      /**
+       * Provides direcotry \a dir from media number \a media_nr.
+       *
+       * \param dir path to the directory relative to media URL
+       * \param recursive whether to provide the whole directory subtree
+       * \param media_nr the media number in the media set
+       * \return local pathname of the requested directory
+       *
+       * \throws MediaException if a problem occured and user has chosen to
+       *         abort the operation. The calling code should take care
+       *         to quit the current operation.
+       * \todo throw SkipRequestException if a problem occured and user has chosen
+       *         to skip the current operation. The calling code should continue
+       *         with the next one, if possible.
+       * \see zypp::media::MediaManager::provideDir()
+       * \see zypp::media::MediaManager::provideDirTree()
+       */
+      Pathname provideDir(const Pathname & dir, bool recursive, unsigned media_nr = 1, ProvideFileOptions options = PROVIDE_DEFAULT );
+
+      /**
+       * Checks if a file exists on the specified media, with user callbacks.
+       *
+       * \param file file to check
+       * \param media_nr Media number
+       *
+       * \throws MediaException if a problem occured and user has chosen to
+       *         abort the operation. The calling code should take care
+       *         to quit the current operation.
+       * \throws SkipRequestException if a problem occured and user has chosen
+       *         to skip the current operation. The calling code should continue
+       *         with the next one, if possible.
+       * \see zypp::media::MediaManager::doesFileExist(MediaAccessId,const Pathname&)
+       */
+      bool doesFileExist(const Pathname & file, unsigned media_nr = 1 );
+
+      /**
+       * Fills \ref retlist with directory information.
+       */
+      void dirInfo( filesystem::DirContent &retlist, const Pathname &dirname,
+                    bool dots = true, unsigned media_nr = 1 );
+
+      /**
+       * Release all attached media of this set.
+       *
+       * \throws MediaNotOpenException for invalid access IDs.
+       */
+      void release();
+
+      /**
+       * Replaces media number in specified url with given \a medianr.
+       *
+       * Media number in the URL is searched for with regex
+       * <tt> "^(.*(cd|dvd))([0-9]+)(\\.iso)$" </tt> for iso scheme and
+       * with <tt> "^(.*(cd|dvd))([0-9]+)(/?)$" </tt> for other schemes.
+       *
+       * For cd and dvd scheme it returns the original URL, as well as for
+       * URL which do not match the above regexes.
+       *
+       * \param url_r   original URL
+       * \param medianr requested media number
+       * \return        rewritten URL if applicable, the original URL otherwise
+       */
+      static Url rewriteUrl (const Url & url_r, const media::MediaNr medianr);
+
+    protected:
+      /**
+       * Provides the \a file from medium number \a media_nr and returns its
+       * local path.
+       *
+       * \note   The method must not throw if \a checkonly is <tt>true</tt>.
+       *
+       * \throws MediaException \a checkonly is <tt>false</tt> and
+       *         a problem occured and user has chosen to
+       *         abort the operation. The calling code should take care
+       *         to quit the current operation.
+       * \throws SkipRequestException \a checkonly is <tt>false</tt> and
+       *         a problem occured and user has chosen
+       *         to skip the current operation. The calling code should continue
+       *         with the next one, if possible.
+       */
+      Pathname provideFileInternal( const OnMediaLocation &resource, ProvideFileOptions options );
+
+      typedef function<void( media::MediaAccessId, const Pathname & )> ProvideOperation;
+
+      void provide( ProvideOperation op, const OnMediaLocation &resource, ProvideFileOptions options, const Pathname &deltafile );
+
+      media::MediaAccessId getMediaAccessId (media::MediaNr medianr);
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    private:
+      /** Media or media set URL */
+      Url _url;
+
+      /**
+       * Prefered mount point.
+       *
+       * \see MediaManager::open(Url,Pathname)
+       * \see MediaHandler::_attachPoint
+       */
+      Pathname _prefAttachPoint;
+
+      std::string _label;
+
+      typedef std::map<media::MediaNr, media::MediaAccessId> MediaMap;
+      typedef std::map<media::MediaNr, media::MediaVerifierRef > VerifierMap;
+
+      /** Mapping between media number and Media Access ID */
+      MediaMap _medias;
+      /** Mapping between media number and corespondent verifier */
+      VerifierMap _verifiers;
+    };
+    ///////////////////////////////////////////////////////////////////
+    ZYPP_DECLARE_OPERATORS_FOR_FLAGS(MediaSetAccess::ProvideFileOptions);
+
+    /** \relates MediaSetAccess Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const MediaSetAccess & obj )
+    { return obj.dumpOn( str ); }
+
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SOURCE_MediaSetAccess_H
diff --git a/zypp/Misc.h b/zypp/Misc.h
new file mode 100644 (file)
index 0000000..c53104b
--- /dev/null
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Misc.h
+ *
+*/
+#ifndef ZYPP_MISC_H
+#define ZYPP_MISC_H
+
+#include "zypp/misc/DefaultLoadSystem.h"
+#include "zypp/misc/CheckAccessDeleted.h"
+
+#endif // ZYPP_MISC_H
diff --git a/zypp/OnMediaLocation.cc b/zypp/OnMediaLocation.cc
new file mode 100644 (file)
index 0000000..0f501aa
--- /dev/null
@@ -0,0 +1,36 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/OnMediaLocation.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/OnMediaLocation.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const OnMediaLocation & obj )
+  {
+    return str << "[" << obj.medianr() << "]" << obj.filename()
+              << "{" << obj.downloadSize() << "|" << obj.checksum() << "}";
+  }
+
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/OnMediaLocation.h b/zypp/OnMediaLocation.h
new file mode 100644 (file)
index 0000000..59d9f92
--- /dev/null
@@ -0,0 +1,169 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/OnMediaLocation.h
+ *
+*/
+#ifndef ZYPP_SOURCE_ONMEDIALOCATION_H
+#define ZYPP_SOURCE_ONMEDIALOCATION_H
+
+#include <iosfwd>
+
+#include "zypp/base/Deprecated.h"
+#include "zypp/Pathname.h"
+#include "zypp/ByteCount.h"
+#include "zypp/CheckSum.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : OnMediaLocation
+  //
+  /**
+   * Describes a path on a certain media amongs as the information
+   * required to download it, like its media number, checksum and
+   * size. It does not specify the URI of the file.
+   *
+   * Media number \c 0 usually indicates no media access.
+   *
+   * \todo Implement cheap copy via COW.
+  */
+  class OnMediaLocation
+  {
+    friend std::ostream & operator<<( std::ostream & str, const OnMediaLocation & obj );
+
+  public:
+    /** Default ctor indicating no media access. */
+    OnMediaLocation()
+    : _medianr( 0 )
+    , _optional(false)
+    {}
+
+    /** Ctor taking a filename and media number (defaults to 1). */
+    OnMediaLocation( const Pathname & filename_r, unsigned medianr_r = 1 )
+    : _medianr( medianr_r )
+    , _filename( filename_r )
+    , _optional(false) // bnc #447010
+    {}
+
+  public:
+    /**
+     * media number where the resource is located.
+     * for a url cd:// this could be 1..N.
+     * for a url of type http://host/path/CD1, a media number 2
+     * means looking on http://host/path/CD1/../CD2
+     */
+    unsigned          medianr()        const { return _medianr; }
+    /**
+     * The path to the resource relatve to the url and path.
+     * If the base is http://novell.com/download/repository, the
+     * resource filename could be "/repodata/repomd.xml"
+     */
+    const Pathname &  filename()       const { return _filename; }
+    /**
+     * the checksum of the resource
+     */
+    const CheckSum &  checksum()       const { return _checksum; }
+    /**
+     * The size of the resource on the server. Therefore
+     * the size of the download.
+     */
+    const ByteCount & downloadSize()   const { return _downloadsize; }
+    /**
+     * The size of the resource once it has been uncompressed
+     * or unpacked.
+     * If the file is file.txt.gz then this is the size of
+     * file.txt
+     */
+    const ByteCount & openSize()       const { return _opendownloadsize; }
+    /**
+     * The checksum of the resource once it has been uncompressed
+     * or unpacked.
+     * If the file is file.txt.gz then this is the checksum of
+     * file.txt
+     */
+    const CheckSum &  openChecksum()   const { return _openchecksum; }
+    /**
+     * whether this is an optional resource. That is a file that
+     * may not be present. This is just a hint to the resource
+     * downloader to not error in case the not found resource is
+     * not found.
+     */
+    const bool optional() const { return _optional; }
+
+  public:
+    /** Unset \c filename and set \c medianr to \c 0. */
+    OnMediaLocation & unsetLocation()
+    { _filename = Pathname(); _medianr = 0; return *this; }
+
+    /** Set filename and media number (defaults to \c 1). */
+    OnMediaLocation & setLocation( const Pathname & val_r,
+                                   unsigned mediaNumber_r = 1 )
+    { _filename = val_r; _medianr = mediaNumber_r; return *this; }
+
+   /** Set the files size. */
+    OnMediaLocation & setDownloadSize( const ByteCount & val_r )
+    { _downloadsize = val_r; return *this; }
+
+    /** Set the files checksum. */
+    OnMediaLocation & setChecksum( const CheckSum & val_r )
+    { _checksum = val_r; return *this; }
+
+    /** Set the files open (uncompressed) size. */
+    OnMediaLocation & setOpenSize( const ByteCount & val_r )
+    { _opendownloadsize = val_r; return *this; }
+
+    /** Set the files open (uncompressed) checksum. */
+    OnMediaLocation & setOpenChecksum( const CheckSum & val_r )
+    { _openchecksum = val_r; return *this; }
+
+    /**
+     * Set the wether the resource is optional or not
+     * \see optional
+     */
+    OnMediaLocation & setOptional( bool val )
+    { _optional = val; return *this; }
+
+  public:
+   /**
+    * Individual manipulation of \c medianr (prefer \ref setLocation).
+    * Using \ref setLocation is prefered as us usually have to adjust
+    * \c filename and \c medianr in sync.
+    */
+    OnMediaLocation & changeMedianr( unsigned val_r )
+    { _medianr = val_r; return *this; }
+
+    /**
+     * Individual manipulation of \c filename (prefer \ref setLocation).
+     * Using \ref setLocation is preferedas us usually have to adjust
+     * \c filename and \c medianr in sync.
+     */
+    OnMediaLocation & changeFilename( const Pathname & val_r )
+    { _filename = val_r; return *this; }
+
+  private:
+    unsigned  _medianr;
+    Pathname  _filename;
+    CheckSum  _checksum;
+    ByteCount _downloadsize;
+    ByteCount _opendownloadsize;
+    CheckSum  _openchecksum;
+    bool      _optional;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates OnMediaLocation Stream output */
+  std::ostream & operator<<( std::ostream & str, const OnMediaLocation & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SOURCE_ONMEDIALOCATION_H
diff --git a/zypp/Package.cc b/zypp/Package.cc
new file mode 100644 (file)
index 0000000..e8ba7eb
--- /dev/null
@@ -0,0 +1,161 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Package.cc
+ *
+*/
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/Package.h"
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(Package);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Package::Package
+  //   METHOD TYPE : Ctor
+  //
+  Package::Package( const sat::Solvable & solvable_r )
+  : ResObject( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Package::~Package
+  //   METHOD TYPE : Dtor
+  //
+  Package::~Package()
+  {}
+
+  VendorSupportOption Package::vendorSupport() const
+  {
+    static const IdString support_unsupported( "support_unsupported" );
+    static const IdString support_acc( "support_acc" );
+    static const IdString support_l1( "support_l1" );
+    static const IdString support_l2( "support_l2" );
+    static const IdString support_l3( "support_l3" );
+
+    Keywords kw( keywords() );
+    for_( it, kw.begin(), kw.end() )
+    {
+      if ( *it == support_unsupported )
+        return VendorSupportUnsupported;
+      if ( *it == support_acc )
+        return VendorSupportACC;
+      if ( *it == support_l1 )
+        return VendorSupportLevel1;
+      if ( *it == support_l2 )
+        return VendorSupportLevel2;
+      if ( *it == support_l3 )
+        return VendorSupportLevel3;
+    }
+    return VendorSupportUnknown;
+  }
+
+  bool Package::maybeUnsupported() const
+  {
+      if ( ( vendorSupport() == VendorSupportUnknown ) ||
+           ( vendorSupport() == VendorSupportACC ) ||
+           ( vendorSupport() == VendorSupportUnsupported ) )
+          return true;
+      return false;
+  }
+
+  Changelog Package::changelog() const
+  {
+      Target_Ptr target( getZYpp()->getTarget() );
+      if ( ! target )
+      {
+        ERR << "Target not initialized. Changelog is not available." << std::endl;
+        return Changelog();
+      }
+
+      if ( repository().isSystemRepo() )
+      {
+          target::rpm::RpmHeader::constPtr header;
+          target->rpmDb().getData(name(), header);
+          return header ? header->tag_changelog() : Changelog(); // might be deleted behind our back (bnc #530595)
+      }
+      WAR << "changelog is not available for uninstalled packages" << std::endl;
+      return Changelog();
+  }
+
+  std::string Package::buildhost() const
+  { return lookupStrAttribute( sat::SolvAttr::buildhost ); }
+
+  std::string Package::distribution() const
+  { return lookupStrAttribute( sat::SolvAttr::distribution ); }
+
+  std::string Package::license() const
+  { return lookupStrAttribute( sat::SolvAttr::license ); }
+
+  std::string Package::packager() const
+  { return lookupStrAttribute( sat::SolvAttr::packager ); }
+
+  std::string Package::group() const
+  { return lookupStrAttribute( sat::SolvAttr::group ); }
+
+  Package::Keywords Package::keywords() const
+  { return Keywords( sat::SolvAttr::keywords, satSolvable() ); }
+
+  std::string Package::url() const
+  { return lookupStrAttribute( sat::SolvAttr::url ); }
+
+  ByteCount Package::sourcesize() const
+  { return lookupNumAttribute( sat::SolvAttr::sourcesize ); }
+
+  std::list<std::string> Package::authors() const
+  {
+    std::list<std::string> ret;
+    str::split( lookupStrAttribute( sat::SolvAttr::authors ), std::back_inserter(ret), "\n" );
+    return ret;
+  }
+
+  Package::FileList Package::filelist() const
+  { return FileList( sat::SolvAttr::filelist, satSolvable() ); }
+
+  CheckSum Package::checksum() const
+  { return lookupCheckSumAttribute( sat::SolvAttr::checksum ); }
+
+  OnMediaLocation Package::location() const
+  { return lookupLocation(); }
+
+  std::string Package::sourcePkgName() const
+  {
+    // no id means same as package
+    sat::detail::IdType id( lookupIdAttribute( sat::SolvAttr::sourcename ) );
+    return id ? IdString( id ).asString() : name();
+  }
+
+  Edition Package::sourcePkgEdition() const
+  {
+   // no id means same as package
+    sat::detail::IdType id( lookupIdAttribute( sat::SolvAttr::sourceevr ) );
+    return id ? Edition( id ) : edition();
+  }
+
+  std::string Package::sourcePkgType() const
+  { return lookupStrAttribute( sat::SolvAttr::sourcearch ); }
+
+  std::string Package::sourcePkgLongName() const
+  { return str::form( "%s-%s.%s", sourcePkgName().c_str(), sourcePkgEdition().c_str(), sourcePkgType().c_str() ); }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Package.h b/zypp/Package.h
new file mode 100644 (file)
index 0000000..446a29a
--- /dev/null
@@ -0,0 +1,134 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Package.h
+ *
+*/
+#ifndef ZYPP_PACKAGE_H
+#define ZYPP_PACKAGE_H
+
+#include "zypp/ResObject.h"
+#include "zypp/PackageKeyword.h"
+#include "zypp/Changelog.h"
+#include "zypp/VendorSupportOptions.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  DEFINE_PTR_TYPE(Package);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Package
+  //
+  /** Package interface.
+  */
+  class Package : public ResObject
+  {
+  public:
+    typedef Package                  Self;
+    typedef ResTraits<Self>          TraitsType;
+    typedef TraitsType::PtrType      Ptr;
+    typedef TraitsType::constPtrType constPtr;
+
+  public:
+    typedef sat::ArrayAttr<PackageKeyword,IdString> Keywords;
+    typedef sat::ArrayAttr<std::string,std::string> FileList;
+
+  public:
+
+    /**
+     * Returns the level of supportability the vendor
+     * gives to this package.
+     *
+     * This is one value from \ref VendorSupportOption
+     */
+    VendorSupportOption vendorSupport() const;
+
+    /**
+     * True if the vendor support for this package
+     * is unknown or explictly unsupported.
+     */
+    bool maybeUnsupported() const;
+
+    /** Get the package change log */
+    Changelog changelog() const;
+    /** */
+    std::string buildhost() const;
+    /** */
+    std::string distribution() const;
+    /** */
+    std::string license() const;
+    /** */
+    std::string packager() const;
+    /** */
+    std::string group() const;
+    /** */
+    Keywords keywords() const;
+    /** Don't ship it as class Url, because it might be
+     * in fact anything but a legal Url. */
+    std::string url() const;
+    /** Size of corresponding the source package. */
+    ByteCount sourcesize() const;
+    /** */
+    std::list<std::string> authors() const;
+
+    /** Return the packages filelist (if available).
+     * The returned \ref FileList appears to be a container of
+     * \c std::string. In fact it is a query, so it does not
+     * consume much memory.
+    */
+    FileList filelist() const;
+
+    /** \name Source package handling
+    */
+    //@{
+    /** Name of the source rpm this package was built from.
+     */
+    std::string sourcePkgName() const;
+
+    /** Edition of the source rpm this package was built from.
+     */
+    Edition sourcePkgEdition() const;
+
+    /** The type of the source rpm (\c "src" or \c "nosrc").
+     */
+    std::string sourcePkgType() const;
+
+    /** The source rpms \c "name-version-release.type"
+     */
+    std::string sourcePkgLongName() const;
+    //@}
+
+    /**
+     * Checksum the source says this package should have.
+     * \see \ref location
+     */
+    CheckSum checksum() const;
+
+    /** Location of the resolvable in the repository.
+     * \ref OnMediaLocation conatins all information required to
+     * retrieve the packge (url, checksum, etc.).
+     */
+    OnMediaLocation location() const;
+
+  protected:
+    friend Ptr make<Self>( const sat::Solvable & solvable_r );
+    /** Ctor */
+    Package( const sat::Solvable & solvable_r );
+    /** Dtor */
+    virtual ~Package();
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PACKAGE_H
diff --git a/zypp/PackageKeyword.h b/zypp/PackageKeyword.h
new file mode 100644 (file)
index 0000000..b46eead
--- /dev/null
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PackageKeyword.h
+ *
+*/
+#ifndef ZYPP_PACKAGEKEYWORD_H
+#define ZYPP_PACKAGEKEYWORD_H
+
+#include "zypp/IdStringType.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PackageKeyword
+  //
+  /** Package keywords.
+   * \see \ref IdStringType
+   */
+  class PackageKeyword : public IdStringType<PackageKeyword>
+  {
+    public:
+      /** Default ctor: empty keyword */
+      PackageKeyword() {}
+
+      /** Ctor taking keyword as string. */
+      explicit PackageKeyword( sat::detail::IdType id_r )  : _str( id_r ) {}
+      explicit PackageKeyword( const IdString & idstr_r )  : _str( idstr_r ) {}
+      explicit PackageKeyword( const std::string & str_r ) : _str( str_r ) {}
+      explicit PackageKeyword( const char * cstr_r )       : _str( cstr_r ) {}
+
+    private:
+      friend class IdStringType<PackageKeyword>;
+      IdString _str;
+   };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PACKAGEKEYWORD_H
diff --git a/zypp/Patch.cc b/zypp/Patch.cc
new file mode 100644 (file)
index 0000000..08121b1
--- /dev/null
@@ -0,0 +1,211 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Patch.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/Patch.h"
+#include "zypp/sat/WhatProvides.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE( Patch );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Patch::Patch
+  //   METHOD TYPE : Ctor
+  //
+  Patch::Patch( const sat::Solvable & solvable_r )
+  : ResObject( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Patch::~Patch
+  //   METHOD TYPE : Dtor
+  //
+  Patch::~Patch()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   Patch interface forwarded to implementation
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  Patch::Category Patch::categoryEnum() const
+  {
+    static const IdString cat_yast             ( "yast" );
+    static const IdString cat_security         ( "security" );
+    static const IdString cat_recommended      ( "recommended" );
+    static const IdString cat_bugfix           ( "bugfix" );           // rhn
+    static const IdString cat_optional         ( "optional" );
+    static const IdString cat_feature          ( "feature" );
+    static const IdString cat_enhancement      ( "enhancement" );      // rnh
+    static const IdString cat_document         ( "document" );
+
+    // patch category is not poolized in the solv file (i.e. an IdString) ;(
+    IdString cat( sat::LookupAttr( sat::SolvAttr::patchcategory, satSolvable() ).begin().c_str() );
+
+    if ( cat == cat_yast )
+      return CAT_YAST;
+    if ( cat == cat_security )
+      return CAT_SECURITY;
+    if ( cat == cat_recommended || cat == cat_bugfix )
+      return CAT_RECOMMENDED;
+    if ( cat == cat_optional || cat == cat_enhancement || cat == cat_feature )
+      return CAT_OPTIONAL;
+    if ( cat == cat_document )
+      return CAT_DOCUMENT;
+
+    return CAT_OTHER;
+  }
+
+  std::string Patch::message( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::message, lang_r ); }
+
+  std::string Patch::category() const
+  { return lookupStrAttribute( sat::SolvAttr::patchcategory ); }
+
+  bool Patch::rebootSuggested() const
+  { return lookupBoolAttribute( sat::SolvAttr::rebootSuggested ); }
+
+  bool Patch::restartSuggested() const
+  { return lookupBoolAttribute( sat::SolvAttr::restartSuggested ); }
+
+  bool Patch::reloginSuggested() const
+  { return lookupBoolAttribute( sat::SolvAttr::reloginSuggested ); }
+
+  Patch::InteractiveFlags Patch::interactiveFlags() const
+  {
+    InteractiveFlags patchFlags (NoFlags);
+    if ( rebootSuggested() )
+      patchFlags |= Reboot;
+
+    if ( ! message().empty() )
+      patchFlags |= Message;
+
+    if ( ! licenseToConfirm().empty() )
+      patchFlags |= License;
+
+    Patch::Contents c( contents() );
+    for_( it, c.begin(), c.end() )
+    {
+      if ( ! makeResObject(*it)->licenseToConfirm().empty() )
+      {
+        patchFlags |= License;
+        break;
+      }
+    }
+    return patchFlags;
+  }
+
+  bool Patch::interactiveWhenIgnoring( InteractiveFlags flags_r ) const
+  {
+    if ( interactiveFlags() & ( ~flags_r ) )
+    {
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  bool Patch::interactive() const
+  {
+    return interactiveWhenIgnoring();
+  }
+
+  Patch::Contents Patch::contents() const
+  {
+    Contents result;
+    // DBG << *this << endl;
+    sat::LookupAttr updateCollection( sat::SolvAttr::updateCollection, satSolvable() );
+    for_( entry, updateCollection.begin(), updateCollection.end() )
+    {
+      IdString name    ( entry.subFind( sat::SolvAttr::updateCollectionName ).idStr() );
+      Edition  edition ( entry.subFind( sat::SolvAttr::updateCollectionEvr ).idStr() );
+      Arch     arch    ( entry.subFind( sat::SolvAttr::updateCollectionArch ).idStr() );
+      if ( name.empty() )
+      {
+        WAR << "Ignore malformed updateCollection entry: " << name << "-" << edition << "." << arch << endl;
+        continue;
+      }
+
+      // The entry is relevant if there is an installed
+      // package with the same name and arch.
+      bool relevant = false;
+      sat::WhatProvides providers( (Capability( name.id() )) );
+      for_( it, providers.begin(), providers.end() )
+      {
+        if ( it->isSystem() && it->ident() == name && it->arch() == arch )
+        {
+          relevant = true;
+          break;
+        }
+      }
+      if ( ! relevant )
+      {
+        // DBG << "Not relevant: " << name << "-" << edition << "." << arch << endl;
+        continue;
+      }
+
+#warning definition of patch contents is poor - needs review
+      /* find exact providers first (this matches the _real_ 'collection content' of the patch */
+      providers = sat::WhatProvides( Capability( arch, name.c_str(), Rel::EQ, edition, ResKind::package ) );
+      if ( providers.empty() )
+      {
+        /* no exact providers: find 'best' providers: those with a larger evr */
+        providers = sat::WhatProvides( Capability( arch, name.c_str(), Rel::GT, edition, ResKind::package ) );
+        if ( providers.empty() )
+        {
+          // Hmm, this patch is not installable, no one is providing the package in the collection
+          // FIXME: raise execption ? fake a solvable ?
+          WAR << "Missing provider: " << name << "-" << edition << "." << arch << endl;
+          continue;
+        }
+      }
+
+      // FIXME ?! loop over providers and try to find installed ones ?
+      // DBG << "Found " << name << "-" << edition << "." << arch << ": " << *(providers.begin()) << endl;
+      result.get().insert( *(providers.begin()) );
+    }
+
+    return result;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Patch::ReferenceIterator
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  Patch::ReferenceIterator::ReferenceIterator( const sat::Solvable & val_r )
+  { base_reference() = sat::LookupAttr( sat::SolvAttr::updateReference, val_r ).begin(); }
+
+  std::string Patch::ReferenceIterator::id() const
+  { return base_reference().subFind( sat::SolvAttr::updateReferenceId ).asString(); }
+  std::string Patch::ReferenceIterator::href() const
+  { return base_reference().subFind( sat::SolvAttr::updateReferenceHref ).asString(); }
+  std::string Patch::ReferenceIterator::title() const
+  { return base_reference().subFind( sat::SolvAttr::updateReferenceTitle ).asString(); }
+  std::string Patch::ReferenceIterator::type() const
+  { return base_reference().subFind( sat::SolvAttr::updateReferenceType ).asString(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Patch.h b/zypp/Patch.h
new file mode 100644 (file)
index 0000000..333db74
--- /dev/null
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Patch.h
+ *
+*/
+#ifndef ZYPP_PATCH_H
+#define ZYPP_PATCH_H
+
+#include "zypp/base/Flags.h"
+#include "zypp/sat/SolvAttr.h"
+#include "zypp/ResObject.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+
+  DEFINE_PTR_TYPE(Patch);
+
+
+  /**
+   * Class representing a patch.
+   *
+   * A patch represents a specific problem that
+   * can be fixed by pulling in the patch dependencies.
+   *
+   * Patches can be marked for installation but their
+   * installation is a no-op.
+   */
+  class Patch : public ResObject
+  {
+    public:
+      typedef Patch                    Self;
+      typedef ResTraits<Self>          TraitsType;
+      typedef TraitsType::PtrType      Ptr;
+      typedef TraitsType::constPtrType constPtr;
+
+    public:
+      typedef sat::SolvableSet Contents;
+
+      enum Category {
+        CAT_OTHER,
+        CAT_YAST,
+        CAT_SECURITY,
+        CAT_RECOMMENDED,
+        CAT_OPTIONAL,
+        CAT_DOCUMENT
+      };
+
+      /**
+       * Flags defining if and why this
+       * patch is interactive.
+       */
+      enum InteractiveFlag {
+        NoFlags = 0x0000,
+        Reboot = 0x0001,
+        Message = 0x0002,
+        License = 0x0004
+      };
+      ZYPP_DECLARE_FLAGS(InteractiveFlags, InteractiveFlag);
+
+    public:
+      /**
+       * Issue date time. For now it is the same as
+       * \ref buildtime().
+       */
+      Date timestamp() const
+      { return buildtime(); }
+
+      /**
+       * Patch category (recommended, security,...)
+       */
+      std::string category() const;
+
+      /** Patch category as enum of wellknown categories.
+       * Unknown values are mapped to \ref CAT_OTHER.
+       */
+      Category categoryEnum() const;
+
+      /**
+       * Does the system need to reboot to finish the update process?
+       */
+      bool rebootSuggested() const;
+
+      /**
+       * Does the patch affect the package manager itself?
+       * restart is suggested then
+       */
+      bool restartSuggested() const;
+
+      /**
+       * Does the patch needs the user to relogin to take effect?
+       * relogin is suggested then
+       */
+      bool reloginSuggested() const;
+
+      /**
+       * \short Information or warning to be displayed to the user
+       */
+      std::string message( const Locale & lang_r = Locale() ) const;
+
+      /**
+       * Get the InteractiveFlags of this Patch
+       */
+      InteractiveFlags interactiveFlags() const;
+
+      /**
+       * Is the patch still interactive when ignoring this flags?
+       */
+      bool interactiveWhenIgnoring( InteractiveFlags flags_r = NoFlags ) const;
+
+      /**
+       * Is the patch installation interactive? (does it need user input?)
+       *
+       * For security reasons patches requiring a reboot are not
+       * installed in an unattended mode. They are considered to be
+       * \c interactive so the user gets informed about the need for
+       * reboot. \a ignoreRebootFlag_r may be used to explicitly turn
+       * off this behavior and include those patches (unless they actually
+       * contain interactive components as well, like messages or licenses).
+       */
+      bool interactive() const;
+
+    public:
+      /**
+       * The collection of packages associated with this patch.
+       */
+      Contents contents() const;
+
+    public:
+
+      /** Query class for Patch issue references */
+      class ReferenceIterator;
+      /**
+       * Get an iterator to the beginning of the patch
+       * references. \see Patch::ReferenceIterator
+       */
+      ReferenceIterator referencesBegin() const;
+      /**
+       * Get an iterator to the end of the patch
+       * references. \see Patch::ReferenceIterator
+       */
+      ReferenceIterator referencesEnd() const;
+
+    protected:
+      friend Ptr make<Self>( const sat::Solvable & solvable_r );
+      /** Ctor */
+      Patch( const sat::Solvable & solvable_r );
+      /** Dtor */
+      virtual ~Patch();
+  };
+  ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Patch::InteractiveFlags);
+
+
+  /**
+   * Query class for Patch issue references
+   * like bugzilla and security issues the
+   * patch is supposed to fix.
+   *
+   * The iterator does not provide a dereference
+   * operator so you can do * on it, but you can
+   * access the attributes of each patch issue reference
+   * directly from the iterator.
+   *
+   * \code
+   * for ( Patch::ReferenceIterator it = patch->referencesBegin();
+   *       it != patch->referencesEnd();
+   *       ++it )
+   * {
+   *   cout << it.href() << endl;
+   * }
+   * \endcode
+   *
+   */
+  class Patch::ReferenceIterator : public boost::iterator_adaptor<
+      Patch::ReferenceIterator           // Derived
+      , sat::LookupAttr::iterator        // Base
+      , int                              // Value
+      , boost::forward_traversal_tag     // CategoryOrTraversal
+      , int                              // Reference
+  >
+  {
+    public:
+      ReferenceIterator() {}
+      explicit ReferenceIterator( const sat::Solvable & val_r );
+
+      /**
+       * The id of the reference. For bugzilla entries
+       * this is the bug number as a string.
+       */
+      std::string id() const;
+      /**
+       * Url or pointer where to find more information
+       */
+      std::string href() const;
+      /**
+       * Title describing the issue
+       */
+      std::string title() const;
+      /**
+       * Type of the reference. For example
+       * "bugzilla"
+       */
+      std::string type() const;
+
+    private:
+      friend class boost::iterator_core_access;
+      int dereference() const { return 0; }
+  };
+
+  inline Patch::ReferenceIterator Patch::referencesBegin() const
+  { return ReferenceIterator(satSolvable()); }
+
+  inline Patch::ReferenceIterator Patch::referencesEnd() const
+  { return ReferenceIterator(); }
+
+  /////////////////////////////////////////////////////////////////
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PATCH_H
diff --git a/zypp/PathInfo.cc b/zypp/PathInfo.cc
new file mode 100644 (file)
index 0000000..5791f2f
--- /dev/null
@@ -0,0 +1,1150 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PathInfo.cc
+ *
+*/
+
+#include <sys/types.h> // for ::minor, ::major macros
+#include <utime.h>     // for ::utime
+#include <sys/statvfs.h>
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/IOStream.h"
+
+#include "zypp/ExternalProgram.h"
+#include "zypp/PathInfo.h"
+#include "zypp/Digest.h"
+#include "zypp/TmpPath.h"
+
+using std::endl;
+using std::string;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, FileType obj )
+    {
+      switch ( obj ) {
+#define EMUMOUT(T) case T: return str << #T; break
+        EMUMOUT( FT_NOT_AVAIL );
+        EMUMOUT( FT_NOT_EXIST );
+        EMUMOUT( FT_FILE );
+        EMUMOUT( FT_DIR );
+        EMUMOUT( FT_CHARDEV );
+        EMUMOUT( FT_BLOCKDEV );
+        EMUMOUT( FT_FIFO );
+        EMUMOUT( FT_LINK );
+        EMUMOUT( FT_SOCKET );
+#undef EMUMOUT
+      }
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : StatMode::fileType
+    // METHOD TYPE : FileType
+    //
+    FileType StatMode::fileType() const
+    {
+      if ( isFile() )
+        return FT_FILE;
+      if ( isDir() )
+        return FT_DIR;
+      if ( isLink() )
+        return FT_LINK;
+      if ( isChr() )
+        return FT_CHARDEV;
+      if ( isBlk() )
+        return FT_BLOCKDEV;
+      if ( isFifo() )
+        return FT_FIFO;
+      if ( isSock() )
+        return FT_SOCKET ;
+
+      return FT_NOT_AVAIL;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const StatMode & obj )
+    {
+      iostr::IosFmtFlagsSaver autoResoreState( str );
+
+      char t = '?';
+      if ( obj.isFile() )
+        t = '-';
+      else if ( obj.isDir() )
+        t = 'd';
+      else if ( obj.isLink() )
+        t = 'l';
+      else if ( obj.isChr() )
+        t = 'c';
+      else if ( obj.isBlk() )
+        t = 'b';
+      else if ( obj.isFifo() )
+        t = 'p';
+      else if ( obj.isSock() )
+        t = 's';
+
+      str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Class : PathInfo
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::PathInfo
+    // METHOD TYPE : Constructor
+    //
+    PathInfo::PathInfo()
+    : mode_e( STAT )
+    , error_i( -1 )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::PathInfo
+    // METHOD TYPE : Constructor
+    //
+    PathInfo::PathInfo( const Pathname & path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+    {
+      operator()();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::PathInfo
+    // METHOD TYPE : Constructor
+    //
+    PathInfo::PathInfo( const std::string & path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+    {
+      operator()();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::PathInfo
+    // METHOD TYPE : Constructor
+    //
+    PathInfo::PathInfo( const char * path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+    {
+      operator()();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::~PathInfo
+    // METHOD TYPE : Destructor
+    //
+    PathInfo::~PathInfo()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::operator()
+    // METHOD TYPE : bool
+    //
+    bool PathInfo::operator()()
+    {
+      if ( path_t.empty() ) {
+        error_i = -1;
+      } else {
+        switch ( mode_e ) {
+        case STAT:
+          error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
+          break;
+        case LSTAT:
+          error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
+          break;
+        }
+        if ( error_i == -1 )
+          error_i = errno;
+      }
+      return !error_i;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::fileType
+    // METHOD TYPE : File_type
+    //
+    FileType PathInfo::fileType() const
+    {
+      if ( isExist() )
+        return asStatMode().fileType();
+      return FT_NOT_EXIST;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::userMay
+    // METHOD TYPE : mode_t
+    //
+    mode_t PathInfo::userMay() const
+    {
+      if ( !isExist() )
+        return 0;
+      if ( owner() == getuid() ) {
+        return( uperm()/0100 );
+      } else if ( group() == getgid() ) {
+        return( gperm()/010 );
+      }
+      return operm();
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : PathInfo::major
+     **        FUNCTION TYPE : unsigned int
+     */
+    unsigned int PathInfo::major() const
+    {
+      return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : PathInfo::minor
+     **        FUNCTION TYPE : unsigned int
+     */
+    unsigned int PathInfo::minor() const
+    {
+      return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE :  std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
+    {
+      iostr::IosFmtFlagsSaver autoResoreState( str );
+
+      str << obj.asString() << "{";
+      if ( !obj.isExist() ) {
+        str << "does not exist}";
+      } else {
+        str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
+
+        if ( obj.isFile() )
+          str << " size " << obj.size();
+
+        str << "}";
+      }
+
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // filesystem utilities
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : _Log_Result
+     **        FUNCTION TYPE : int
+     **
+     **        DESCRIPTION : Helper function to log return values.
+    */
+#define _Log_Result MIL << endl, __Log_Result
+    inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
+    {
+      if ( res )
+      {
+        if ( rclass )
+          WAR << " FAILED: " << rclass << " " << res << endl;
+        else
+          WAR << " FAILED: " << str::strerror( res ) << endl;
+      }
+      return res;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PathInfo::mkdir
+    // METHOD TYPE : int
+    //
+    int mkdir( const Pathname & path, unsigned mode )
+    {
+      MIL << "mkdir " << path << ' ' << str::octstring( mode );
+      if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : assert_dir()
+    // METHOD TYPE : int
+    //
+    int assert_dir( const Pathname & path, unsigned mode )
+    {
+      if ( path.empty() )
+        return ENOENT;
+
+      { // Handle existing paths in advance.
+        PathInfo pi( path );
+        if ( pi.isDir() )
+          return 0;
+        if ( pi.isExist() )
+          return EEXIST;
+      }
+
+      string spath = path.asString()+"/";
+      string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
+      string::size_type pos = string::npos;
+      int ret = 0;
+
+      while ( (pos = spath.find('/',lastpos)) != string::npos )
+      {
+        string dir( spath.substr(0,pos) );
+        ret = ::mkdir( dir.c_str(), mode );
+        if ( ret == -1 )
+        {
+          if ( errno == EEXIST ) // ignore errors about already existing paths
+            ret = 0;
+          else
+          {
+            ret = errno;
+            WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
+          }
+        }
+        else
+        {
+          MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
+        }
+        lastpos = pos+1;
+      }
+
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : rmdir
+    // METHOD TYPE : int
+    //
+    int rmdir( const Pathname & path )
+    {
+      MIL << "rmdir " << path;
+      if ( ::rmdir( path.asString().c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : recursive_rmdir
+    // METHOD TYPE : int
+    //
+    static int recursive_rmdir_1( const Pathname & dir )
+    {
+      DIR * dp;
+      struct dirent * d;
+
+      if ( ! (dp = opendir( dir.c_str() )) )
+        return _Log_Result( errno );
+
+      while ( (d = readdir(dp)) )
+      {
+        std::string direntry = d->d_name;
+        if ( direntry == "." || direntry == ".." )
+          continue;
+        Pathname new_path( dir / d->d_name );
+
+        struct stat st;
+        if ( ! lstat( new_path.c_str(), &st ) )
+        {
+          if ( S_ISDIR( st.st_mode ) )
+            recursive_rmdir_1( new_path );
+          else
+            ::unlink( new_path.c_str() );
+        }
+      }
+      closedir( dp );
+
+      if ( ::rmdir( dir.c_str() ) < 0 )
+        return errno;
+
+      return 0;
+    }
+    ///////////////////////////////////////////////////////////////////
+    int recursive_rmdir( const Pathname & path )
+    {
+      MIL << "recursive_rmdir " << path << ' ';
+      PathInfo p( path );
+
+      if ( !p.isExist() ) {
+        return _Log_Result( 0 );
+      }
+
+      if ( !p.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      return _Log_Result( recursive_rmdir_1( path ) );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : clean_dir
+    // METHOD TYPE : int
+    //
+    int clean_dir( const Pathname & path )
+    {
+      MIL << "clean_dir " << path << ' ';
+      PathInfo p( path );
+
+      if ( !p.isExist() ) {
+        return _Log_Result( 0 );
+      }
+
+      if ( !p.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      string cmd( str::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
+      ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+        MIL << "  " << output;
+      }
+      int ret = prog.close();
+      return _Log_Result( ret, "returned" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : copy_dir
+    // METHOD TYPE : int
+    //
+    int copy_dir( const Pathname & srcpath, const Pathname & destpath )
+    {
+      MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
+
+      PathInfo sp( srcpath );
+      if ( !sp.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      PathInfo dp( destpath );
+      if ( !dp.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      PathInfo tp( destpath + srcpath.basename() );
+      if ( tp.isExist() ) {
+        return _Log_Result( EEXIST );
+      }
+
+
+      const char *const argv[] = {
+        "/bin/cp",
+        "-dR",
+        "--",
+        srcpath.asString().c_str(),
+        destpath.asString().c_str(),
+        NULL
+      };
+      ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+        MIL << "  " << output;
+      }
+      int ret = prog.close();
+      return _Log_Result( ret, "returned" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : copy_dir_content
+    // METHOD TYPE : int
+    //
+    int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
+    {
+      MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
+
+      PathInfo sp( srcpath );
+      if ( !sp.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      PathInfo dp( destpath );
+      if ( !dp.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      if ( srcpath == destpath ) {
+        return _Log_Result( EEXIST );
+      }
+
+      std::string src( srcpath.asString());
+      src += "/.";
+      const char *const argv[] = {
+        "/bin/cp",
+        "-dR",
+        "--",
+        src.c_str(),
+        destpath.asString().c_str(),
+        NULL
+      };
+      ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+        MIL << "  " << output;
+      }
+      int ret = prog.close();
+      return _Log_Result( ret, "returned" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : readdir
+    //  METHOD TYPE : int
+    //
+    int readdir( std::list<std::string> & retlist,
+                 const Pathname & path, bool dots )
+    {
+      retlist.clear();
+
+      MIL << "readdir " << path << ' ';
+
+      DIR * dir = ::opendir( path.asString().c_str() );
+      if ( ! dir ) {
+        return _Log_Result( errno );
+      }
+
+      struct dirent *entry;
+      while ( (entry = ::readdir( dir )) != 0 ) {
+
+        if ( entry->d_name[0] == '.' ) {
+          if ( !dots )
+            continue;
+          if ( entry->d_name[1] == '\0'
+               || (    entry->d_name[1] == '.'
+                    && entry->d_name[2] == '\0' ) )
+            continue;
+        }
+        retlist.push_back( entry->d_name );
+      }
+
+      ::closedir( dir );
+
+      return _Log_Result( 0 );
+    }
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : readdir
+    // METHOD TYPE : int
+    //
+    int readdir( std::list<Pathname> & retlist,
+                 const Pathname & path, bool dots )
+    {
+      retlist.clear();
+
+      std::list<string> content;
+      int res = readdir( content, path, dots );
+
+      if ( !res ) {
+        for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
+          retlist.push_back( path + *it );
+        }
+      }
+
+      return res;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : readdir
+    // METHOD TYPE : int
+    //
+
+    bool DirEntry::operator==( const DirEntry &rhs ) const
+    {
+      // if one of the types is not known, use the name only
+      if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
+        return ( name == rhs.name );
+      return ((name == rhs.name ) && (type == rhs.type));
+    }
+
+    int readdir( DirContent & retlist, const Pathname & path,
+                 bool dots, PathInfo::Mode statmode )
+    {
+      retlist.clear();
+
+      std::list<string> content;
+      int res = readdir( content, path, dots );
+
+      if ( !res ) {
+        for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
+          PathInfo p( path + *it, statmode );
+          retlist.push_back( DirEntry( *it, p.fileType() ) );
+        }
+      }
+
+      return res;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : is_empty_dir
+    // METHOD TYPE : int
+    //
+    int is_empty_dir(const Pathname & path)
+    {
+      DIR * dir = ::opendir( path.asString().c_str() );
+      if ( ! dir ) {
+        return _Log_Result( errno );
+      }
+
+      struct dirent *entry;
+      while ( (entry = ::readdir( dir )) != NULL )
+      {
+        std::string name(entry->d_name);
+
+        if ( name == "." || name == "..")
+         continue;
+
+        break;
+      }
+      ::closedir( dir );
+
+      return entry != NULL ? -1 : 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : unlink
+    // METHOD TYPE : int
+    //
+    int unlink( const Pathname & path )
+    {
+      MIL << "unlink " << path;
+      if ( ::unlink( path.asString().c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : rename
+    // METHOD TYPE : int
+    //
+    int rename( const Pathname & oldpath, const Pathname & newpath )
+    {
+      MIL << "rename " << oldpath << " -> " << newpath;
+      if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : exchange
+    // METHOD TYPE : int
+    //
+    int exchange( const Pathname & lpath, const Pathname & rpath )
+    {
+      MIL << "exchange " << lpath << " <-> " << rpath;
+      if ( lpath.empty() || rpath.empty() )
+        return _Log_Result( EINVAL );
+
+      PathInfo linfo( lpath );
+      PathInfo rinfo( rpath );
+
+      if ( ! linfo.isExist() )
+      {
+        if ( ! rinfo.isExist() )
+          return _Log_Result( 0 ); // both don't exist.
+
+        // just rename rpath -> lpath
+        int ret = assert_dir( lpath.dirname() );
+        if ( ret != 0 )
+          return _Log_Result( ret );
+        if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+          return _Log_Result( errno );
+        }
+        return _Log_Result( 0 );
+      }
+
+      // HERE: lpath exists:
+      if ( ! rinfo.isExist() )
+      {
+        // just rename lpath -> rpath
+        int ret = assert_dir( rpath.dirname() );
+        if ( ret != 0 )
+          return _Log_Result( ret );
+        if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
+          return _Log_Result( errno );
+        }
+        return _Log_Result( 0 );
+      }
+
+      // HERE: both exist
+      TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
+      if ( ! tmpfile )
+        return _Log_Result( errno );
+      Pathname tmp( tmpfile.path() );
+      ::unlink( tmp.c_str() );
+
+      if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+        ::rename( tmp.c_str(), lpath.c_str() );
+        return _Log_Result( errno );
+      }
+      if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
+        ::rename( lpath.c_str(), rpath.c_str() );
+        ::rename( tmp.c_str(), lpath.c_str() );
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : copy
+    // METHOD TYPE : int
+    //
+    int copy( const Pathname & file, const Pathname & dest )
+    {
+      MIL << "copy " << file << " -> " << dest << ' ';
+
+      PathInfo sp( file );
+      if ( !sp.isFile() ) {
+        return _Log_Result( EINVAL );
+      }
+
+      PathInfo dp( dest );
+      if ( dp.isDir() ) {
+        return _Log_Result( EISDIR );
+      }
+
+      const char *const argv[] = {
+        "/bin/cp",
+        "--remove-destination",
+        "--",
+        file.asString().c_str(),
+        dest.asString().c_str(),
+        NULL
+      };
+      ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+        MIL << "  " << output;
+      }
+      int ret = prog.close();
+      return _Log_Result( ret, "returned" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : symlink
+    // METHOD TYPE : int
+    //
+    int symlink( const Pathname & oldpath, const Pathname & newpath )
+    {
+      MIL << "symlink " << newpath << " -> " << oldpath;
+      if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : hardlink
+    // METHOD TYPE : int
+    //
+    int hardlink( const Pathname & oldpath, const Pathname & newpath )
+    {
+      MIL << "hardlink " << newpath << " -> " << oldpath;
+      if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : hardlink
+    // METHOD TYPE : int
+    //
+    int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
+    {
+      MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
+
+      PathInfo oldpi( oldpath, PathInfo::LSTAT );
+      if ( oldpi.isLink() )
+      {
+       // dont hardlink symliknks!
+       return copy( oldpath, newpath );
+      }
+
+      // Here: no symlink
+      if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
+      {
+        switch ( errno )
+        {
+          case EEXIST: // newpath already exists
+            if ( unlink( newpath ) == 0 && ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) != -1 )
+              return _Log_Result( 0 );
+            break;
+          case EXDEV: // oldpath  and  newpath are not on the same mounted file system
+            return copy( oldpath, newpath );
+            break;
+        }
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : readlink
+    // METHOD TYPE : int
+    //
+    int readlink( const Pathname & symlink_r, Pathname & target_r )
+    {
+      static const ssize_t bufsiz = 2047;
+      static char buf[bufsiz+1];
+      ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
+      if ( ret == -1 )
+      {
+        target_r = Pathname();
+        MIL << "readlink " << symlink_r;
+        return _Log_Result( errno );
+      }
+      buf[ret] = '\0';
+      target_r = buf;
+      return 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : expandlink
+    //  METHOD TYPE : Pathname
+    //
+    Pathname expandlink( const Pathname & path_r )
+    {
+      static const unsigned int level_limit = 256;
+      static unsigned int count;
+      Pathname path(path_r);
+      PathInfo info(path_r, PathInfo::LSTAT);
+
+      for (count = level_limit; info.isLink() && count; count--)
+      {
+        DBG << "following symlink " << path;
+        path = path.dirname() / readlink(path);
+        DBG << "->" << path << std::endl;
+        info = PathInfo(path, PathInfo::LSTAT);
+      }
+
+      // expand limit reached
+      if (count == 0)
+      {
+        ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
+        return Pathname();
+      }
+      // symlink
+      else if (count < level_limit)
+      {
+        // check for a broken link
+        if (PathInfo(path).isExist())
+          return path;
+        // broken link, return an empty path
+        else
+        {
+          ERR << path << " is broken (expanded from " << path_r << ")" << endl;
+          return Pathname();
+        }
+      }
+
+      // not a symlink, return the original pathname
+      DBG << "not a symlink" << endl;
+      return path;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : copy_file2dir
+    // METHOD TYPE : int
+    //
+    int copy_file2dir( const Pathname & file, const Pathname & dest )
+    {
+      MIL << "copy_file2dir " << file << " -> " << dest << ' ';
+
+      PathInfo sp( file );
+      if ( !sp.isFile() ) {
+        return _Log_Result( EINVAL );
+      }
+
+      PathInfo dp( dest );
+      if ( !dp.isDir() ) {
+        return _Log_Result( ENOTDIR );
+      }
+
+      const char *const argv[] = {
+        "/bin/cp",
+        "--",
+        file.asString().c_str(),
+        dest.asString().c_str(),
+        NULL
+      };
+      ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+        MIL << "  " << output;
+      }
+      int ret = prog.close();
+      return _Log_Result( ret, "returned" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : md5sum
+    // METHOD TYPE : std::string
+    //
+    std::string md5sum( const Pathname & file )
+    {
+      if ( ! PathInfo( file ).isFile() ) {
+        return string();
+      }
+      std::ifstream istr( file.asString().c_str() );
+      if ( ! istr ) {
+        return string();
+      }
+      return Digest::digest( "MD5", istr );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : sha1sum
+    // METHOD TYPE : std::string
+    //
+    std::string sha1sum( const Pathname & file )
+    {
+      return checksum(file, "SHA1");
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : checksum
+    //  METHOD TYPE : std::string
+    //
+    std::string checksum( const Pathname & file, const std::string &algorithm )
+    {
+      if ( ! PathInfo( file ).isFile() ) {
+        return string();
+      }
+      std::ifstream istr( file.asString().c_str() );
+      if ( ! istr ) {
+        return string();
+      }
+      return Digest::digest( algorithm, istr );
+    }
+
+    bool is_checksum( const Pathname & file, const CheckSum &checksum )
+    {
+      return ( filesystem::checksum(file,  checksum.type()) == checksum.checksum() );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : erase
+    // METHOD TYPE : int
+    //
+    int erase( const Pathname & path )
+    {
+      int res = 0;
+      PathInfo p( path, PathInfo::LSTAT );
+      if ( p.isExist() )
+        {
+          if ( p.isDir() )
+            res = recursive_rmdir( path );
+          else
+            res = unlink( path );
+        }
+      return res;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : chmod
+    // METHOD TYPE : int
+    //
+    int chmod( const Pathname & path, mode_t mode )
+    {
+      MIL << "chmod " << path << ' ' << str::octstring( mode );
+      if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    int addmod( const Pathname & path, mode_t mode )
+    {
+      mode_t omode( PathInfo( path ).st_mode() );
+      mode_t tmode( omode | mode );
+      if ( omode != mode )
+        return chmod( path, tmode );
+      return 0;
+    }
+
+    int delmod( const Pathname & path, mode_t mode )
+    {
+      mode_t omode( PathInfo( path ).st_mode() );
+      mode_t tmode( omode & ~mode );
+      if ( omode != mode )
+        return chmod( path, tmode );
+      return 0;
+    }
+
+   //////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : zipType
+    // METHOD TYPE : ZIP_TYPE
+    //
+    ZIP_TYPE zipType( const Pathname & file )
+    {
+      ZIP_TYPE ret = ZT_NONE;
+
+      int fd = open( file.asString().c_str(), O_RDONLY );
+
+      if ( fd != -1 ) {
+        const int magicSize = 3;
+        unsigned char magic[magicSize];
+        memset( magic, 0, magicSize );
+        if ( read( fd, magic, magicSize ) == magicSize ) {
+          if ( magic[0] == 0037 && magic[1] == 0213 ) {
+            ret = ZT_GZ;
+          } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
+            ret = ZT_BZ2;
+          }
+        }
+        close( fd );
+      }
+
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : df
+    // METHOD TYPE : ByteCount
+    //
+    ByteCount df( const Pathname & path_r )
+    {
+      ByteCount ret( -1 );
+      struct statvfs sb;
+      if ( statvfs( path_r.c_str(), &sb ) == 0 )
+        {
+          ret = sb.f_bfree * sb.f_bsize;
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : getUmask
+    // METHOD TYPE : mode_t
+    //
+    mode_t getUmask()
+    {
+      mode_t mask = ::umask( 0022 );
+      ::umask( mask );
+      return mask;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : getUmask
+    // METHOD TYPE : mode_t
+    //
+    int assert_file( const Pathname & path, unsigned mode )
+    {
+      int ret = assert_dir( path.dirname() );
+      MIL << "assert_file " << str::octstring( mode ) << " " << path;
+      if ( ret != 0 )
+        return _Log_Result( ret );
+
+      PathInfo pi( path );
+      if ( pi.isExist() )
+        return _Log_Result( pi.isFile() ? 0 : EEXIST );
+
+      int fd = ::creat( path.c_str(), mode );
+      if ( fd == -1 )
+        return _Log_Result( errno );
+
+      ::close( fd );
+      return _Log_Result( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : touch
+    //  METHOD TYPE : int
+    //
+    int touch (const Pathname & path)
+    {
+      MIL << "touch " << path;
+      struct ::utimbuf times;
+      times.actime = ::time( 0 );
+      times.modtime = ::time( 0 );
+      if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
+        return _Log_Result( errno );
+      }
+      return _Log_Result( 0 );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PathInfo.h b/zypp/PathInfo.h
new file mode 100644 (file)
index 0000000..e5ddf2d
--- /dev/null
@@ -0,0 +1,769 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PathInfo.h
+ *
+*/
+#ifndef ZYPP_PATHINFO_H
+#define ZYPP_PATHINFO_H
+
+extern "C"
+{
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+}
+
+#include <cerrno>
+#include <iosfwd>
+#include <list>
+#include <set>
+#include <map>
+
+#include "zypp/Pathname.h"
+#include "zypp/CheckSum.h"
+#include "zypp/ByteCount.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  /** Types and functions for filesystem operations.
+   * \todo move zypp::filesystem stuff into separate header
+   * \todo Add tmpfile and tmpdir handling.
+   * \todo think about using Exceptions in zypp::filesystem
+   * \todo provide a readdir iterator; at least provide an interface
+   * using an insert_iterator to be independent from std::container.
+  */
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /** File type information.
+     * \todo Think about an \ref g_EnumerationClass
+    */
+    enum FileType
+      {
+        FT_NOT_AVAIL = 0x00, // no typeinfo available
+        FT_NOT_EXIST = 0x01, // file does not exist
+        FT_FILE      = 0x02,
+        FT_DIR       = 0x04,
+        FT_CHARDEV   = 0x08,
+        FT_BLOCKDEV  = 0x10,
+        FT_FIFO      = 0x20,
+        FT_LINK      = 0x40,
+        FT_SOCKET    = 0x80
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates FileType Stram output. */
+    extern std::ostream & operator<<( std::ostream & str, FileType obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : StatMode
+    /**
+     * @short Wrapper class for mode_t values as derived from ::stat
+     **/
+    class StatMode
+    {
+      friend std::ostream & operator<<( std::ostream & str, const StatMode & obj );
+
+    public:
+      /** Ctor taking  mode_t value from ::stat. */
+      StatMode( const mode_t & mode_r = 0 )
+      : _mode( mode_r )
+      {}
+
+    public:
+
+      /** \name Query FileType. */
+      //@{
+      FileType fileType() const;
+
+      bool   isFile()  const { return S_ISREG( _mode ); }
+      bool   isDir ()  const { return S_ISDIR( _mode ); }
+      bool   isLink()  const { return S_ISLNK( _mode ); }
+      bool   isChr()   const { return S_ISCHR( _mode ); }
+      bool   isBlk()   const { return S_ISBLK( _mode ); }
+      bool   isFifo()  const { return S_ISFIFO( _mode ); }
+      bool   isSock()  const { return S_ISSOCK( _mode ); }
+      //@}
+
+      /** \name Query user permissions. */
+      //@{
+      bool   isRUsr()  const { return (_mode & S_IRUSR); }
+      bool   isWUsr()  const { return (_mode & S_IWUSR); }
+      bool   isXUsr()  const { return (_mode & S_IXUSR); }
+
+      /** Short for isRUsr().*/
+      bool   isR()     const { return isRUsr(); }
+      /** Short for isWUsr().*/
+      bool   isW()     const { return isWUsr(); }
+      /** Short for isXUsr().*/
+      bool   isX()     const { return isXUsr(); }
+      //@}
+
+      /** \name Query group permissions. */
+      //@{
+      bool   isRGrp()  const { return (_mode & S_IRGRP); }
+      bool   isWGrp()  const { return (_mode & S_IWGRP); }
+      bool   isXGrp()  const { return (_mode & S_IXGRP); }
+      //@}
+
+      /** \name Query others permissions. */
+      //@{
+      bool   isROth()  const { return (_mode & S_IROTH); }
+      bool   isWOth()  const { return (_mode & S_IWOTH); }
+      bool   isXOth()  const { return (_mode & S_IXOTH); }
+      //@}
+
+      /** \name Query special permissions. */
+      //@{
+      /** Set UID bit. */
+      bool   isUid()   const { return (_mode & S_ISUID); }
+      /** Set GID bit. */
+      bool   isGid()   const { return (_mode & S_ISGID); }
+      /** Sticky bit. */
+      bool   isVtx()   const { return (_mode & S_ISVTX); }
+      //@}
+
+      /** \name Query permission */
+      //@{
+      /** Test for equal permission bits. */
+      bool   isPerm ( mode_t m ) const { return (m == perm()); }
+      /** Test for set permission bits. */
+      bool   hasPerm( mode_t m ) const { return (m == (m & perm())); }
+      //@}
+
+      /** \name Extract permission bits only. */
+      //@{
+      mode_t uperm()   const { return (_mode & S_IRWXU); }
+      mode_t gperm()   const { return (_mode & S_IRWXG); }
+      mode_t operm()   const { return (_mode & S_IRWXO); }
+      mode_t perm()    const { return (_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX)); }
+      //@}
+
+      /** Return the mode_t value. */
+      mode_t st_mode() const { return _mode; }
+
+    private:
+      mode_t _mode;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates StatMode Stream output. */
+    extern std::ostream & operator<<( std::ostream & str, const StatMode & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : DevInoCache
+    /** Simple cache remembering device/inode to detect hardlinks.
+     * \code
+     *     DevInoCache trace;
+     *     for ( all files ) {
+     *       if ( trace.insert( file.device, file.inode ) ) {
+     *         // 1st occurance of file
+     *       }
+     *         // else: hardlink; already counted this device/inode
+     *       }
+     *     }
+     * \endcode
+     **/
+    class DevInoCache
+    {
+    public:
+      /** Ctor */
+      DevInoCache() {}
+
+      /** Clear cache. */
+      void clear() { _devino.clear(); }
+
+      /** Remember dev/ino.
+       * \Return <code>true</code> if it's inserted the first
+       * time, <code>false</code> if alredy present in cache
+       * (a hardlink to a previously remembered file).
+       **/
+      bool insert( const dev_t & dev_r, const ino_t & ino_r ) {
+        return _devino[dev_r].insert( ino_r ).second;
+      }
+
+    private:
+      std::map<dev_t,std::set<ino_t> > _devino;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PathInfo
+    /** Wrapper class for ::stat/::lstat.
+     *
+     * \note All attribute quieries test for isExist(), and return \c false or
+     * \c 0, if stat was not successful.
+     *
+     * \note For convenience PathInfo is available as zypp::PathInfo too.
+     **/
+    class PathInfo
+    {
+      friend std::ostream & operator<<( std::ostream & str, const PathInfo & obj );
+
+    public:
+      /** stat() or lstat() */
+      enum Mode { STAT, LSTAT };
+
+    public:
+      /** \name Construct from Pathname.
+       * Default mode is \c STAT.
+      */
+      //@{
+      PathInfo();
+      explicit
+      PathInfo( const Pathname & path, Mode initial = STAT );
+      explicit
+      PathInfo( const std::string & path, Mode initial = STAT );
+      explicit
+      PathInfo( const char * path, Mode initial = STAT );
+      //@}
+
+      /**Dtor */
+      ~PathInfo();
+
+      /** Return current Pathname. */
+      const Pathname &    path()     const { return path_t; }
+      /** Return current Pathname as String. */
+      const std::string & asString() const { return path_t.asString(); }
+      /** Return current Pathname as C-string. */
+      const char * c_str()           const { return path_t.asString().c_str(); }
+      /** Return current stat Mode. */
+      Mode                mode()     const { return mode_e; }
+      /** Return error returned from last stat/lstat call. */
+      int                 error()    const { return error_i; }
+
+      /** Set a new Pathname. */
+      void setPath( const Pathname & path ) { if ( path != path_t ) error_i = -1; path_t = path; }
+      /** Set a new Mode . */
+      void setMode( Mode mode )             { if ( mode != mode_e ) error_i = -1; mode_e = mode; }
+
+      /** STAT \a path. */
+      bool stat      ( const Pathname & path ) { setPath( path ); setMode( STAT );  return operator()(); }
+      /** LSTAT \a path. */
+      bool lstat     ( const Pathname & path ) { setPath( path ); setMode( LSTAT ); return operator()(); }
+      /** Restat \a path using current mode. */
+      bool operator()( const Pathname & path ) { setPath( path ); return operator()(); }
+
+      /** STAT current path. */
+      bool stat()   { setMode( STAT );  return operator()(); }
+      /** LSTAT current path. */
+      bool lstat()  { setMode( LSTAT ); return operator()(); }
+      /** Restat current path using current mode. */
+      bool operator()();
+
+    public:
+
+      /** Return whether valid stat info exists.
+       * That's usg. whether the file exist and you had permission to
+       * stat it.
+      */
+      bool   isExist() const { return !error_i; }
+
+      /** \name Query StatMode attibutes.
+       * Combines \ref zypp::PathInfo::isExist and \ref zypp::filesystem::StatMode query.
+      */
+      //@{
+      FileType fileType() const;
+
+      bool   isFile()  const { return isExist() && S_ISREG( statbuf_C.st_mode ); }
+      bool   isDir ()  const { return isExist() && S_ISDIR( statbuf_C.st_mode ); }
+      bool   isLink()  const { return isExist() && S_ISLNK( statbuf_C.st_mode ); }
+      bool   isChr()   const { return isExist() && S_ISCHR( statbuf_C.st_mode ); }
+      bool   isBlk()   const { return isExist() && S_ISBLK( statbuf_C.st_mode ); }
+      bool   isFifo()  const { return isExist() && S_ISFIFO( statbuf_C.st_mode ); }
+      bool   isSock()  const { return isExist() && S_ISSOCK( statbuf_C.st_mode ); }
+
+      // permission
+      bool   isRUsr()  const { return isExist() && (statbuf_C.st_mode & S_IRUSR); }
+      bool   isWUsr()  const { return isExist() && (statbuf_C.st_mode & S_IWUSR); }
+      bool   isXUsr()  const { return isExist() && (statbuf_C.st_mode & S_IXUSR); }
+
+      bool   isR()     const { return isRUsr(); }
+      bool   isW()     const { return isWUsr(); }
+      bool   isX()     const { return isXUsr(); }
+
+      bool   isRGrp()  const { return isExist() && (statbuf_C.st_mode & S_IRGRP); }
+      bool   isWGrp()  const { return isExist() && (statbuf_C.st_mode & S_IWGRP); }
+      bool   isXGrp()  const { return isExist() && (statbuf_C.st_mode & S_IXGRP); }
+
+      bool   isROth()  const { return isExist() && (statbuf_C.st_mode & S_IROTH); }
+      bool   isWOth()  const { return isExist() && (statbuf_C.st_mode & S_IWOTH); }
+      bool   isXOth()  const { return isExist() && (statbuf_C.st_mode & S_IXOTH); }
+
+      bool   isUid()   const { return isExist() && (statbuf_C.st_mode & S_ISUID); }
+      bool   isGid()   const { return isExist() && (statbuf_C.st_mode & S_ISGID); }
+      bool   isVtx()   const { return isExist() && (statbuf_C.st_mode & S_ISVTX); }
+
+      bool   isPerm ( mode_t m ) const { return isExist() && (m == perm()); }
+      bool   hasPerm( mode_t m ) const { return isExist() && (m == (m & perm())); }
+
+      mode_t uperm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXU) : 0; }
+      mode_t gperm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXG) : 0; }
+      mode_t operm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXO) : 0; }
+      mode_t perm()    const { return isExist() ? (statbuf_C.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX)) : 0; }
+
+      mode_t st_mode() const { return isExist() ? statbuf_C.st_mode : 0; }
+      //@}
+
+      /** Return st_mode() as filesystem::StatMode. */
+      StatMode asStatMode() const { return st_mode(); }
+
+      nlink_t nlink()  const { return isExist() ? statbuf_C.st_nlink : 0; }
+
+      /** \name Owner and group */
+      //@{
+      uid_t  owner()   const { return isExist() ? statbuf_C.st_uid : 0; }
+      gid_t  group()   const { return isExist() ? statbuf_C.st_gid : 0; }
+      //@}
+
+      /** \name Permission according to current uid/gid. */
+      //@{
+      /** Returns current users permission (<tt>[0-7]</tt>)*/
+      mode_t userMay() const;
+
+      bool   userMayR() const { return( userMay() & 04 ); }
+      bool   userMayW() const { return( userMay() & 02 ); }
+      bool   userMayX() const { return( userMay() & 01 ); }
+
+      bool   userMayRW()  const { return( (userMay() & 06) == 06 ); }
+      bool   userMayRX()  const { return( (userMay() & 05) == 05 ); }
+      bool   userMayWX()  const { return( (userMay() & 03) == 03 ); }
+
+      bool   userMayRWX() const { return( userMay() == 07 ); }
+      //@}
+
+      /** \name Device and inode info. */
+      //@{
+      ino_t  ino()     const { return isExist() ? statbuf_C.st_ino  : 0; }
+      dev_t  dev()     const { return isExist() ? statbuf_C.st_dev  : 0; }
+      dev_t  rdev()    const { return isExist() ? statbuf_C.st_rdev : 0; }
+
+      unsigned int major() const;
+      unsigned int minor() const;
+      //@}
+
+      /** \name Size info. */
+      //@{
+      off_t         size()    const { return isExist() ? statbuf_C.st_size : 0; }
+      unsigned long blksize() const { return isExist() ? statbuf_C.st_blksize : 0; }
+      unsigned long blocks()  const { return isExist() ? statbuf_C.st_blocks  : 0; }
+      //@}
+
+      /** \name Time stamps. */
+      //@{
+      time_t atime()   const { return isExist() ? statbuf_C.st_atime : 0; } /* time of last access */
+      time_t mtime()   const { return isExist() ? statbuf_C.st_mtime : 0; } /* time of last modification */
+      time_t ctime()   const { return isExist() ? statbuf_C.st_ctime : 0; }
+      //@}
+
+    private:
+      Pathname    path_t;
+      struct stat statbuf_C;
+      Mode        mode_e;
+      int         error_i;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates PathInfo Stream output. */
+    extern std::ostream & operator<<( std::ostream & str, const PathInfo & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Directory related functions. */
+    //@{
+    /**
+     * Like '::mkdir'. Attempt to create a new directory named path. mode
+     * specifies the permissions to use. It is modified by the process's
+     * umask in the usual way.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int mkdir( const Pathname & path, unsigned mode = 0755 );
+
+    /**
+     * Like 'mkdir -p'. No error if directory exists. Make parent directories
+     * as needed. mode specifies the permissions to use, if directories have to
+     * be created. It is modified by the process's umask in the usual way.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int assert_dir( const Pathname & path, unsigned mode = 0755 );
+
+    /**
+     * Like '::rmdir'. Delete a directory, which must be empty.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int rmdir( const Pathname & path );
+
+    /**
+     * Like 'rm -r DIR'. Delete a directory, recursively removing its contents.
+     *
+     * @return 0 on success, ENOTDIR if path is not a directory, otherwise the
+     * commands return value.
+     **/
+    int recursive_rmdir( const Pathname & path );
+
+    /**
+     * Like 'rm -r DIR/ *'. Delete directory contents, but keep the directory itself.
+     *
+     * @return 0 on success, ENOTDIR if path is not a directory, otherwise the
+     * commands return value.
+     **/
+    int clean_dir( const Pathname & path );
+
+    /**
+     * Like 'cp -a srcpath destpath'. Copy directory tree. srcpath/destpath must be
+     * directories. 'basename srcpath' must not exist in destpath.
+     *
+     * @return 0 on success, ENOTDIR if srcpath/destpath is not a directory, EEXIST if
+     * 'basename srcpath' exists in destpath, otherwise the commands return value.
+     **/
+    int copy_dir( const Pathname & srcpath, const Pathname & destpath );
+
+    /**
+     * Like 'cp -a srcpath/. destpath'. Copy the content of srcpath recursively
+     * into destpath. Both \p srcpath and \p destpath has to exists.
+     *
+     * @return 0 on success, ENOTDIR if srcpath/destpath is not a directory,
+     * EEXIST if srcpath and destpath are equal, otherwise the commands
+     * return value.
+     */
+    int copy_dir_content( const Pathname & srcpath, const Pathname & destpath);
+
+    /**
+     * Return content of directory via retlist. If dots is false
+     * entries starting with '.' are not reported. "." and ".."
+     * are never reported.
+     *
+     * Returns just the directory entries as string.
+     *
+     * @return 0 on success, errno on failure.
+     *
+     * \todo provide some readdirIterator.
+     **/
+
+    int readdir( std::list<std::string> & retlist,
+                 const Pathname & path, bool dots = true );
+
+    /**
+     * Return content of directory via retlist. If dots is false
+     * entries starting with '.' are not reported. "." and ".."
+     * are never reported.
+     *
+     * Returns the directory entries prefixed with \a path.
+     *
+     * @return 0 on success, errno on failure.
+     *
+     * \todo provide some readdirIterator.
+     **/
+
+    int readdir( std::list<Pathname> & retlist,
+                 const Pathname & path, bool dots = true );
+
+    /** Listentry returned by readdir. */
+    struct DirEntry {
+      std::string name;
+      FileType    type;
+      DirEntry( const std::string & name_r = std::string(), FileType type_r = FT_NOT_AVAIL )
+      : name( name_r )
+      , type( type_r )
+      {}
+
+      bool operator==( const DirEntry &rhs ) const;
+    };
+
+    /** Returned by readdir. */
+    typedef std::list<DirEntry> DirContent;
+
+    /**
+     * Return content of directory via retlist. If dots is false
+     * entries starting with '.' are not reported. "." and ".."
+     * are never reported.
+     *
+     * The type of individual directory entries is determined accoding to
+     * statmode (i.e. via stat or lstat).
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    int readdir( DirContent & retlist, const Pathname & path,
+                 bool dots = true, PathInfo::Mode statmode = PathInfo::STAT );
+
+
+    /**
+     * Check if the specified directory is empty.
+     * \param path The path of the directory to check.
+     * \return 0 if directory is empty, -1 if not, errno > 0 on failure.
+     */
+    int is_empty_dir(const Pathname & path);
+
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name File related functions. */
+    //@{
+    /**
+     * Create an empty file if it does not yet exist. Make parent directories
+     * as needed. mode specifies the permissions to use. It is modified by the
+     * process's umask in the usual way.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int assert_file( const Pathname & path, unsigned mode = 0644 );
+
+    /**
+     * Change file's modification and access times.
+     *
+     * \return 0 on success, errno on failure
+     * \see man utime
+     */
+    int touch (const Pathname & path);
+
+    /**
+     * Like '::unlink'. Delete a file (symbolic link, socket, fifo or device).
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int unlink( const Pathname & path );
+
+    /**
+     * Like '::rename'. Renames a file, moving it between directories if required.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int rename( const Pathname & oldpath, const Pathname & newpath );
+
+    /** Exchanges two files or directories.
+     *
+     * Most common use is when building a new config file (or dir)
+     * in a tempfile. After the job is done, configfile and tempfile
+     * are exchanged. This includes moving away the configfile in case
+     * the tempfile does not exist. Parent directories are created as
+     * needed.
+     *
+     * \note Paths are exchanged using \c ::rename, so take care both paths
+     * are located on the same filesystem.
+     *
+     * \code
+     * Pathname configfile( "/etc/myconfig" );
+     * TmpFile  newconfig( TmpFile::makeSibling( configfile ) );
+     * // now write the new config:
+     * std::ofstream o( newconfig.path().c_str() );
+     * o << "mew values << endl;
+     * o.close();
+     * // If everything is fine, exchange the files:
+     * exchange( newconfig.path(), configfile );
+     * // Now the old configfile is still available at newconfig.path()
+     * // until newconfig goes out of scope.
+     * \endcode
+     *
+     * @return 0 on success, errno on failure
+     */
+    int exchange( const Pathname & lpath, const Pathname & rpath );
+
+    /**
+     * Like 'cp file dest'. Copy file to destination file.
+     *
+     * @return 0 on success, EINVAL if file is not a file, EISDIR if
+     * destiantion is a directory, otherwise the commands return value.
+     **/
+    int copy( const Pathname & file, const Pathname & dest );
+
+    /**
+     * Like '::symlink'. Creates a symbolic link named newpath which contains
+     * the string oldpath. If newpath exists it will not be overwritten.
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    int symlink( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Like '::link'. Creates a hard link named newpath to an existing file
+     * oldpath. If newpath exists it will not be overwritten.
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    int hardlink( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Create \a newpath as hardlink or copy of \a oldpath.
+     *
+     * @return 0 on success, errno on failure.
+     */
+    int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Like '::readlink'. Return the contents of the symbolic link
+     * \a symlink_r via \a target_r.
+     *
+     * @return 0 on success, errno on failure.
+     */
+    int readlink( const Pathname & symlink_r, Pathname & target_r );
+    /** \overload Return an empty Pathname on error. */
+    inline Pathname readlink( const Pathname & symlink_r )
+    {
+      Pathname target;
+      readlink( symlink_r, target );
+      return target;
+    }
+
+    /**
+     * Recursively follows the symlink pointed to by \a path_r and returns
+     * the Pathname to the real file or directory pointed to by the link.
+     *
+     * There is a recursion limit of 256 iterations to protect against a cyclic
+     * link.
+     *
+     * @return Pathname of the file or directory pointed to by the given link
+     *   if it is a valid link. If \a path_r is not a link, an exact copy of
+     *   it is returned. If \a path_r is a broken or a cyclic link, an empty
+     *   Pathname is returned and the event logged.
+     */
+    Pathname expandlink( const Pathname & path_r );
+
+    /**
+     * Like 'cp file dest'. Copy file to dest dir.
+     *
+     * @return 0 on success, EINVAL if file is not a file, ENOTDIR if dest
+     * is no directory, otherwise the commands return value.
+     **/
+    int copy_file2dir( const Pathname & file, const Pathname & dest );
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Digest computaion.
+     * \todo check cooperation with zypp::Digest
+    */
+    //@{
+    /**
+     * Compute a files md5sum.
+     *
+     * @return the files md5sum on success, otherwise an empty string..
+     **/
+    std::string md5sum( const Pathname & file );
+
+    /**
+     * Compute a files sha1sum.
+     *
+     * @return the files sha1sum on success, otherwise an empty string..
+     **/
+    std::string sha1sum( const Pathname & file );
+    //@}
+
+    /**
+     * Compute a files checksum
+     *
+     * @return the files checksum on success, otherwise an empty string..
+     **/
+    std::string checksum( const Pathname & file, const std::string &algorithm );
+
+    /**
+     * check files checksum
+     *
+     * @return true if the checksum matchs
+     **/
+    bool is_checksum( const Pathname & file, const CheckSum &checksum );
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Changing permissions. */
+    //@{
+    /**
+     * Like '::chmod'. The mode of the file given by path is changed.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    int chmod( const Pathname & path, mode_t mode );
+
+    /**
+     * Add the \c mode bits to the file given by path.
+     *
+     * @return 0 on success, errno on failure
+     */
+    int addmod( const Pathname & path, mode_t mode );
+
+    /**
+     * Remove the \c mode bits from the file given by path.
+     *
+     * @return 0 on success, errno on failure
+     */
+    int delmod( const Pathname & path, mode_t mode );
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Misc. */
+    //@{
+    /**
+     * Test whether a file is compressed (gzip/bzip2).
+     *
+     * @return ZT_GZ, ZT_BZ2 if file is compressed, otherwise ZT_NONE.
+     **/
+    enum ZIP_TYPE { ZT_NONE, ZT_GZ, ZT_BZ2 };
+
+    ZIP_TYPE zipType( const Pathname & file );
+
+    /**
+     * Erase whatever happens to be located at path (file or directory).
+     *
+     * @return 0 on success.
+     *
+     * \todo check cooperation with zypp::TmpFile and zypp::TmpDir
+     **/
+    int erase( const Pathname & path );
+
+    /**
+     * Report free disk space on a mounted file system.
+     *
+     * path is the path name of any file within the mounted filesystem.
+     *
+     * @return Free disk space or -1 on error.
+     **/
+    ByteCount df( const Pathname & path );
+
+    /**
+     * Get the current umask (file mode creation mask)
+     *
+     * @return The current umask
+     **/
+    mode_t getUmask();
+
+     /**
+     * Modify \c mode_r according to the current umask
+     * <tt>( mode_r & ~getUmask() )</tt>.
+     * \see \ref getUmask.
+     * @return The resulting permissions.
+     **/
+    inline mode_t applyUmaskTo( mode_t mode_r )
+    { return mode_r & ~getUmask(); }
+    //@}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+
+  /** Dragged into namespace zypp. */
+  using filesystem::PathInfo;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PATHINFO_H
diff --git a/zypp/Pathname.cc b/zypp/Pathname.cc
new file mode 100644 (file)
index 0000000..7b833f9
--- /dev/null
@@ -0,0 +1,319 @@
+/*---------------------------------------------------------------------\
+ |                          ____ _   __ __ ___                          |
+ |                         |__  / \ / / . \ . \                         |
+ |                           / / \ V /|  _/  _/                         |
+ |                          / /__ | | | | | |                           |
+ |                         /_____||_| |_| |_|                           |
+ |                                                                      |
+ \---------------------------------------------------------------------*/
+/** \file      zypp/Pathname.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+
+using std::string;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : DirStack
+      //
+      /** silly helper to build Pathnames.
+      */
+      class DirStack {
+
+        struct Dir {
+
+          Dir *  up;
+          Dir *  dn;
+          string name;
+
+          Dir( const string & n = "" ) {
+            name = n;
+            up = dn = 0;
+          }
+
+          ~Dir() {
+            if ( up )
+              up->dn = dn;
+            if ( dn )
+              dn->up = up;
+          }
+        };
+
+        Dir *  top;
+        Dir *  bot;
+
+        void Pop() {
+          if ( !top )
+            return;
+          top = top->dn;
+          if ( top )
+            delete top->up;
+          else {
+            delete bot;
+            bot = 0;
+          }
+        }
+
+      public:
+
+        DirStack() { top = bot = 0; }
+        ~DirStack() {
+          while ( bot )
+            Pop();
+        }
+
+        void Push( const string & n ) {
+          if ( n.empty() || n == "." ) { // '.' or '/' only for bot
+            if ( bot )
+              return;
+          } else if ( n == ".." && top ) {
+            if ( top->name == "" )          // "/.."        ==> "/"
+              return;
+
+            if ( top->name != "." && top->name != ".." ) {      // "somedir/.." ==> ""
+              Pop();
+              return;
+            }
+            // "../.." "./.." stays
+          }
+
+          Dir * d = new Dir( n );
+          if ( !top )
+            top = bot = d;
+          else {
+            top->up = d;
+            d->dn = top;
+            d->up = 0;
+            top = d;
+          }
+        }
+
+        string str() {
+          if ( !bot )
+            return "";
+          string ret;
+          for ( Dir * d = bot; d; d = d->up ) {
+            if ( d != bot )
+              ret += "/";
+            ret += d->name;
+          }
+          if ( ret.empty() )
+            return "/";
+          return ret;
+        }
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::_assign
+    // METHOD TYPE : void
+    //
+    void Pathname::_assign( const string & name_tv )
+    {
+      prfx_i = 0;
+      name_t = name_tv;
+
+      if ( name_t.empty() )
+        return;
+
+      string   Tprfx;
+      DirStack Stack_Ci;
+
+      char *       Buf_aci    = new char[name_tv.length() + 1];
+      char *       W_pci      = Buf_aci;
+      const char * R_pci      = name_tv.c_str();
+
+      // check for prefix
+      if (    name_t.length() >= 2
+           && name_t[1] == ':'
+           && (    ( 'a' <= name_t[0] && name_t[0] <= 'z' )
+                || ( 'A' <= name_t[0] && name_t[0] <= 'Z' ) ) ) {
+        Tprfx  = name_t.substr( 0, 2 );
+        prfx_i = 2;
+        R_pci += 2;
+      }
+
+      // rel or abs path
+      if ( *R_pci == '/' ) {
+        Stack_Ci.Push( "" );
+        ++R_pci;
+      } else {
+        Stack_Ci.Push( "." );
+      }
+
+      do {
+        switch ( *R_pci ) {
+        case '/':
+        case '\0':
+          if ( W_pci != Buf_aci ) {
+            *W_pci = '\0';
+            W_pci = Buf_aci;
+            Stack_Ci.Push( Buf_aci );
+          }
+          break;
+
+        default:
+          *W_pci++ = *R_pci;
+          break;
+        }
+      } while( *R_pci++ );
+
+      delete[] Buf_aci;
+      name_t = Tprfx + Stack_Ci.str();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::dirname
+    // METHOD TYPE : Pathname
+    //
+    Pathname Pathname::dirname( const Pathname & name_tv )
+    {
+      if ( name_tv.empty() )
+        return "";
+
+      Pathname ret_t( name_tv );
+      string::size_type idx = ret_t.name_t.find_last_of( '/' );
+
+      if ( idx == string::npos ) {
+        ret_t.name_t.erase( ret_t.prfx_i );
+        ret_t.name_t += ".";
+      } else if ( idx == ret_t.prfx_i ) {
+        ret_t.name_t.erase( ret_t.prfx_i );
+        ret_t.name_t += "/";
+      } else {
+        ret_t.name_t.erase( idx );
+      }
+
+      return ret_t;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::basename
+    // METHOD TYPE : string
+    //
+    string Pathname::basename( const Pathname & name_tv )
+    {
+      if ( name_tv.empty() )
+        return string();
+
+      string ret_t( name_tv.asString() );
+      ret_t.erase( 0, name_tv.prfx_i );
+      string::size_type idx = ret_t.find_last_of( '/' );
+      if ( idx != string::npos ) {
+        ret_t.erase( 0, idx+1 );
+      }
+
+      return ret_t;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::asUrl
+    // METHOD TYPE : Url
+    //
+    Url Pathname::asUrl() const
+    {
+      Url ret( "dir:///" );
+      ret.setPathName( asString() );
+      return ret;
+    }
+
+    std::string Pathname::showRoot( const Pathname & root_r, const Pathname & path_r )
+    {
+      return str::Str() << "(" << root_r << ")" << path_r;
+    }
+
+    std::string Pathname::showRootIf( const Pathname & root_r, const Pathname & path_r )
+    {
+      if ( root_r.empty() || root_r == "/" )
+        return path_r.asString();
+      return showRoot( root_r, path_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::extension
+    // METHOD TYPE : string
+    //
+    string Pathname::extension( const Pathname & name_tv )
+    {
+      if ( name_tv.empty() )
+        return string();
+
+      string base( basename( name_tv ) );
+      string::size_type pos = base.rfind( '.' );
+      if ( pos == string::npos )
+        return string();
+      return base.substr( pos );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::assertprefix
+    // METHOD TYPE : Pathname
+    //
+    Pathname Pathname::assertprefix( const Pathname & root_r, const Pathname & path_r )
+    {
+      if ( root_r.empty()
+           || path_r == root_r
+           || str::hasPrefix( path_r.asString(), root_r.asString() ) )
+        return path_r;
+      return root_r / path_r;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::cat
+    // METHOD TYPE : Pathname
+    //
+    Pathname Pathname::cat( const Pathname & name_tv, const Pathname & add_tv )
+    {
+      if ( add_tv.empty() )
+        return name_tv;
+      if ( name_tv.empty() )
+        return add_tv;
+
+      string ret_ti( add_tv.asString() );
+      ret_ti.replace( 0, add_tv.prfx_i, "/" );
+
+      return name_tv.asString() + ret_ti;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Pathname::Extend
+    // METHOD TYPE : Pathname
+    //
+    Pathname Pathname::extend( const Pathname & l, const string & r )
+    {
+      return l.asString() + r;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Pathname.h b/zypp/Pathname.h
new file mode 100644 (file)
index 0000000..a93c4b3
--- /dev/null
@@ -0,0 +1,196 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Pathname.h
+ *
+*/
+#ifndef ZYPP_PATHNAME_H
+#define ZYPP_PATHNAME_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class Url;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace filesystem
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Pathname
+    //
+    /** Pathname.
+     *
+     * \note For convenience Pathname is available as zypp::Pathname too.
+     *
+     * Always stores normalized paths (no inner '.' or '..' components
+     * and no consecutive '/'es). Concatenation automatically adds
+     * the path separator '/'.
+     *
+     * \todo Add support for handling extensions incl. stripping
+     * extensions from basename (basename("/path/foo.baa", ".baa") ==> "foo")
+     * \todo Review. Maybe use COW pimpl, check storage.
+     * \todo \b EXPLICIT ctors.
+    */
+    class Pathname
+    {
+    public:
+      /** Default ctor: an empty path. */
+      Pathname()
+      : prfx_i( 0 )
+      {}
+
+      /** Ctor from string. */
+      Pathname( const std::string & name_tv )
+      { _assign( name_tv ); }
+
+      /** Ctor from char*. */
+      Pathname( const char * name_tv )
+      { _assign( name_tv ? name_tv : "" ); }
+
+      /** Assign */
+      Pathname & operator=( const Pathname & path_tv )
+      {
+        prfx_i = path_tv.prfx_i;
+        name_t = path_tv.name_t;
+        return *this;
+      }
+
+      /** Concatenate and assing. \see cat */
+      Pathname & operator/=( const Pathname & path_tv )
+      { return( *this = cat( *this, path_tv ) ); }
+
+      /** Concatenate and assing. \see cat
+       * \deprecated: use /=
+      */
+      Pathname & operator+=( const Pathname & path_tv )
+      { return( *this = cat( *this, path_tv ) ); }
+
+      /** String representation. */
+      const std::string & asString() const
+      { return name_t; }
+
+      /** String representation as "(root)/path" */
+      static std::string showRoot( const Pathname & root_r, const Pathname & path_r );
+
+      /** String representation as "(root)/path", unless \a root is \c "/" or empty. */
+      static std::string showRootIf( const Pathname & root_r, const Pathname & path_r );
+
+      /** Url representation using \c dir schema. */
+      Url asUrl() const;
+
+      /** String representation. */
+      const char * c_str() const
+      { return name_t.c_str(); }
+
+      /** Test for an empty path. */
+      bool empty()    const { return name_t.empty(); }
+      /** Test for an absolute path. */
+      bool absolute() const { return !empty() && name_t[prfx_i] == '/'; }
+      /** Test for a relative path. */
+      bool relative() const { return !empty() && name_t[prfx_i] != '/'; }
+
+      /** Return all but the last component od this path. */
+      Pathname dirname() const { return dirname( *this ); }
+      static Pathname dirname( const Pathname & name_tv );
+
+      /** Return the last component of this path. */
+      std::string basename() const { return basename( *this ); }
+      static std::string basename( const Pathname & name_tv );
+
+      /** Return all of the characters in name after and including
+       * the last dot in the last element of name.  If there is no dot
+       * in the last element of name then returns the empty string.
+      */
+      std::string extension() const { return extension( *this ); }
+      static std::string extension( const Pathname & name_tv );
+
+      /** Return this path, adding a leading '/' if relative. */
+      Pathname absolutename() const { return absolutename( *this ); }
+      static Pathname absolutename( const Pathname & name_tv )
+      { return name_tv.relative() ? cat( "/", name_tv ) : name_tv; }
+
+      /** Return this path, removing a leading '/' if absolute.*/
+      Pathname relativename() const { return relativename( *this ); }
+      static Pathname relativename( const Pathname & name_tv )
+      { return name_tv.absolute() ? cat( ".", name_tv ) : name_tv; }
+
+      /** Return \c path_r prefixed with \c root_r, unless it is already prefixed. */
+      static Pathname assertprefix( const Pathname & root_r, const Pathname & path_r );
+
+      /** Concatenation of pathnames.
+       * \code
+       *   "foo"  / "baa"  ==> "foo/baa"
+       *   "foo/" / "baa"  ==> "foo/baa"
+       *   "foo"  / "/baa" ==> "foo/baa"
+       *   "foo/" / "/baa" ==> "foo/baa"
+       * \endcode
+      */
+      Pathname cat( const Pathname & r ) const { return cat( *this, r ); }
+      static Pathname cat( const Pathname & l, const Pathname & r );
+
+      /** Append string \a r to the last component of the path.
+       * \code
+       *   "foo/baa".extend( ".h" ) ==> "foo/baa.h"
+       * \endcode
+      */
+      Pathname extend( const std::string & r ) const { return extend( *this, r ); }
+      static Pathname extend( const Pathname & l, const std::string & r );
+
+    private:
+      std::string::size_type prfx_i;
+      std::string            name_t;
+
+      void _assign( const std::string & name_tv );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Pathname */
+    inline bool operator==( const Pathname & l, const Pathname & r )
+    { return l.asString() == r.asString(); }
+
+    /** \relates Pathname */
+    inline bool operator!=( const Pathname & l, const Pathname & r )
+    { return l.asString() != r.asString(); }
+
+    /** \relates Pathname Concatenate two Pathname. */
+    inline Pathname operator/( const Pathname & l, const Pathname & r )
+    { return Pathname::cat( l, r ); }
+
+    /** \relates Pathname Concatenate two Pathname.
+     * \deprecated: use /
+    */
+    inline Pathname operator+( const Pathname & l, const Pathname & r )
+    { return Pathname::cat( l, r ); }
+
+    /** \relates Pathname */
+    inline bool operator<( const Pathname & l, const Pathname & r )
+    { return l.asString() < r.asString(); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Pathname Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Pathname & obj )
+    { return str << obj.asString(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace filesystem
+  ///////////////////////////////////////////////////////////////////
+
+  /** Dragged into namespace zypp. */
+  using filesystem::Pathname;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PATHNAME_H
diff --git a/zypp/Pattern.cc b/zypp/Pattern.cc
new file mode 100644 (file)
index 0000000..9d748a8
--- /dev/null
@@ -0,0 +1,253 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Pattern.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/ResPool.h"
+#include "zypp/Pattern.h"
+#include "zypp/Filter.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PatternExpander
+    //
+    /** Recursively expand a Pattern.
+     *
+     * This means recursively expanding Patterns included by this or
+     * extending this. The result is a \c set of <tt>Pattern::constPtr</tt>
+     * accessible via iterator.
+     */
+    class PatternExpander
+    {
+      public:
+        typedef std::map<Pattern::constPtr, DefaultIntegral<bool, false> > PatternMap;
+        typedef PatternMap::size_type size_type;
+        typedef PatternMap::key_type  value_type;
+        typedef MapKVIteratorTraits<PatternMap>::Key_const_iterator const_iterator;
+
+      public:
+        PatternExpander()
+        {}
+
+        /** Recursively expand Pattern. */
+        size_type doExpand( Pattern::constPtr pat_r )
+        {
+          // INT << "+++ " << pat_r << " ++++++++++++++++++++++++++++++++++" << endl;
+          _patternMap.clear();
+          if ( pat_r )
+          {
+            _patternMap[pat_r];
+            Pattern::constPtr unprocessed( pat_r );
+            // MIL << _patternMap << endl;
+            do {
+              expandIncludes( unprocessed );
+              expandExtending( unprocessed );
+              _patternMap[unprocessed] = true;
+              // MIL << _patternMap << endl;
+            } while( (unprocessed = nextUnprocessed()) );
+          }
+          // SEC << "--- " << _patternMap.size() << " ----------------------------------" << endl;
+          return _patternMap.size();
+        }
+
+        const_iterator begin() const
+        { return make_map_key_begin( _patternMap ); }
+
+        const_iterator end() const
+        { return make_map_key_end( _patternMap ); }
+
+      private:
+        /** Get the next unprocessed Pattern in \c _patternMap. */
+        Pattern::constPtr nextUnprocessed() const
+        {
+          for_( it, _patternMap.begin(), _patternMap.end() )
+          {
+            if ( ! it->second )
+              return it->first;
+          }
+          return NULL;
+        }
+
+      private:
+        /** Store all included patterns in \c _patternMap. */
+        void expandIncludes( const Pattern::constPtr & pat_r )
+        {
+          Pattern::NameList c( pat_r->includes() );
+          for_( it, c.begin(), c.end() )
+          {
+            expandInclude( Capability( it->c_str()/*, *ResKind::pattern*/ ) );
+          }
+        }
+
+        /** Store Patterns matching an \c Includes capability in \c _patternMap. */
+        void expandInclude( const Capability & include_r )
+        {
+          sat::WhatProvides w( include_r );
+          for_( it, w.begin(), w.end() )
+          {
+            _patternMap[asKind<Pattern>(PoolItem(*it))];
+          }
+        }
+
+      private:
+        /** Store all patterns extending \c pat_r in \c _patternMap. */
+        void expandExtending( const Pattern::constPtr & pat_r )
+        {
+          ResPool pool( ResPool::instance() );
+          for_( it, pool.byKindBegin<Pattern>(), pool.byKindEnd<Pattern>() )
+          {
+            expandIfExtends( pat_r, *it );
+          }
+        }
+
+        /** Store \c extending_r if it extends \c pat_r. */
+        void expandIfExtends( const Pattern::constPtr & pat_r, const PoolItem & extending_r )
+        {
+          Pattern::constPtr extending( asKind<Pattern>(extending_r) );
+          Pattern::NameList c( extending->extends() );
+          for_( it, c.begin(), c.end() )
+          {
+            if ( providedBy( pat_r, Capability( it->c_str()/*, *ResKind::pattern*/ ) ) )
+            {
+              // an extends matches the Pattern
+              _patternMap[extending];
+              break;
+            }
+          }
+        }
+
+        /** Return true if Capability \c extends_r is provided by Pattern. */
+        bool providedBy( const Pattern::constPtr & pat_r, const Capability & extends_r )
+        {
+          if ( !pat_r )
+            return false;
+
+          sat::Solvable pat( pat_r->satSolvable() );
+          sat::WhatProvides w( extends_r );
+          for_( it, w.begin(), w.end() )
+          {
+            if ( pat == *it )
+              return true;
+          }
+          return false;
+        }
+
+      private:
+        PatternMap _patternMap;
+    };
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+  IMPL_PTR_TYPE(Pattern);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Pattern::Pattern
+  //   METHOD TYPE : Ctor
+  //
+  Pattern::Pattern( const sat::Solvable & solvable_r )
+  : ResObject( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Pattern::~Pattern
+  //   METHOD TYPE : Dtor
+  //
+  Pattern::~Pattern()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   Pattern interface forwarded to implementation
+  //
+  ///////////////////////////////////////////////////////////////////
+  /** */
+  bool Pattern::isDefault() const
+  { return lookupBoolAttribute( sat::SolvAttr::isdefault ); }
+  /** */
+  bool Pattern::userVisible() const
+  { return lookupBoolAttribute( sat::SolvAttr::isvisible ); }
+  /** */
+  std::string Pattern::category( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::category, lang_r ); }
+  /** */
+  Pathname Pattern::icon() const
+  { return lookupStrAttribute( sat::SolvAttr::icon ); }
+  /** */
+  Pathname Pattern::script() const
+  { return lookupStrAttribute( sat::SolvAttr::script ); }
+
+  std::string Pattern::order() const
+  { return lookupStrAttribute( sat::SolvAttr::order ); }
+
+  Pattern::NameList Pattern::includes() const
+  { return NameList( sat::SolvAttr::includes, satSolvable() ); }
+
+  Pattern::NameList Pattern::extends() const
+  { return NameList( sat::SolvAttr::extends, satSolvable() ); }
+
+  Pattern::Contents Pattern::core() const
+  {
+    // get items providing the requirements
+    sat::WhatProvides prv( requires() );
+    // return packages only.
+    return Pattern::Contents( make_filter_begin( filter::byKind<Package>(), prv ),
+                              make_filter_end( filter::byKind<Package>(), prv ) );
+  }
+
+  Pattern::Contents Pattern::depends() const
+  {
+    // load requires, recommends, suggests
+    CapabilitySet caps;
+    {
+      Capabilities c( requires() );
+      caps.insert( c.begin(),c.end() );
+      c = recommends();
+      caps.insert( c.begin(),c.end() );
+      c = suggests();
+      caps.insert( c.begin(),c.end() );
+    }
+    // get items providing the above
+    sat::WhatProvides prv( caps );
+    // return packages only.
+    return Pattern::Contents( make_filter_begin( filter::byKind<Package>(), prv ),
+                              make_filter_end( filter::byKind<Package>(), prv ) );
+  }
+
+  Pattern::Contents Pattern::contents() const
+  {
+    PatternExpander expander;
+    if ( ! expander.doExpand( this ) )
+      return Contents(); // empty pattern set
+
+    Contents result;
+    for_( it, expander.begin(), expander.end() )
+    {
+      Contents c( (*it)->depends() );
+      result.get().insert( c.begin(), c.end() );
+    }
+    return result;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Pattern.h b/zypp/Pattern.h
new file mode 100644 (file)
index 0000000..2a3fefd
--- /dev/null
@@ -0,0 +1,88 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Pattern.h
+ *
+*/
+#ifndef ZYPP_PATTERN_H
+#define ZYPP_PATTERN_H
+
+#include "zypp/ResObject.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  DEFINE_PTR_TYPE(Pattern);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Pattern
+  //
+  /** Pattern interface.
+  */
+  class Pattern : public ResObject
+  {
+    public:
+      typedef Pattern                  Self;
+      typedef ResTraits<Self>          TraitsType;
+      typedef TraitsType::PtrType      Ptr;
+      typedef TraitsType::constPtrType constPtr;
+
+    public:
+      typedef sat::ArrayAttr<IdString,IdString> NameList;
+      typedef sat::SolvableSet                  Contents;
+
+    public:
+      /** */
+      bool isDefault() const;
+      /** */
+      bool userVisible() const;
+      /** */
+      std::string category( const Locale & lang_r = Locale() ) const;
+      /** */
+      Pathname icon() const;
+      /** */
+      Pathname script() const;
+      /** */
+      std::string order() const;
+
+    public:
+      /** Ui hint: included patterns. */
+      NameList includes() const;
+
+      /** Ui hint: patterns this one extends. */
+      NameList extends() const;
+
+      /** Ui hint: Required Packages. */
+      Contents core() const;
+
+      /** Ui hint: Dependent packages.
+       * This also includes recommended and suugested packages.
+      */
+      Contents depends() const;
+
+      /** The collection of packages associated with this pattern.
+       * This also evaluates the patterns includes/extends relation.
+       */
+      Contents contents() const;
+
+    protected:
+      friend Ptr make<Self>( const sat::Solvable & solvable_r );
+      /** Ctor */
+      Pattern( const sat::Solvable & solvable_r );
+      /** Dtor */
+      virtual ~Pattern();
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PATTERN_H
diff --git a/zypp/PluginFrame.cc b/zypp/PluginFrame.cc
new file mode 100644 (file)
index 0000000..b05815b
--- /dev/null
@@ -0,0 +1,311 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginFrame.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PluginFrame.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginFrame::Impl
+  //
+  /** PluginFrame implementation. */
+  struct PluginFrame::Impl
+  {
+    public:
+      Impl()
+      {}
+
+      Impl( const std::string & command_r )
+      { setCommand( command_r ); }
+
+      Impl( const std::string & command_r, const std::string body_r )
+       : _body( body_r )
+      { setCommand( command_r ); }
+
+      Impl( std::istream & stream_r );
+
+    public:
+      bool empty() const
+      { return _command.empty() && _body.empty(); }
+
+      const std::string & command() const
+      { return _command; }
+
+      void setCommand( const std::string & command_r )
+      {
+       if ( command_r.find( '\n' ) != std::string::npos )
+         ZYPP_THROW( PluginFrameException( "Multiline command", command_r ) );
+       _command = command_r;
+      }
+
+      const std::string & body() const
+      { return _body; }
+
+      std::string & bodyRef()
+      { return _body; }
+
+      void setBody( const std::string & body_r )
+      { _body = body_r; }
+
+    public:
+      typedef std::pair<HeaderListIterator,HeaderListIterator> constKeyRange;
+      typedef std::pair<HeaderList::iterator,HeaderList::iterator> KeyRange;
+
+      HeaderList & headerList()
+      { return _header; }
+
+      const HeaderList & headerList() const
+      { return _header; }
+
+      const std::string & getHeader( const std::string & key_r ) const
+      {
+       constKeyRange r( _header.equal_range( key_r ) );
+       if ( r.first == r.second )
+         ZYPP_THROW( PluginFrameException( "No value for key", key_r ) );
+       const std::string & ret( r.first->second );
+       if ( ++r.first != r.second )
+         ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
+       return ret;
+      }
+
+      const std::string & getHeader( const std::string & key_r, const std::string & default_r ) const
+      {
+       constKeyRange r( _header.equal_range( key_r ) );
+       if ( r.first == r.second )
+         return default_r;
+       const std::string & ret( r.first->second );
+       if ( ++r.first != r.second )
+         ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
+       return ret;
+      }
+
+      const std::string & getHeaderNT( const std::string & key_r, const std::string & default_r ) const
+      {
+       HeaderListIterator iter( _header.find( key_r ) );
+       return iter != _header.end() ? iter->second : default_r;
+      }
+
+      HeaderList::value_type mkHeaderPair( const std::string & key_r, const std::string & value_r )
+      {
+       if ( key_r.find_first_of( ":\n" ) != std::string::npos )
+         ZYPP_THROW( PluginFrameException( "Illegal char in header key", key_r ) );
+       if ( value_r.find_first_of( ":\n" ) != std::string::npos )
+         ZYPP_THROW( PluginFrameException( "Illegal char in header value", value_r ) );
+       return HeaderList::value_type( key_r, value_r );
+      }
+
+      void setHeader( const std::string & key_r, const std::string & value_r )
+      {
+       clearHeader( key_r );
+       addHeader( key_r, value_r );
+      }
+
+      void addHeader( const std::string & key_r, const std::string & value_r )
+      {
+       _header.insert( mkHeaderPair( key_r, value_r ) );
+      }
+
+      void clearHeader( const std::string & key_r )
+      {
+       _header.erase( key_r );
+      }
+
+    public:
+      std::ostream & writeTo( std::ostream & stream_r ) const;
+
+    private:
+      std::string _command;
+      std::string _body;
+      HeaderList  _header;
+
+    public:
+      /** Offer default Impl. */
+      static shared_ptr<Impl> nullimpl()
+      {
+       static shared_ptr<Impl> _nullimpl( new Impl );
+       return _nullimpl;
+      }
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const
+      { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PluginFrame::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PluginFrame::Impl & obj )
+  {
+    return str << "PluginFrame[" << obj.command() << "](" << obj.headerList().size() << "){" << obj.body().size() << "}";
+  }
+
+  PluginFrame::Impl::Impl( std::istream & stream_r )
+  {
+    //DBG << "Parse from " << stream_r << endl;
+    if ( ! stream_r )
+      ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
+
+    // JFYI: stream status after getline():
+    //  Bool  | Bits
+    //  ------|---------------
+    //  true  | [g___] >FOO< : FOO line was \n-terminated
+    //  true  | [_e__] >BAA< : BAA before EOF, but not \n-terminated
+    //  false | [_eF_] ><    : No valid data to consume
+
+    //command
+    _command = str::getline( stream_r );
+    if ( ! stream_r.good() )
+      ZYPP_THROW( PluginFrameException( "Missing NL after command" ) );
+
+    // header
+    do {
+      std::string data = str::getline( stream_r );
+      if ( ! stream_r.good() )
+       ZYPP_THROW( PluginFrameException( "Missing NL after header" ) );
+
+      if ( data.empty() )
+       break;  // --> empty line sep. header and body
+
+      std::string::size_type sep( data.find( ':') );
+      if ( sep ==  std::string::npos )
+       ZYPP_THROW( PluginFrameException( "Missing colon in header" ) );
+
+      _header.insert( HeaderList::value_type( data.substr(0,sep), data.substr(sep+1) ) );
+    } while ( true );
+
+    // data
+    _body = str::receiveUpTo( stream_r, '\0' );
+    if ( ! stream_r.good() )
+      ZYPP_THROW( PluginFrameException( "Missing NUL after body" ) );
+  }
+
+  std::ostream & PluginFrame::Impl::writeTo( std::ostream & stream_r ) const
+  {
+    //DBG << "Write " << *this << " to " << stream_r << endl;
+    if ( ! stream_r )
+      ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
+
+    // command
+    stream_r << _command << endl;
+    // header
+    for_( it, _header.begin(), _header.end() )
+      stream_r << it->first << ':' << it->second << endl;
+    // body
+    stream_r << endl
+             << _body << '\0';
+
+    if ( ! stream_r )
+      ZYPP_THROW( PluginFrameException( "Write error" ) );
+    return stream_r;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginFrame
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const std::string & PluginFrame::ackCommand()
+  {
+    static std::string _val( "ACK" );
+    return _val;
+  }
+
+  const std::string & PluginFrame::errorCommand()
+  {
+    static std::string _val( "ERROR" );
+    return _val;
+  }
+
+  PluginFrame::PluginFrame()
+    : _pimpl( Impl::nullimpl() )
+  {}
+
+  PluginFrame::PluginFrame( const std::string & command_r )
+    : _pimpl( new Impl( command_r ) )
+  {}
+
+  PluginFrame::PluginFrame( const std::string & command_r, const std::string body_r )
+    : _pimpl( new Impl( command_r, body_r ) )
+  {}
+
+  PluginFrame::PluginFrame( std::istream & stream_r )
+    : _pimpl( new Impl( stream_r ) )
+  {}
+
+  bool PluginFrame::empty() const
+  { return _pimpl->empty(); }
+
+  const std::string & PluginFrame::command() const
+  { return _pimpl->command(); }
+
+  void  PluginFrame::setCommand( const std::string & command_r )
+  { _pimpl->setCommand( command_r ); }
+
+  const std::string & PluginFrame::body() const
+  { return _pimpl->body(); }
+
+  std::string & PluginFrame::bodyRef()
+  { return _pimpl->bodyRef(); }
+
+  void  PluginFrame::setBody( const std::string & body_r )
+  { _pimpl->setBody( body_r ); }
+
+  std::ostream & PluginFrame::writeTo( std::ostream & stream_r ) const
+  { return _pimpl->writeTo( stream_r ); }
+
+  PluginFrame::HeaderList & PluginFrame::headerList()
+  { return _pimpl->headerList(); }
+
+  const PluginFrame::HeaderList & PluginFrame::headerList() const
+  { return _pimpl->headerList(); }
+
+  const std::string & PluginFrame::getHeader( const std::string & key_r ) const
+  { return _pimpl->getHeader( key_r ); }
+
+  const std::string & PluginFrame::getHeader( const std::string & key_r, const std::string & default_r ) const
+  { return _pimpl->getHeader( key_r, default_r ); }
+
+  const std::string & PluginFrame::getHeaderNT( const std::string & key_r, const std::string & default_r ) const
+  { return _pimpl->getHeaderNT( key_r, default_r ); }
+
+  void PluginFrame::setHeader( const std::string & key_r, const std::string & value_r )
+  { _pimpl->setHeader( key_r, value_r ); }
+
+  void PluginFrame::addHeader( const std::string & key_r, const std::string & value_r )
+  { _pimpl->addHeader( key_r, value_r ); }
+
+  void PluginFrame::clearHeader( const std::string & key_r )
+  { _pimpl->clearHeader( key_r ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<( std::ostream & str, const PluginFrame & obj )
+  { return str << *obj._pimpl; }
+
+  bool operator==( const PluginFrame & lhs, const PluginFrame & rhs )
+  {
+    return ( lhs._pimpl == rhs._pimpl )
+        || (( lhs.command() ==  rhs.command() ) && ( lhs.headerList() == rhs.headerList() ) && ( lhs.body() == rhs.body() ));
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PluginFrame.h b/zypp/PluginFrame.h
new file mode 100644 (file)
index 0000000..3eaf84c
--- /dev/null
@@ -0,0 +1,255 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginFrame.h
+ *
+*/
+#ifndef ZYPP_PLUGINFRAME_H
+#define ZYPP_PLUGINFRAME_H
+
+#include <iosfwd>
+#include <string>
+#include <map>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/SafeBool.h"
+
+#include "zypp/PluginFrameException.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Command frame for communication with \ref PluginScript
+   *
+   * \code
+   *   COMMAND
+   *   key:value header lines
+   *
+   *   multiline body separated from header
+   *   by an empty line and terminated by NUL.
+   *   ^@
+   * \endcode
+   *
+   * \see PluginScript
+   */
+  class PluginFrame : private base::SafeBool<PluginFrame>
+  {
+    friend std::ostream & operator<<( std::ostream & str, const PluginFrame & obj );
+    friend bool operator==( const PluginFrame & lhs, const PluginFrame & rhs );
+
+    public:
+      /** "ACK" command. */
+      static const std::string & ackCommand();
+      /** "ERROR" command. */
+      static const std::string & errorCommand();
+
+    public:
+      /** Default exception type */
+      typedef PluginFrameException Exception;
+
+      /** Default ctor (empty frame) */
+      PluginFrame();
+
+      /** Ctor taking the command
+       * \throw PluginFrameException If \ref setCommand throws
+       */
+      PluginFrame( const std::string & command_r );
+
+      /** Ctor taking command and body
+       * \throw PluginFrameException If \ref setCommand throws
+       */
+      PluginFrame( const std::string & command_r, const std::string body_r );
+
+      /** Ctor reading frame data from a stream
+       * \throw PluginFrameException On error reading from stream
+       * \throw PluginFrameException On error parsing the data
+       */
+      PluginFrame( std::istream & stream_r );
+
+    public:
+      /** Whether this is an empty frame. */
+      bool empty() const;
+
+      /** Evaluate in a boolean context (empty frame) */
+      using base::SafeBool<PluginFrame>::operator bool_type;
+
+    public:
+      /** Return the frame command. */
+      const std::string & command() const;
+
+      /** Set the frame command
+       * \throw PluginFrameException If illegal command string (e.g. multiline)
+       */
+      void setCommand( const std::string & command_r );
+
+      /** Convenience to identify an ACK command. */
+      bool isAckCommand() const
+      { return command() == ackCommand(); }
+
+      /** Convenience to identify an ERROR command. */
+      bool isErrorCommand() const
+      {return command() == errorCommand(); }
+
+      /** Return the frame body. */
+      const std::string & body() const;
+
+      /** Return a reference to the frame body.
+       * This may avoid creating unnecessary copies if you
+       * want to manipulate large body data.
+       * \code
+       *   std::string tmp;
+       *   frame.bodyRef().swap( tmp );
+       * \endcode
+       */
+      std::string & bodyRef();
+
+      /** Set the frame body */
+      void setBody( const std::string & body_r );
+
+    public:
+      /** The header list */
+      typedef std::multimap<std::string, std::string> HeaderList;
+
+      /** Header list iterator */
+      typedef HeaderList::const_iterator HeaderListIterator;
+
+    private:
+      /** Modifyalble header list for internal use only. */
+      HeaderList & headerList();
+
+    public:
+      /** The header list. */
+      const HeaderList & headerList() const;
+
+      /** Whether header list is empty. */
+      bool headerEmpty() const
+      { return headerList().empty(); }
+
+      /** Return size of the header list. */
+      unsigned headerSize() const
+      { return headerList().size(); }
+
+      /** Return iterator pointing to the 1st header (or \ref headerEnd) */
+      HeaderListIterator headerBegin() const
+      { return headerList().begin(); }
+
+      /** Return iterator pointing behind the last header. */
+      HeaderListIterator headerEnd() const
+      { return headerList().end(); }
+
+      /** Clear the list of headers. */
+      void headerClear()
+      { headerList().clear(); }
+
+
+      /** Whether the header list contains at least one entry for \c key_r. */
+      bool hasKey( const std::string & key_r ) const
+      { return keyEmpty( key_r ); }
+
+      /** \overload */
+      bool keyEmpty( const std::string & key_r ) const
+      { return headerList().find( key_r ) == headerEnd(); }
+
+      /** Return number of header entires for \c key_r. */
+      bool keySize( const std::string & key_r ) const
+      { return headerList().count( key_r ); }
+
+      /** Return iterator pointing to the 1st header for \c key_r (or \ref keyEnd(key_r)) */
+      HeaderListIterator keyBegin( const std::string & key_r ) const
+      { return headerList().lower_bound( key_r ); }
+
+      /** Return iterator pointing behind the last header for \c key_r.*/
+      HeaderListIterator keyEnd( const std::string & key_r ) const
+      { return headerList().upper_bound( key_r ); }
+
+
+      /** Return header value for \c key_r.
+       * \throw PluginFrameException If no header for key_r exists.
+       * \throw PluginFrameException If multiple header for key_r exist.
+       */
+      const std::string & getHeader( const std::string & key_r ) const;
+
+      /** Return header value for \c key_r or \c default_r if it does not exist.
+       * \throw PluginFrameException If multiple header for key_r exist.
+       */
+      const std::string & getHeader( const std::string & key_r, const std::string & default_r ) const;
+
+      /** Not throwing version returing one of the matching header values or \c default_r string. */
+      const std::string & getHeaderNT( const std::string & key_r, const std::string & default_r = std::string() ) const;
+
+      /** Set header for \c key_r removing all other occurences of \c key_r.
+       * \throw PluginFrameException If key contains illegal chars (\c NL or \c :)
+       * \throw PluginFrameException If value contains illegal chars (\c NL)
+       */
+      void setHeader( const std::string & key_r, const std::string & value_r = std::string() );
+
+      /** Add header for \c key_r leaving already existing headers for \c key_r unchanged.
+       * \throw PluginFrameException If key contains illegal chars (\c NL or \c :)
+       * \throw PluginFrameException If value contains illegal chars (\c NL)
+       */
+      void addHeader( const std::string & key_r, const std::string & value_r = std::string() );
+
+      /** Remove all headers for \c key_r. */
+      void clearHeader( const std::string & key_r );
+
+    public:
+      /** Write frame to stream
+       * \throw PluginFrameException On error writing to stream
+       */
+      std::ostream & writeTo( std::ostream & stream_r ) const;
+
+      /** \overload Static version. */
+      static std::ostream & writeTo( std::ostream & stream_r, const PluginFrame & frame_r )
+      { return frame_r.writeTo( stream_r ); }
+
+      /** Read frame from stream
+       * \throw PluginFrameException If \ref PluginFrame(std::istream&) throws
+       */
+      std::istream & readFrom( std::istream & stream_r )
+      { *this = PluginFrame( stream_r ); return stream_r; }
+
+      /** \overload Static version. */
+      static std::istream & readFrom( std::istream & stream_r, PluginFrame & frame_r )
+      { frame_r = PluginFrame( stream_r ); return stream_r; }
+
+    private:
+      friend base::SafeBool<PluginFrame>::operator bool_type() const;
+      /** Evaluate in a boolean context */
+      bool boolTest() const
+      { return empty(); }
+    public:
+      /** Implementation */
+      class Impl;
+    private:
+      /** Pointer to implementation */
+      RWCOW_pointer<Impl> _pimpl;
+  };
+
+  /** \relates PluginFrame Stream output for logging */
+  std::ostream & operator<<( std::ostream & str, const PluginFrame & obj );
+
+  /** \relates PluginFrame Stream output sending all data */
+  inline std::ostream & dumpOn( std::ostream & str, const PluginFrame & obj )
+  { return PluginFrame::writeTo( str, obj ); }
+
+  /** \relates PluginFrame Construct from stream. */
+  inline std::istream & operator>>( std::istream & str, PluginFrame & obj )
+  { return PluginFrame::readFrom( str, obj ); }
+
+  /** \relates PluginFrame Comparison based on content. */
+  bool operator==( const PluginFrame & lhs, const PluginFrame & rhs );
+
+  /** \relates PluginFrame Comparison based on content. */
+  inline bool operator!=( const PluginFrame & lhs, const PluginFrame & rhs )
+  { return( ! operator==( lhs, rhs ) ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PLUGINFRAME_H
diff --git a/zypp/PluginFrameException.cc b/zypp/PluginFrameException.cc
new file mode 100644 (file)
index 0000000..bbe72e8
--- /dev/null
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginFrameException.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/LogTools.h"
+
+#include "zypp/PluginFrameException.h"
+#include "zypp/PluginFrame.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  PluginFrameException::PluginFrameException()
+    : Exception( "PluginFrameException" )
+  {}
+
+  PluginFrameException::PluginFrameException( const std::string & msg_r )
+    : Exception( msg_r )
+  {}
+
+  PluginFrameException::PluginFrameException( const std::string & msg_r, const std::string & hist_r )
+    : Exception( msg_r )
+  { addHistory( hist_r ); }
+
+  PluginFrameException::~PluginFrameException() throw()
+  {};
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PluginFrameException.h b/zypp/PluginFrameException.h
new file mode 100644 (file)
index 0000000..1451621
--- /dev/null
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginFrameException.h
+ *
+*/
+#ifndef ZYPP_PLUGINFRAMEEXCEPTION_H
+#define ZYPP_PLUGINFRAMEEXCEPTION_H
+
+#include <iosfwd>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginFrameException
+  //
+  /** Base class for \ref PluginFrame \ref Exception. */
+  class PluginFrameException : public Exception
+  {
+    public:
+      PluginFrameException();
+      PluginFrameException( const std::string & msg_r );
+      PluginFrameException( const std::string & msg_r, const std::string & hist_r );
+      virtual ~PluginFrameException() throw();
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PLUGINFRAMEEXCEPTION_H
diff --git a/zypp/PluginScript.cc b/zypp/PluginScript.cc
new file mode 100644 (file)
index 0000000..a080c4f
--- /dev/null
@@ -0,0 +1,515 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginScript.cc
+ *
+*/
+#include <sys/types.h>
+#include <signal.h>
+
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Signal.h"
+#include "zypp/base/IOStream.h"
+
+#include "zypp/PluginScript.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/PathInfo.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace
+  {
+    const char * PLUGIN_DEBUG = getenv( "ZYPP_PLUGIN_DEBUG" );
+
+    /** Dump buffer string if PLUGIN_DEBUG is on.
+     * \ingroup g_RAII
+     */
+    struct PluginDebugBuffer
+    {
+      PluginDebugBuffer( const std::string & buffer_r ) : _buffer( buffer_r ) {}
+      ~PluginDebugBuffer()
+      {
+       if ( PLUGIN_DEBUG )
+       {
+         if ( _buffer.empty() )
+         {
+           _DBG("PLUGIN") << "< (empty)" << endl;
+         }
+         else
+         {
+           std::istringstream datas( _buffer );
+           iostr::copyIndent( datas, _DBG("PLUGIN"), "< "  ) << endl;
+         }
+       }
+      }
+      const std::string & _buffer;
+    };
+
+    /** Dump programms stderr.
+     * \ingroup g_RAII
+     */
+    struct PluginDumpStderr
+    {
+      PluginDumpStderr( ExternalProgramWithStderr & prog_r ) : _prog( prog_r ) {}
+      ~PluginDumpStderr()
+      {
+       std::string line;
+       while ( _prog.stderrGetline( line ) )
+         _WAR("PLUGIN") << "! " << line << endl;
+      }
+      ExternalProgramWithStderr & _prog;
+    };
+
+    inline void setBlocking( FILE * file_r, bool yesno_r = true )
+    {
+      if ( ! file_r )
+       ZYPP_THROW( PluginScriptException( "setNonBlocking" ) );
+
+      int fd = ::fileno( file_r );
+      if ( fd == -1 )
+       ZYPP_THROW( PluginScriptException( "setNonBlocking" ) );
+
+      int flags = ::fcntl( fd, F_GETFL );
+      if ( flags == -1 )
+       ZYPP_THROW( PluginScriptException( "setNonBlocking" ) );
+
+      if ( ! yesno_r )
+       flags |= O_NONBLOCK;
+      else if ( flags & O_NONBLOCK )
+       flags ^= O_NONBLOCK;
+
+      flags = ::fcntl( fd, F_SETFL, flags );
+      if ( flags == -1 )
+       ZYPP_THROW( PluginScriptException( "setNonBlocking" ) );
+    }
+
+    inline void setNonBlocking( FILE * file_r, bool yesno_r = true )
+    { setBlocking( file_r, !yesno_r ); }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginScript::Impl
+  //
+  /** \ref PluginScript implementation. */
+  struct PluginScript::Impl
+  {
+    public:
+      Impl( const Pathname & script_r = Pathname(), const Arguments & args_r = Arguments() )
+        : _sendTimeout( _defaultSendTimeout )
+        , _receiveTimeout( _defaultReceiveTimeout )
+        , _script( script_r )
+        , _args( args_r )
+      {}
+
+      ~ Impl()
+      { try { close(); } catch(...) {} }
+
+    public:
+      static long _defaultSendTimeout;
+      static long _defaultReceiveTimeout;
+
+      long _sendTimeout;
+      long _receiveTimeout;
+
+   public:
+      const Pathname & script() const
+      { return _script; }
+
+      const Arguments & args() const
+      { return _args; }
+
+      pid_t getPid() const
+      { return _cmd ? _cmd->getpid() : NotConnected; }
+
+      bool isOpen() const
+      { return _cmd; }
+
+      int lastReturn() const
+      { return _lastReturn; }
+
+      const std::string & lastExecError() const
+      { return _lastExecError; }
+
+    public:
+      void open( const Pathname & script_r = Pathname(), const Arguments & args_r = Arguments() );
+
+      int close();
+
+      void send( const PluginFrame & frame_r ) const;
+
+      PluginFrame receive() const;
+
+    private:
+      Pathname _script;
+      Arguments _args;
+      scoped_ptr<ExternalProgramWithStderr> _cmd;
+      DefaultIntegral<int,0> _lastReturn;
+      std::string _lastExecError;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PluginScrip::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PluginScript::Impl & obj )
+  {
+    return dumpRangeLine( str << "PluginScript[" << obj.getPid() << "] " << obj.script(),
+                         obj.args().begin(), obj.args().end() );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  namespace
+  {
+    const long PLUGIN_TIMEOUT =        str::strtonum<long>( getenv( "ZYPP_PLUGIN_TIMEOUT" ) );
+    const long PLUGIN_SEND_TIMEOUT =   str::strtonum<long>( getenv( "ZYPP_PLUGIN_SEND_TIMEOUT" ) );
+    const long PLUGIN_RECEIVE_TIMEOUT =        str::strtonum<long>( getenv( "ZYPP_PLUGIN_RECEIVE_TIMEOUT" ) );
+  }
+
+  long PluginScript::Impl::_defaultSendTimeout =    ( PLUGIN_SEND_TIMEOUT > 0    ? PLUGIN_SEND_TIMEOUT
+                                                                                : ( PLUGIN_TIMEOUT > 0 ? PLUGIN_TIMEOUT : 30 ) );
+  long PluginScript::Impl::_defaultReceiveTimeout = ( PLUGIN_RECEIVE_TIMEOUT > 0 ? PLUGIN_RECEIVE_TIMEOUT
+                                                                                : ( PLUGIN_TIMEOUT > 0 ? PLUGIN_TIMEOUT : 30 ) );
+
+  ///////////////////////////////////////////////////////////////////
+
+  void PluginScript::Impl::open( const Pathname & script_r, const Arguments & args_r )
+  {
+    dumpRangeLine( DBG << "Open " << script_r, args_r.begin(), args_r.end() ) << endl;
+
+    if ( _cmd )
+      ZYPP_THROW( PluginScriptException( "Already connected", str::Str() << *this ) );
+
+    {
+      PathInfo pi( script_r );
+      if ( ! ( pi.isFile() && pi.isX() ) )
+       ZYPP_THROW( PluginScriptException( "Script is not executable", str::Str() << pi ) );
+    }
+
+    // go and launch script
+    // TODO: ExternalProgram::maybe use Stderr_To_FileDesc for script loging
+    Arguments args;
+    args.reserve( args_r.size()+1 );
+    args.push_back( script_r.asString() );
+    args.insert( args.end(), args_r.begin(), args_r.end() );
+    _cmd.reset( new ExternalProgramWithStderr( args ) );
+
+    // Be protected against full pipe, etc.
+    setNonBlocking( _cmd->outputFile() );
+    setNonBlocking( _cmd->inputFile() );
+
+    // store running scripts data
+    _script = script_r;
+    _args = args_r;
+    _lastReturn.reset();
+    _lastExecError.clear();
+
+    DBG << *this << endl;
+  }
+
+  int PluginScript::Impl::close()
+  {
+    if ( _cmd )
+    {
+      DBG << "Close:" << *this << endl;
+      bool doKill = true;
+      try {
+       // do not kill script if _DISCONNECT is ACKed.
+       send( PluginFrame( "_DISCONNECT" ) );
+       PluginFrame ret( receive() );
+       if ( ret.isAckCommand() )
+       {
+         doKill = false;
+         str::strtonum( ret.getHeaderNT( "exit" ), _lastReturn.get() );
+         _lastExecError = ret.body();
+       }
+      }
+      catch (...)
+      { /* NOP */ }
+
+      if ( doKill )
+      {
+       _cmd->kill();
+       _lastReturn = _cmd->close();
+       _lastExecError = _cmd->execError();
+      }
+      DBG << *this << " -> [" << _lastReturn << "] " << _lastExecError << endl;
+      _cmd.reset();
+    }
+    return _lastReturn;
+  }
+
+  void PluginScript::Impl::send( const PluginFrame & frame_r ) const
+  {
+    if ( !_cmd )
+      ZYPP_THROW( PluginScriptNotConnected( "Not connected", str::Str() << *this ) );
+
+    if ( frame_r.command().empty() )
+      WAR << "Send: No command in frame" << frame_r << endl;
+
+    // prepare frame data to write
+    std::string data;
+    {
+      std::ostringstream datas;
+      frame_r.writeTo( datas );
+      datas.str().swap( data );
+    }
+    DBG << "->send " << frame_r << endl;
+
+    if ( PLUGIN_DEBUG )
+    {
+      std::istringstream datas( data );
+      iostr::copyIndent( datas, _DBG("PLUGIN") ) << endl;
+    }
+
+    // try writing the pipe....
+    FILE * filep = _cmd->outputFile();
+    if ( ! filep )
+      ZYPP_THROW( PluginScriptException( "Bad file pointer." ) );
+
+    int fd = ::fileno( filep );
+    if ( fd == -1 )
+      ZYPP_THROW( PluginScriptException( "Bad file descriptor" ) );
+
+    //DBG << " ->[" << fd << " " << (::feof(filep)?'e':'_') << (::ferror(filep)?'F':'_') << "]" << endl;
+    {
+      PluginDumpStderr _dump( *_cmd ); // dump scripts stderr before leaving
+      SignalSaver sigsav( SIGPIPE, SIG_IGN );
+      const char * buffer = data.c_str();
+      ssize_t buffsize = data.size();
+      do {
+       fd_set wfds;
+       FD_ZERO( &wfds );
+       FD_SET( fd, &wfds );
+
+       struct timeval tv;
+       tv.tv_sec = _sendTimeout;
+       tv.tv_usec = 0;
+
+       int retval = select( fd+1, NULL, &wfds, NULL, &tv );
+       if ( retval > 0 )       // FD_ISSET( fd, &wfds ) will be true.
+       {
+         //DBG << "Ready to write..." << endl;
+         ssize_t ret = ::write( fd, buffer, buffsize );
+         if ( ret == buffsize )
+         {
+           //DBG << "::write(" << buffsize << ") -> " << ret << endl;
+           ::fflush( filep );
+           break;              // -> done
+         }
+         else if ( ret > 0 )
+         {
+           //WAR << "::write(" << buffsize << ") -> " << ret << " INCOMPLETE..." << endl;
+           ::fflush( filep );
+           buffsize -= ret;
+           buffer += ret;      // -> continue
+         }
+         else // ( retval == -1 )
+         {
+           if ( errno != EINTR )
+           {
+             ERR << "write(): " << Errno() << endl;
+             if ( errno == EPIPE )
+               ZYPP_THROW( PluginScriptDiedUnexpectedly( "Send: script died unexpectedly", str::Str() << Errno() ) );
+             else
+               ZYPP_THROW( PluginScriptException( "Send: send error", str::Str() << Errno() ) );
+           }
+         }
+       }
+       else if ( retval == 0 )
+       {
+         WAR << "Not ready to write within timeout." << endl;
+         ZYPP_THROW( PluginScriptSendTimeout( "Not ready to write within timeout." ) );
+       }
+       else // ( retval == -1 )
+       {
+         if ( errno != EINTR )
+         {
+           ERR << "select(): " << Errno() << endl;
+           ZYPP_THROW( PluginScriptException( "Error waiting on file descriptor", str::Str() << Errno() ) );
+         }
+       }
+      } while( true );
+    }
+  }
+
+  PluginFrame PluginScript::Impl::receive() const
+  {
+    if ( !_cmd )
+      ZYPP_THROW( PluginScriptNotConnected( "Not connected", str::Str() << *this ) );
+
+    // try reading the pipe....
+    FILE * filep = _cmd->inputFile();
+    if ( ! filep )
+      ZYPP_THROW( PluginScriptException( "Bad file pointer." ) );
+
+    int fd = ::fileno( filep );
+    if ( fd == -1 )
+      ZYPP_THROW( PluginScriptException( "Bad file descriptor" ) );
+
+    ::clearerr( filep );
+    std::string data;
+    {
+      PluginDebugBuffer _debug( data ); // dump receive buffer if PLUGIN_DEBUG
+      PluginDumpStderr _dump( *_cmd ); // dump scripts stderr before leaving
+      do {
+       int ch = fgetc( filep );
+       if ( ch != EOF )
+       {
+         data.push_back( ch );
+         if ( ch == '\0' )
+           break;
+       }
+       else if ( ::feof( filep ) )
+       {
+         WAR << "Unexpected EOF" << endl;
+         ZYPP_THROW( PluginScriptDiedUnexpectedly( "Receive: script died unexpectedly", str::Str() << Errno() ) );
+       }
+       else if ( errno != EINTR )
+       {
+         if ( errno == EWOULDBLOCK )
+         {
+           // wait a while for fd to become ready for reading...
+           fd_set rfds;
+           FD_ZERO( &rfds );
+           FD_SET( fd, &rfds );
+
+           struct timeval tv;
+           tv.tv_sec = _receiveTimeout;
+           tv.tv_usec = 0;
+
+           int retval = select( fd+1, &rfds, NULL, NULL, &tv );
+           if ( retval > 0 )   // FD_ISSET( fd, &rfds ) will be true.
+           {
+             ::clearerr( filep );
+           }
+           else if ( retval == 0 )
+           {
+             WAR << "Not ready to read within timeout." << endl;
+             ZYPP_THROW( PluginScriptReceiveTimeout( "Not ready to read within timeout." ) );
+           }
+           else // ( retval == -1 )
+           {
+             if ( errno != EINTR )
+             {
+               ERR << "select(): " << Errno() << endl;
+               ZYPP_THROW( PluginScriptException( "Error waiting on file descriptor", str::Str() << Errno() ) );
+             }
+           }
+         }
+         else
+         {
+           ERR << "read(): " << Errno() << endl;
+           ZYPP_THROW( PluginScriptException( "Receive: receive error", str::Str() << Errno() ) );
+         }
+       }
+      } while ( true );
+    }
+    // DBG << " <-read " << data.size() << endl;
+    std::istringstream datas( data );
+    PluginFrame ret( datas );
+    DBG << "<-" << ret << endl;
+    return ret;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PluginScript
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const pid_t PluginScript::NotConnected( -1 );
+
+  long PluginScript::defaultSendTimeout()
+  { return Impl::_defaultSendTimeout; }
+
+  long PluginScript::defaultReceiveTimeout()
+  { return Impl::_defaultReceiveTimeout; }
+
+  void PluginScript::defaultSendTimeout( long newval_r )
+  { Impl::_defaultSendTimeout = newval_r > 0 ? newval_r : 0; }
+
+  void PluginScript::defaultReceiveTimeout( long newval_r )
+  { Impl::_defaultReceiveTimeout = newval_r > 0 ? newval_r : 0; }
+
+  long PluginScript::sendTimeout() const
+  { return _pimpl->_sendTimeout; }
+
+  long PluginScript::receiveTimeout() const
+  { return _pimpl->_receiveTimeout; }
+
+  void PluginScript::sendTimeout( long newval_r )
+  { _pimpl->_sendTimeout = newval_r > 0 ? newval_r : 0; }
+
+  void PluginScript::receiveTimeout( long newval_r )
+  { _pimpl->_receiveTimeout = newval_r > 0 ? newval_r : 0; }
+
+  PluginScript::PluginScript()
+    : _pimpl( new Impl )
+  {}
+
+  PluginScript::PluginScript( const Pathname & script_r )
+    : _pimpl( new Impl( script_r ) )
+  {}
+
+  PluginScript::PluginScript( const Pathname & script_r, const Arguments & args_r )
+    : _pimpl( new Impl( script_r, args_r ) )
+  {}
+
+  const Pathname & PluginScript::script() const
+  { return _pimpl->script(); }
+
+  const PluginScript::Arguments & PluginScript::args() const
+  { return _pimpl->args(); }
+
+  bool PluginScript::isOpen() const
+  { return _pimpl->isOpen(); }
+
+  pid_t PluginScript::getPid() const
+  { return _pimpl->getPid(); }
+
+  int PluginScript::lastReturn() const
+  { return _pimpl->lastReturn(); }
+
+  const std::string & PluginScript::lastExecError() const
+  { return _pimpl->lastExecError(); }
+
+  void PluginScript::open()
+  { _pimpl->open( _pimpl->script(), _pimpl->args() ); }
+
+  void PluginScript::open( const Pathname & script_r )
+  { _pimpl->open( script_r ); }
+
+  void PluginScript::open( const Pathname & script_r, const Arguments & args_r )
+  { _pimpl->open( script_r, args_r ); }
+
+  int PluginScript::close()
+  { return _pimpl->close(); }
+
+  void PluginScript::send( const PluginFrame & frame_r ) const
+  { _pimpl->send( frame_r ); }
+
+  PluginFrame PluginScript::receive() const
+  { return _pimpl->receive(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<( std::ostream & str, const PluginScript & obj )
+  { return str << *obj._pimpl; }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PluginScript.h b/zypp/PluginScript.h
new file mode 100644 (file)
index 0000000..2f1e4d9
--- /dev/null
@@ -0,0 +1,197 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginScript.h
+ *
+*/
+#ifndef ZYPP_PLUGINSCRIPT_H
+#define ZYPP_PLUGINSCRIPT_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/PluginFrame.h"
+#include "zypp/PluginScriptException.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /**
+   * \brief Interface to pluigin scripts using a \c Stomp inspired communication protocol.
+   *
+   * \note \ref PluginScript is copyable and assignable, but the connection is shared
+   * among multiple copies. It gets automatically closed if the last copy goes out of
+   * scope.
+   *
+   * Timeout when sending/receiving data to/from a plugin default to 30 sec. The value
+   * (in seconds) my be changed via the environment variables \c ZYPP_PLUGIN_SEND_TIMEOUT,
+   * \c ZYPP_PLUGIN_RECEIVE_TIMEOUT or \c ZYPP_PLUGIN_TIMEOUT (both: send and receive).
+   *
+   * \code
+   *  // Setup comnnection to plugin script
+   *  PluginScript scr;
+   *  PluginScript::Arguments args;
+   *  args.push_back( "-v" );
+   *  scr.open( "/soem/testplugin", args );
+   *
+   *  // send frame to plugin
+   *  PluginFrame f( "COMMAND" );
+   *  f.setHeader( "key", "value" );
+   *  f.setBody( "some\ndata" );
+   *  scr.send( f );
+   *
+   *  // receive frame from plugin
+   *  PluginFrame r( scr.receive() );
+   *
+   *  // explicitly close or let PluginScript go out of scope
+   *  scr.close();
+   * \endcode
+   *
+   * \see http://stomp.codehaus.org/
+   */
+  class PluginScript
+  {
+    friend std::ostream & operator<<( std::ostream & str, const PluginScript & obj );
+
+    public:
+      /** Commandline arguments passed to a script on \ref open. */
+      typedef std::vector<std::string> Arguments;
+
+      /** \c pid_t(-1) constant indicating no connection. */
+      static const pid_t NotConnected;
+
+    public:
+      /** \name Get/set the global timeout settings.
+       * Timeout when sending/receiving data to/from a plugin default to 30 sec. The value
+       * (in seconds) my be changed via the environment variables \c ZYPP_PLUGIN_SEND_TIMEOUT,
+       * \c ZYPP_PLUGIN_RECEIVE_TIMEOUT or \c ZYPP_PLUGIN_TIMEOUT (both: send and receive).
+       */
+      //@{
+       /** Global default timeout (sec.) when sending data. */
+       static long defaultSendTimeout();
+
+       /** Global default timeout (sec.) when receiving data. */
+       static long defaultReceiveTimeout();
+
+       /** Set global default timeout (sec.) when sending data. */
+       static void defaultSendTimeout( long newval_r );
+
+       /** Set global default timeout (sec.) when receiving data. */
+       static void defaultReceiveTimeout( long newval_r );
+
+       /** Set global default timeout (sec.) (both: send and receive).*/
+       static void defaultTimeout( long newval_r )
+       { defaultSendTimeout( newval_r ); defaultReceiveTimeout( newval_r ); }
+      //@}
+
+    public:
+      /** Default ctor. */
+      PluginScript();
+
+      /** Ctor taking script path and no arguments. */
+      PluginScript( const Pathname & script_r );
+
+      /** Ctor taking script path and script arguments. */
+      PluginScript( const Pathname & script_r, const Arguments & args_r );
+
+    public:
+      /** Return the script path if set. */
+      const Pathname & script() const;
+
+      /** Return the script arguments if set. */
+      const Arguments & args() const;
+
+      /** Whether we are connected to a script. */
+      bool isOpen() const;
+
+      /** Return a connected scripts pid or \ref NotConnected. */
+      pid_t getPid() const;
+
+      /** Remembers a scripts return value after \ref close until next \ref open. */
+      int lastReturn() const;
+
+      /** Remembers a scripts execError string after \ref close until next \ref open.
+       * \see \ref ExternalProgram::execError.
+       */
+      const std::string & lastExecError() const;
+
+     public:
+      /** \name Get/set local timeout settings. */
+      //@{
+       /** Local default timeout (sec.) when sending data. */
+       long sendTimeout() const;
+
+       /** Local default timeout (sec.) when receiving data. */
+       long receiveTimeout() const;
+
+       /** Set local default timeout (sec.) when sending data. */
+       void sendTimeout( long newval_r );
+
+       /** Set local default timeout (sec.) when receiving data. */
+       void receiveTimeout( long newval_r );
+
+       /** Set local default timeout (sec.) (both: send and receive).*/
+       void timeout( long newval_r )
+       { sendTimeout( newval_r ); receiveTimeout( newval_r ); }
+      //@}
+
+   public:
+      /** Setup connection and execute script.
+       * \throw PluginScriptException if already connected to a script
+       * \throw PluginScriptException if script does not exist or is not executable
+       * \throw PluginScriptException on error
+       */
+      void open();
+
+      /** \overload taking script path and no arguments. */
+      void open( const Pathname & script_r );
+
+      /** \overload taking script path and script arguments. */
+      void open( const Pathname & script_r, const Arguments & args_r );
+
+      /** Close any open connection. */
+      int close();
+
+    public:
+      /** Send a \ref PluginFrame.
+       * \throw PluginScriptNotConnected
+       * \throw PluginScriptSendTimeout
+       * \throw PluginScriptDiedUnexpectedly (does not \ref close)
+       * \throw PluginScriptException on error
+       *
+       */
+      void send( const PluginFrame & frame_r ) const;
+
+      /** Receive a \ref PluginFrame.
+       * \throw PluginScriptNotConnected
+       * \throw PluginScriptReceiveTimeout
+       * \throw PluginScriptDiedUnexpectedly (does not \ref close)
+       * \throw PluginScriptException on error
+       */
+      PluginFrame receive() const;
+
+    public:
+      /** Implementation. */
+      class Impl;
+    private:
+      /** Pointer to implementation. */
+      RW_pointer<Impl> _pimpl;
+  };
+
+  /** \relates PluginScript Stream output */
+  std::ostream & operator<<( std::ostream & str, const PluginScript & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PLUGINSCRIPT_H
diff --git a/zypp/PluginScriptException.cc b/zypp/PluginScriptException.cc
new file mode 100644 (file)
index 0000000..77e2a21
--- /dev/null
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginScriptException.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/LogTools.h"
+
+#include "zypp/PluginScriptException.h"
+#include "zypp/PluginScript.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  PluginScriptException::PluginScriptException()
+    : Exception( "PluginScriptException" )
+  {}
+
+  PluginScriptException::PluginScriptException( const std::string & msg_r )
+    : Exception( msg_r )
+  {}
+
+  PluginScriptException::PluginScriptException( const std::string & msg_r, const std::string & hist_r )
+    : Exception( msg_r )
+  { addHistory( hist_r ); }
+
+  PluginScriptException::~PluginScriptException() throw()
+  {}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PluginScriptException.h b/zypp/PluginScriptException.h
new file mode 100644 (file)
index 0000000..bf8c8cf
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PluginScriptException.h
+ *
+*/
+#ifndef ZYPP_PLUGINSCRIPTEXCEPTION_H
+#define ZYPP_PLUGINSCRIPTEXCEPTION_H
+
+#include <iosfwd>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Base class for \ref PluginScript \ref Exception. */
+  class PluginScriptException : public Exception
+  {
+    public:
+      PluginScriptException();
+      PluginScriptException( const std::string & msg_r );
+      PluginScriptException( const std::string & msg_r, const std::string & hist_r );
+      virtual ~PluginScriptException() throw();
+  };
+
+  /** Convenience macro to declare more specific PluginScriptExceptions. */
+#define declException( EXCP, BASE )                                                            \
+  class EXCP : public BASE {                                                                   \
+    public:                                                                                    \
+      EXCP() : BASE( #EXCP ) {}                                                                        \
+      EXCP( const std::string & msg_r ) : BASE( msg_r ) {}                                     \
+      EXCP( const std::string & msg_r, const std::string & hist_r ) : BASE( msg_r, hist_r ) {} \
+      virtual ~EXCP() throw() {}                                                               \
+  }
+
+  /** Script connection not open. */
+  declException( PluginScriptNotConnected, PluginScriptException );
+
+  /** Script died unexpectedly. */
+  declException( PluginScriptDiedUnexpectedly, PluginScriptException );
+
+
+  /** Communication timeout. */
+  declException( PluginScriptTimeout, PluginScriptException );
+
+  /** Timeout while sending data. */
+  declException( PluginScriptSendTimeout, PluginScriptTimeout );
+
+  /** Timeout while receiving data. */
+  declException( PluginScriptReceiveTimeout, PluginScriptTimeout );
+
+#undef declException
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PLUGINSCRIPTEXCEPTION_H
diff --git a/zypp/PoolItem.cc b/zypp/PoolItem.cc
new file mode 100644 (file)
index 0000000..3f9e633
--- /dev/null
@@ -0,0 +1,295 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolItem.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/ResPool.h"
+#include "zypp/Package.h"
+#include "zypp/VendorAttr.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItem::Impl
+  //
+  /** PoolItem implementation.
+   * \c _buddy handling:
+   * \li \c ==0 no buddy
+   * \li \c >0 this uses \c _buddy status
+   * \li \c <0 this status used by \c -_buddy
+   */
+  struct PoolItem::Impl
+  {
+    public:
+      Impl() {}
+
+      Impl( ResObject::constPtr res_r,
+            const ResStatus & status_r )
+      : _status( status_r )
+      , _resolvable( res_r )
+      {}
+
+      ResStatus & status() const
+      { return _buddy > 0 ? PoolItem(buddy()).status() : _status; }
+
+      sat::Solvable buddy() const
+      {
+        if ( !_buddy )
+          return sat::Solvable::noSolvable;
+        if ( _buddy < 0 )
+          return sat::Solvable( -_buddy );
+        return sat::Solvable( _buddy );
+      }
+
+      void setBuddy( sat::Solvable solv_r );
+
+      ResObject::constPtr resolvable() const
+      { return _resolvable; }
+
+      ResStatus & statusReset() const
+      {
+        _status.setLock( false, zypp::ResStatus::USER );
+        _status.resetTransact( zypp::ResStatus::USER );
+        return _status;
+      }
+
+    public:
+      bool isUndetermined() const
+      {
+         return status().isUndetermined();
+      }
+
+      bool isRelevant() const
+      {
+         return !status().isNonRelevant();
+      }
+
+      bool isSatisfied() const
+      {
+         return status().isSatisfied();
+      }
+
+      bool isBroken() const
+      {
+         return status().isBroken();
+      }
+
+      bool isNeeded() const
+      {
+       return status().isToBeInstalled() || ( isBroken() && ! status().isLocked() );
+      }
+
+      bool isUnwanted() const
+      {
+       return isBroken() && status().isLocked();
+      }
+
+    private:
+      mutable ResStatus     _status;
+      ResObject::constPtr   _resolvable;
+      DefaultIntegral<sat::detail::IdType,sat::detail::noId> _buddy;
+
+    /** \name Poor man's save/restore state.
+       * \todo There may be better save/restore state strategies.
+     */
+    //@{
+    public:
+      void saveState() const
+      { _savedStatus = status(); }
+      void restoreState() const
+      { status() = _savedStatus; }
+      bool sameState() const
+      {
+        if ( status() == _savedStatus )
+          return true;
+        // some bits changed...
+        if ( status().getTransactValue() != _savedStatus.getTransactValue()
+             && ( ! status().isBySolver() // ignore solver state changes
+                  // removing a user lock also goes to bySolver
+                  || _savedStatus.getTransactValue() == ResStatus::LOCKED ) )
+          return false;
+        if ( status().isLicenceConfirmed() != _savedStatus.isLicenceConfirmed() )
+          return false;
+        return true;
+      }
+    private:
+      mutable ResStatus _savedStatus;
+    //@}
+
+    public:
+      /** Offer default Impl. */
+      static shared_ptr<Impl> nullimpl()
+      {
+        static shared_ptr<Impl> _nullimpl( new Impl );
+        return _nullimpl;
+      }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolItem::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PoolItem::Impl & obj )
+  {
+    str << obj.status();
+    if (obj.resolvable())
+       str << *obj.resolvable();
+    else
+       str << "(NULL)";
+    return str;
+  }
+
+  inline void PoolItem::Impl::setBuddy( sat::Solvable solv_r )
+  {
+    PoolItem myBuddy( solv_r );
+    if ( myBuddy )
+    {
+      myBuddy._pimpl->_buddy = -resolvable()->satSolvable().id();
+      _buddy = myBuddy.satSolvable().id();
+      DBG << *this << " has buddy " << myBuddy << endl;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItem
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::PoolItem
+  //   METHOD TYPE : Ctor
+  //
+  PoolItem::PoolItem()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::PoolItem
+  //   METHOD TYPE : Ctor
+  //
+  PoolItem::PoolItem( const sat::Solvable & solvable_r )
+  : _pimpl( ResPool::instance().find( solvable_r )._pimpl )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::PoolItem
+  //   METHOD TYPE : Ctor
+  //
+  PoolItem::PoolItem( const ResObject::constPtr & resolvable_r )
+  : _pimpl( ResPool::instance().find( resolvable_r )._pimpl )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::PoolItem
+  //   METHOD TYPE : Ctor
+  //
+  PoolItem::PoolItem( Impl * implptr_r )
+  : _pimpl( implptr_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::makePoolItem
+  //   METHOD TYPE : PoolItem
+  //
+  PoolItem PoolItem::makePoolItem( const sat::Solvable & solvable_r )
+  {
+    return PoolItem( new Impl( makeResObject( solvable_r ), solvable_r.isSystem() ) );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::~PoolItem
+  //   METHOD TYPE : Dtor
+  //
+  PoolItem::~PoolItem()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PoolItem::pool
+  //   METHOD TYPE : ResPool
+  //
+  ResPool PoolItem::pool() const
+  { return ResPool::instance(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   Forward to Impl:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ResStatus & PoolItem::status() const
+  { return _pimpl->status(); }
+
+  ResStatus & PoolItem::statusReset() const
+  { return _pimpl->statusReset(); }
+
+  sat::Solvable PoolItem::buddy() const
+  { return _pimpl->buddy(); }
+
+  void PoolItem::setBuddy( sat::Solvable solv_r )
+  { _pimpl->setBuddy( solv_r ); }
+
+  bool PoolItem::isUndetermined() const
+  { return _pimpl->isUndetermined(); }
+
+  bool PoolItem::isRelevant() const
+  { return _pimpl->isRelevant(); }
+
+  bool PoolItem::isSatisfied() const
+  { return _pimpl->isSatisfied(); }
+
+  bool PoolItem::isBroken() const
+  { return _pimpl->isBroken(); }
+
+  bool PoolItem::isNeeded() const
+  { return _pimpl->isNeeded(); }
+
+  bool PoolItem::isUnwanted() const
+  { return _pimpl->isUnwanted(); }
+
+  void PoolItem::saveState() const
+  { _pimpl->saveState(); }
+
+  void PoolItem::restoreState() const
+  { _pimpl->restoreState(); }
+
+  bool PoolItem::sameState() const
+  { return _pimpl->sameState(); }
+
+  ResObject::constPtr PoolItem::resolvable() const
+  { return _pimpl->resolvable(); }
+
+  /******************************************************************
+   **
+   **  FUNCTION NAME : operator<<
+   **  FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const PoolItem & obj )
+  {
+    return str << *obj._pimpl;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PoolItem.h b/zypp/PoolItem.h
new file mode 100644 (file)
index 0000000..363cb44
--- /dev/null
@@ -0,0 +1,248 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolItem.h
+ *
+*/
+#ifndef ZYPP_POOLITEM_H
+#define ZYPP_POOLITEM_H
+
+#include <iosfwd>
+#include <functional>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ResObject.h"
+#include "zypp/ResStatus.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class ResPool;
+
+  namespace pool
+  {
+    class PoolImpl;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItem
+  //
+  /** Reference to a PoolItem connecting ResObject and ResStatus.
+   *
+   * The "real" PoolItem is usg. somewhere in the ResPool. This is
+   * a reference to it. All copies made will reference (and modify)
+   * the same PoolItem. All changes via a PoolItem are immediately
+   * visible in all copies (now COW).
+   *
+   * \note Constness: Like pointer types, a <tt>const PoolItem</tt>
+   * does \b not refer to a <tt>const PoolItem</tt>. The reference is
+   * \c const, i.e. you can't change the refered PoolItem. The PoolItem
+   * (i.e. the status) is always mutable.
+   *
+  */
+  class PoolItem
+  {
+    friend std::ostream & operator<<( std::ostream & str, const PoolItem & obj );
+
+    public:
+      /** Implementation */
+      class Impl;
+
+    public:
+      /** Default ctor for use in std::container. */
+      PoolItem();
+
+      /** Ctor looking up the \ref sat::Solvable in the \ref ResPool. */
+      explicit PoolItem( const sat::Solvable & solvable_r );
+
+      /** Ctor looking up the \ref ResObject in the \ref ResPool. */
+      explicit PoolItem( const ResObject::constPtr & resolvable_r );
+
+      /** Dtor */
+      ~PoolItem();
+
+    public:
+      /** \name Status related methods. */
+      //@{
+      /** Returns the current status. */
+      ResStatus & status() const;
+
+      /** Reset status. */
+      ResStatus & statusReset() const;
+
+
+      /** \name Status validation.
+       * Performed for non-packages.
+      */
+      //@{
+      /** No validation is performed for packages. */
+      bool isUndetermined() const;
+
+      /** Returns true if the solvable is relevant which means e.g. for patches
+       *  that at least one package of the patch is installed.
+       */
+      bool isRelevant() const;
+
+      /** Whether a relevant items requirements are met. */
+      bool isSatisfied() const;
+
+      /** Whether a relevant items requirements are broken. */
+      bool isBroken() const;
+
+      /** This includes \c unlocked broken patches, as well as those already
+       * selected to be installed (otherwise classified as \c satisfied).
+       */
+      bool isNeeded() const;
+
+      /** Broken (needed) but locked patches. */
+      bool isUnwanted() const;
+      //@}
+
+      //@}
+    public:
+      /** Return the \ref ResPool the item belongs to. */
+      ResPool pool() const;
+
+      /** Return the corresponding \ref sat::Solvable. */
+      sat::Solvable satSolvable() const
+      { return resolvable() ? resolvable()->satSolvable() : sat::Solvable::noSolvable; }
+
+      /** Return the buddy we share our status object with.
+       * A \ref Product e.g. may share it's status with an associated reference \ref Package.
+      */
+      sat::Solvable buddy() const;
+
+    public:
+      /** Returns the ResObject::constPtr.
+       * \see \ref operator->
+       */
+      ResObject::constPtr resolvable() const;
+
+      /** Implicit conversion into ResObject::constPtr to
+       *  support query filters operating on ResObject.
+       */
+      operator ResObject::constPtr() const
+      { return resolvable(); }
+
+      /** Forward \c -> access to ResObject. */
+      ResObject::constPtr operator->() const
+      { return resolvable(); }
+
+      /** Conversion to bool to allow pointer style tests
+       *  for nonNULL \ref resolvable. */
+      operator ResObject::constPtr::unspecified_bool_type() const
+      { return resolvable(); }
+
+    private:
+      friend class Impl;
+      friend class pool::PoolImpl;
+      /** \ref PoolItem generator for \ref pool::PoolImpl. */
+      static PoolItem makePoolItem( const sat::Solvable & solvable_r );
+      /** Buddies are set by \ref pool::PoolImpl.*/
+      void setBuddy( sat::Solvable solv_r );
+      /** internal ctor */
+      explicit PoolItem( Impl * implptr_r );
+      /** Pointer to implementation */
+      RW_pointer<Impl> _pimpl;
+
+    private:
+      /** \name tmp hack for save/restore state. */
+      /** \todo get rid of it. */
+      //@{
+      friend class PoolItemSaver;
+      void saveState() const;
+      void restoreState() const;
+      bool sameState() const;
+      //@}
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolItem Stream output */
+  std::ostream & operator<<( std::ostream & str, const PoolItem & obj );
+
+  /** \relates PoolItem */
+  inline bool operator==( const PoolItem & lhs, const PoolItem & rhs )
+  { return lhs.resolvable() == rhs.resolvable(); }
+
+  /** \relates PoolItem */
+  inline bool operator==( const PoolItem & lhs, const ResObject::constPtr & rhs )
+  { return lhs.resolvable() == rhs; }
+
+  /** \relates PoolItem */
+  inline bool operator==( const ResObject::constPtr & lhs, const PoolItem & rhs )
+  { return lhs == rhs.resolvable(); }
+
+
+  /** \relates PoolItem */
+  inline bool operator!=( const PoolItem & lhs, const PoolItem & rhs )
+  { return ! (lhs==rhs); }
+
+  /** \relates PoolItem */
+  inline bool operator!=( const PoolItem & lhs, const ResObject::constPtr & rhs )
+  { return ! (lhs==rhs); }
+
+  /** \relates PoolItem */
+  inline bool operator!=( const ResObject::constPtr & lhs, const PoolItem & rhs )
+  { return ! (lhs==rhs); }
+
+
+  /** \relates PoolItem Test for same content. */
+  inline bool identical( const PoolItem & lhs, const PoolItem & rhs )
+  { return lhs == rhs || lhs.satSolvable().identical( rhs.satSolvable() ); }
+
+  /** \relates PoolItem Test for same content. */
+  inline bool identical( const PoolItem & lhs, sat::Solvable rhs )
+  { return lhs.satSolvable().identical( rhs ); }
+
+  /** \relates PoolItem Test for same content. */
+  inline bool identical( sat::Solvable lhs, const PoolItem & rhs )
+  { return lhs.identical( rhs.satSolvable() ); }
+
+
+  /** \relates PoolItem Test for same name version release and arch. */
+  inline bool sameNVRA( const PoolItem & lhs, const PoolItem & rhs )
+  { return lhs == rhs || lhs.satSolvable().sameNVRA( rhs.satSolvable() ); }
+
+  /** \relates PoolItem Test for same name version release and arch. */
+  inline bool sameNVRA( const PoolItem & lhs, sat::Solvable rhs )
+  { return lhs.satSolvable().sameNVRA( rhs ); }
+
+  /** \relates PoolItem Test for same name version release and arch. */
+  inline bool sameNVRA( sat::Solvable lhs, const PoolItem & rhs )
+  { return lhs.sameNVRA( rhs.satSolvable() ); }
+
+  /** Solvable to PoolItem transform functor.
+   * \relates PoolItem
+   * \relates sat::SolvIterMixin
+   */
+  struct asPoolItem
+  {
+    typedef PoolItem result_type;
+
+    PoolItem operator()( const sat::Solvable & solv_r ) const
+    { return PoolItem( solv_r ); }
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+
+  /** \relates zypp::PoolItem Order in std::container follows ResObject::constPtr.*/
+  template<>
+    inline bool less<zypp::PoolItem>::operator()( const zypp::PoolItem & lhs, const zypp::PoolItem & rhs ) const
+    { return lhs.resolvable() < rhs.resolvable(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOLITEM_H
diff --git a/zypp/PoolItemBest.cc b/zypp/PoolItemBest.cc
new file mode 100644 (file)
index 0000000..d6e4062
--- /dev/null
@@ -0,0 +1,80 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolItemBest.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/PoolItemBest.h"
+#include "zypp/ui/SelectableTraits.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItemBest::Impl
+  //
+  /** PoolItemBest implementation. */
+  struct PoolItemBest::Impl
+  {
+    Container _container;
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const
+      { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItemBest
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  void PoolItemBest::_ctor_init()
+  { _dont_use_this_use_pimpl.reset( new RWCOW_pointer<Impl>(new Impl) ); }
+
+  const PoolItemBest::Container & PoolItemBest::container() const
+  { return pimpl()->_container; }
+
+  void PoolItemBest::add( const PoolItem & pi_r )
+  {
+    Container & container( pimpl()->_container );
+    PoolItem & ccand( container[pi_r.satSolvable().ident()] );
+    if ( ! ccand || ui::SelectableTraits::AVOrder()( pi_r, ccand ) )
+      ccand = pi_r;
+  }
+
+  PoolItem PoolItemBest::find( IdString ident_r ) const
+  {
+    const Container & container( pimpl()->_container );
+    Container::const_iterator it( container.find( ident_r ) );
+    return it != container.end() ? it->second : PoolItem();
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const PoolItemBest & obj )
+  {
+    return dumpRange( str << "(" << obj.size() << ") ", obj.begin(), obj.end() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PoolItemBest.h b/zypp/PoolItemBest.h
new file mode 100644 (file)
index 0000000..2dbebcb
--- /dev/null
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolItemBest.h
+ *
+*/
+#ifndef ZYPP_POOLITEMBEST_H
+#define ZYPP_POOLITEMBEST_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolItemBest
+  //
+  /** Find the best candidates e.g. in a \ref PoolQuery result.
+   *
+   * The class basically maintains a \c map<IdString,PoolItem> and remembers
+   * for each \c ident (\ref sat::Solvable::ident) the best \ref PoolItem that
+   * was added.
+   *
+   * The default \ref Predicate to determine the best choice is the same that
+   * sorts the \ref ui::Selectable list of available objects, thus follows the
+   * same rules the \ref resolver will apply.
+   *
+   * \code
+   *   PoolQuery q;
+   *   q.addAttribute(sat::SolvAttr::name, "lib*");
+   *   q.setMatchGlob();
+   *
+   *  // get the best matches and tag them for installation:
+   *  PoolItemBest bestMatches( q.begin(), q.end() );
+   *  if ( ! bestMatches.empty() )
+   *  {
+   *    for_( it, bestMatches.begin(), bestMatches.end() )
+   *    {
+   *      ui::asSelectable()( *it )->setOnSystem( *it, ResStatus::USER );
+   *    }
+   *  }
+   * \endcode
+   *
+   * \todo Support arbitrary Predicates.
+   */
+  class PoolItemBest
+  {
+      typedef std::tr1::unordered_map<IdString,PoolItem> Container;
+    public:
+      /** Predicate returning \c True if \a lhs is a better choice. */
+      typedef boost::function<bool ( const PoolItem & lhs, const PoolItem & rhs )> Predicate;
+
+      typedef Container::size_type     size_type;
+      typedef Container::value_type    value_type;
+      typedef MapKVIteratorTraits<Container>::Value_const_iterator     iterator;
+      typedef MapKVIteratorTraits<Container>::Key_const_iterator       ident_iterator;
+
+    public:
+      /** Default ctor. */
+      PoolItemBest()
+      {}
+
+      /** Ctor feeding a \ref sat::Solvable. */
+      PoolItemBest( sat::Solvable slv_r )
+      { _ctor_init(); add( slv_r ); }
+
+      /** Ctor feeding a \ref PoolItem. */
+      PoolItemBest( const PoolItem & pi_r )
+      { _ctor_init(); add( pi_r ); }
+
+      /** Ctor feeding a range of  \ref sat::Solvable or \ref PoolItem. */
+      template<class _Iterator>
+      PoolItemBest( _Iterator begin_r, _Iterator end_r )
+      { _ctor_init(); add( begin_r, end_r ); }
+
+    public:
+      /** Feed one \ref sat::Solvable. */
+      void add( sat::Solvable slv_r )
+      { add( PoolItem( slv_r ) ); }
+
+      /** Feed one \ref PoolItem. */
+      void add( const PoolItem & pi_r );
+
+      /** Feed a range of  \ref sat::Solvable or \ref PoolItem. */
+      template<class _Iterator>
+      void add( _Iterator begin_r, _Iterator end_r )
+      {
+        for_( it, begin_r, end_r )
+          add( *it );
+      }
+
+    public:
+      /** \name Iterate the collected PoolItems. */
+      //@{
+      /** Whether PoolItems were collected. */
+      bool empty() const       { return container().empty(); }
+      /** Number of PoolItems collected. */
+      size_type size() const   { return container().size(); }
+      /** Pointer to the first PoolItem. */
+      iterator begin() const   { return make_map_value_begin( container() ); }
+      /** Pointer behind the last PoolItem. */
+      iterator end() const     { return make_map_value_end( container() ); }
+
+      /** Return the collected \ref PoolItem with \ref sat::Solvable::ident \a ident_r. */
+      PoolItem find( IdString ident_r ) const;
+      /** \overload Use Solvables ident string. */
+      PoolItem find( sat::Solvable slv_r ) const       { return find( slv_r.ident() ); }
+      /** \overload Use PoolItems ident string. */
+      PoolItem find( const PoolItem & pi_r ) const     { return find( pi_r.satSolvable().ident() ); }
+      //@}
+
+      /** \name Iterate the collected PoolItems ident strings. */
+      //@{
+      /** Pointer to the first item. */
+      ident_iterator identBegin() const        { return make_map_key_begin( container() ); }
+      /** Pointer behind the last item. */
+      ident_iterator identEnd() const  { return make_map_key_end( container() ); }
+      //@}
+
+    private:
+      void _ctor_init();
+      const Container & container() const;
+    private:
+      /** Implementation  */
+      class Impl;
+      /** Pointer to implementation */
+      RWCOW_pointer<Impl> & pimpl()             { return *(reinterpret_cast<RWCOW_pointer<Impl>*>( _dont_use_this_use_pimpl.get() )); }
+      /** Pointer to implementation */
+      const RWCOW_pointer<Impl> & pimpl() const { return *(reinterpret_cast<RWCOW_pointer<Impl>*>( _dont_use_this_use_pimpl.get() )); }
+      /** Avoid need to include Impl definition when inlined ctors (due to tepmlate) are provided. */
+      shared_ptr<void> _dont_use_this_use_pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolItemBest Stream output */
+  std::ostream & operator<<( std::ostream & str, const PoolItemBest & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOLITEMBEST_H
diff --git a/zypp/PoolQuery.cc b/zypp/PoolQuery.cc
new file mode 100644 (file)
index 0000000..a2dba81
--- /dev/null
@@ -0,0 +1,1732 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolQuery.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/base/String.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/RelCompare.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/AttrMatcher.h"
+
+#include "zypp/PoolQuery.h"
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
+
+using namespace std;
+using namespace zypp::sat;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    // some Helpers and Predicates
+    /////////////////////////////////////////////////////////////////
+
+    bool isDependencyAttribute( sat::SolvAttr attr_r )
+    {
+      static sat::SolvAttr deps[] = {
+        SolvAttr::provides,
+        SolvAttr::requires,
+        SolvAttr::recommends,
+        SolvAttr::obsoletes,
+        SolvAttr::conflicts,
+        SolvAttr::suggests,
+        SolvAttr::supplements,
+        SolvAttr::enhances,
+      };
+      for_( it, arrayBegin(deps), arrayEnd(deps) )
+        if ( *it == attr_r )
+          return true;
+      return false;
+    }
+
+    /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
+     * Query asserts \a iter_r points to a capability and we
+     * have to check the range only.
+     */
+    struct EditionRangePredicate
+    {
+      EditionRangePredicate( const Rel & op, const Edition & edition )
+        : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+      EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r )
+      {
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+
+        CapDetail cap( iter_r.id() );
+        if ( ! cap.isSimple() )
+          return false;
+        if ( cap.isNamed() ) // no range to match
+          return true;
+        return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "EditionRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
+      }
+
+      Edition::MatchRange _range;
+      Arch                _arch;
+   };
+
+    /** Whether the current Solvables edition is within a given range and/or its arch matches. */
+    struct SolvableRangePredicate
+    {
+      SolvableRangePredicate( const Rel & op, const Edition & edition )
+        : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+
+      SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r )
+      {
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+       return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "SolvableRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
+      }
+
+      Edition::MatchRange _range;
+      Arch                _arch;
+    };
+
+    /** Whether the current capability matches a given one.
+     * Query asserts \a iter_r points to a capability and we
+     * have to check the match only.
+     */
+    struct CapabilityMatchPredicate
+    {
+      CapabilityMatchPredicate( Capability cap_r )
+        : _cap( cap_r )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r ) const
+      {
+        return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "CapabilityMatch" );
+        str::appendEscaped( ret, _cap.asString() );
+        return ret;
+      }
+
+      Capability _cap;
+    };
+
+    /////////////////////////////////////////////////////////////////
+    //
+    /////////////////////////////////////////////////////////////////
+    /** Match data per attribtue.
+     *
+     * This includes the attribute itself, an optional \ref sat::AttrMatcher
+     * to restrict the query to certain string values, and an optional
+     * boolean \ref Predicate that may apply further restrictions that can
+     * not be expressed by the \ref attrMatcher.
+     *
+     * Example for such a \ref predicate would be an additional edition range
+     * check whan looking for dependencies. The \ref attrMatcher would
+     * find potential matches by looking at the dependencies name, the
+     * predicate will then check the edition ranges.
+     *
+     * As the \ref predicate takes an iterator pointing to the current
+     * match, it's also suitable for sub-structure (flexarray) inspection
+     * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
+     *
+     * \note: \see \ref addPredicate for further constraints.
+     */
+    struct AttrMatchData
+    {
+      typedef function<bool(sat::LookupAttr::iterator)> Predicate;
+
+      static bool always( sat::LookupAttr::iterator ) { return true; }
+      static bool never( sat::LookupAttr::iterator ) { return false; }
+
+      AttrMatchData()
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r )
+        : attr( attr_r )
+        , attrMatcher( attrMatcher_r )
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r,
+                     const Predicate & predicate_r, const std::string & predicateStr_r )
+        : attr( attr_r )
+        , attrMatcher( attrMatcher_r )
+        , predicate( predicate_r )
+        , predicateStr( predicateStr_r )
+      {}
+
+      /** A usable Predicate must provide a string serialization.
+       * As there is no \c operator== for \ref Predicate, we compare it's
+       * string representation instead. If you add new predicated, check the
+       * deserialization code in \ref deserialize.
+       */
+      template<class _Predicate>
+      void addPredicate( const _Predicate & predicate_r )
+      {
+        predicate    = predicate_r;
+        predicateStr = predicate_r.serialize();
+      }
+
+      /** Dumb serialization.
+       * \code
+       *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
+       * \endcode
+      */
+      std::string serialize() const
+      {
+        std::string ret( "AttrMatchData" );
+        str::appendEscaped( ret, attr.asString() );
+        str::appendEscaped( ret, attrMatcher.searchstring() );
+        // TODO: Actually the flag should be serialized too, but for PoolQuery
+        // it's by now sufficient to differ between mode OTHER and others,
+        // i.e. whether to compile or not compile.
+        str::appendEscaped( ret, attrMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
+        str::appendEscaped( ret, predicateStr );
+        return ret;
+      }
+
+       /** Dumb restore from serialized string.
+        * \throw Exception on parse error.
+        */
+      static AttrMatchData deserialize( const std::string & str_r )
+      {
+        std::vector<std::string> words;
+        str::splitEscaped( str_r, std::back_inserter(words) );
+        if ( words.empty() || words[0] != "AttrMatchData" )
+          ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
+        if ( words.size() != 5 )
+          ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+
+        AttrMatchData ret;
+        ret.attr = sat::SolvAttr( words[1] );
+        ret.attrMatcher = sat::AttrMatcher( words[2] );
+        if ( words[3] == "C" )
+          ret.attrMatcher.setFlags( Match::OTHER );
+        ret.predicateStr = words[4];
+
+        // now the predicate
+        words.clear();
+        str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
+        if ( ! words.empty() )
+        {
+          if ( words[0] == "EditionRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "SolvableRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "CapabilityMatch" )
+          {
+            if ( words.size() != 2 )
+              ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+            ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
+          }
+          else
+            ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
+        }
+        return ret;
+     }
+
+      sat::SolvAttr    attr;
+      sat::AttrMatcher attrMatcher;
+      Predicate        predicate;
+      std::string      predicateStr;
+    };
+
+    /** \relates AttrMatchData */
+    inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
+    {
+      str << obj.attr << ": " << obj.attrMatcher;
+      if ( obj.predicate )
+        str << " +(" << obj.predicateStr << ")";
+      return str;
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      return ( lhs.attr == rhs.attr
+               && lhs.attrMatcher == rhs.attrMatcher
+               && lhs.predicateStr == rhs.predicateStr );
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    { return !( lhs == rhs ); }
+
+    /** \relates AttrMatchData Arbitrary order for std::container. */
+    inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      if ( lhs.attr != rhs.attr )
+        return (  lhs.attr < rhs.attr );
+      if ( lhs.attrMatcher != rhs.attrMatcher )
+        return (  lhs.attrMatcher < rhs.attrMatcher );
+      if ( lhs.predicateStr != rhs.predicateStr )
+        return (  lhs.predicateStr < rhs.predicateStr );
+      return false;
+    }
+
+    typedef std::list<AttrMatchData> AttrMatchList;
+
+
+  } /////////////////////////////////////////////////////////////////
+  // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PoolQuery::Impl
+  //
+  /** */
+  class PoolQuery::Impl
+  {
+  public:
+    Impl()
+      : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
+      , _match_word(false)
+      , _require_all(false)
+      , _status_flags(ALL)
+    {}
+
+    ~Impl()
+    {}
+
+  public:
+    /** String representation */
+    string asString() const;
+
+    /** \name Raw query options. */
+    //@{
+    /** Raw search strings. */
+    StrContainer _strings;
+    /** Raw attributes */
+    AttrRawStrMap _attrs;
+    /** Uncompiled attributes with predicate. */
+    std::set<AttrMatchData> _uncompiledPredicated;
+
+    /** Sat solver search flags */
+    Match _flags;
+    bool _match_word;
+    bool _require_all;
+
+    /** Sat solver status flags */
+    StatusFilter _status_flags;
+
+    /** Edition condition operand */
+    Edition _edition;
+    /** Operator for edition condition */
+    Rel _op;
+
+    /** Repos to search. */
+    StrContainer _repos;
+
+    /** Kinds to search */
+    Kinds _kinds;
+    //@}
+
+  public:
+
+    bool operator==( const PoolQuery::Impl & rhs ) const
+    {
+      return ( _strings == rhs._strings
+               && _attrs == rhs._attrs
+               && _uncompiledPredicated == rhs._uncompiledPredicated
+               && _flags == rhs._flags
+               && _match_word == rhs._match_word
+               && _require_all == rhs._require_all
+               && _status_flags == rhs._status_flags
+               && _edition == rhs._edition
+               && _op == rhs._op
+               && _repos == rhs._repos
+               && _kinds == rhs._kinds );
+    }
+
+    bool operator!=( const PoolQuery::Impl & rhs ) const
+    { return ! operator==( rhs ); }
+
+  public:
+    /** Compile the regex.
+     * Basically building the \ref _attrMatchList from strings.
+     * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+     */
+    void compile() const;
+
+    /** AttrMatcher per attribtue. */
+    mutable AttrMatchList _attrMatchList;
+
+  private:
+    /** Pass flags from \ref compile, as they may have been changed. */
+    string createRegex( const StrContainer & container, const Match & flags ) const;
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  struct MyInserter
+  {
+    MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
+
+    bool operator()(const string & str)
+    {
+      _cont.insert(str);
+      return true;
+    }
+
+    PoolQuery::StrContainer & _cont;
+  };
+
+
+  struct EmptyFilter
+  {
+    bool operator()(const string & str)
+    {
+      return !str.empty();
+    }
+  };
+
+  void PoolQuery::Impl::compile() const
+  {
+    _attrMatchList.clear();
+
+    Match cflags( _flags );
+    if ( cflags.mode() == Match::OTHER ) // this will never succeed...
+      ZYPP_THROW( MatchUnknownModeException( cflags ) );
+
+    /** Compiled search strings. */
+    string rcstrings;
+
+
+    // 'different'         - will have to iterate through all and match by ourselves (slow)
+    // 'same'              - will pass the compiled string to dataiterator_init
+    // 'one-attr'          - will pass it to dataiterator_init
+    // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
+
+    // // NO ATTRIBUTE
+    // else
+    //   for all _strings
+    //     create regex; store in rcstrings; if more strings flag regex;
+    if (_attrs.empty())
+    {
+      ; // A default 'query-all' will be added after all sources are processed.
+    }
+
+    // // ONE ATTRIBUTE
+    // else if _attrs is not empty but it contains just one attr
+    //   for all _strings and _attr[key] strings
+    //     create regex; flag 'one-attr'; if more strings flag regex;
+    else if (_attrs.size() == 1)
+    {
+      StrContainer joined;
+      invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+      invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+      rcstrings = createRegex(joined, cflags);
+      if (joined.size() > 1) // switch to regex for multiple strings
+        cflags.setModeRegex();
+      _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
+                                sat::AttrMatcher( rcstrings, cflags ) ) );
+    }
+
+    // // MULTIPLE ATTRIBUTES
+    else
+    {
+      // check whether there are any per-attribute strings
+      bool attrvals_empty = true;
+      for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+        if (!ai->second.empty())
+          for(StrContainer::const_iterator it = ai->second.begin();
+              it != ai->second.end(); it++)
+            if (!it->empty())
+            {
+              attrvals_empty = false;
+              goto attremptycheckend;
+            }
+attremptycheckend:
+
+      // chceck whether the per-attribute strings are all the same
+      bool attrvals_thesame = true;
+      AttrRawStrMap::const_iterator ai = _attrs.begin();
+      const StrContainer & set1 = ai->second;
+      ++ai;
+      for (; ai != _attrs.end(); ++ai)
+      {
+        StrContainer result;
+        set_difference(
+          set1.begin(), set1.end(),
+          ai->second.begin(), ai->second.end(),
+          inserter(result, result.begin())/*, ltstr()*/);
+        if (!result.empty())
+        {
+          attrvals_thesame = false;
+          break;
+        }
+      }
+
+      // // THE SAME STRINGS FOR DIFFERENT ATTRS
+      // else if _attrs is not empty but it does not contain strings
+      //   for each key in _attrs take all _strings
+      //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
+      if (attrvals_empty || attrvals_thesame)
+      {
+        StrContainer joined;
+        if (attrvals_empty)
+        {
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          rcstrings = createRegex(joined, cflags);
+        }
+        else
+        {
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+          rcstrings = createRegex(joined, cflags);
+        }
+        if (joined.size() > 1) // switch to regex for multiple strings
+          cflags.setModeRegex();
+        // May use the same AttrMatcher for all
+        sat::AttrMatcher matcher( rcstrings, cflags );
+        for_( ai, _attrs.begin(), _attrs.end() )
+        {
+          _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
+        }
+      }
+
+      // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
+      // if _attrs is not empty and it contains non-empty vectors with non-empty strings
+      //   for each key in _attrs take all _strings + all _attrs[key] strings
+      //     create regex; flag 'different'; if more strings flag regex;
+      else
+      {
+        for_(ai, _attrs.begin(), _attrs.end())
+        {
+          StrContainer joined;
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
+          string s = createRegex(joined, cflags);
+          if (joined.size() > 1) // switch to regex for multiple strings
+            cflags.setModeRegex();
+          _attrMatchList.push_back( AttrMatchData( ai->first,
+                                    sat::AttrMatcher( s, cflags ) ) );
+        }
+      }
+    }
+
+    // Now handle any predicated queries
+    if ( ! _uncompiledPredicated.empty() )
+    {
+      StrContainer global;
+      invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
+      for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
+      {
+        if ( it->attrMatcher.flags().mode() == Match::OTHER )
+        {
+          // need to compile:
+          StrContainer joined( global );
+          const std::string & mstr( it->attrMatcher.searchstring() );
+          if ( ! mstr.empty() )
+            joined.insert( mstr );
+
+          cflags = _flags;
+          rcstrings = createRegex( joined, cflags );
+          if ( joined.size() > 1 ) // switch to regex for multiple strings
+            cflags.setModeRegex();
+
+          _attrMatchList.push_back( AttrMatchData( it->attr,
+                                    sat::AttrMatcher( rcstrings, cflags ),
+                                                      it->predicate, it->predicateStr ) );
+        }
+        else
+        {
+          // copy matcher
+         _attrMatchList.push_back( *it );
+        }
+      }
+    }
+
+    // If no attributes defined at all, then add 'query all'
+    if ( _attrMatchList.empty() )
+    {
+      cflags = _flags;
+      rcstrings = createRegex( _strings, cflags );
+      if ( _strings.size() > 1 ) // switch to regex for multiple strings
+        cflags.setModeRegex();
+      _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
+                                sat::AttrMatcher( rcstrings, cflags ) ) );
+    }
+
+    // Finally check here, whether all involved regex compile.
+    for_( it, _attrMatchList.begin(), _attrMatchList.end() )
+    {
+      it->attrMatcher.compile(); // throws on error
+    }
+    //DBG << asString() << endl;
+  }
+
+
+  /**
+   * Converts '*' and '?' wildcards within str into their regex equivalents.
+   */
+  static string wildcards2regex(const string & str)
+  {
+    string regexed = str;
+
+    string r_all(".*"); // regex equivalent of '*'
+    string r_one(".");  // regex equivalent of '?'
+    string::size_type pos;
+
+    // replace all "*" in input with ".*"
+    for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
+      regexed = regexed.replace(pos, 1, r_all);
+
+    // replace all "?" in input with "."
+    for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
+      regexed = regexed.replace(pos, 1, r_one);
+
+    return regexed;
+  }
+
+  string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
+  {
+//! macro for word boundary tags for regexes
+#define WB (_match_word ? string("\\b") : string())
+    string rstr;
+
+    if (container.empty())
+      return rstr;
+
+    if (container.size() == 1)
+    {
+      return WB + *container.begin() + WB;
+    }
+
+    // multiple strings
+
+    bool use_wildcards = flags.isModeGlob();
+    StrContainer::const_iterator it = container.begin();
+    string tmp;
+
+    if (use_wildcards)
+      tmp = wildcards2regex(*it);
+    else
+      tmp = *it;
+
+    if (_require_all)
+    {
+      if ( ! flags.isModeString() ) // not match exact
+        tmp += ".*" + WB + tmp;
+      rstr = "(?=" + tmp + ")";
+    }
+    else
+    {
+      if ( flags.isModeString() || flags.isModeGlob() )
+        rstr = "^";
+      rstr += WB + "(" + tmp;
+    }
+
+    ++it;
+
+    for (; it != container.end(); ++it)
+    {
+      if (use_wildcards)
+        tmp = wildcards2regex(*it);
+      else
+        tmp = *it;
+
+      if (_require_all)
+      {
+        if ( ! flags.isModeString() ) // not match exact
+          tmp += ".*" + WB + tmp;
+        rstr += "(?=" + tmp + ")";
+      }
+      else
+      {
+        rstr += "|" + tmp;
+      }
+    }
+
+    if (_require_all)
+    {
+      if ( ! flags.isModeString() ) // not match exact
+        rstr += WB + ".*";
+    }
+    else
+    {
+      rstr += ")" + WB;
+      if ( flags.isModeString() || flags.isModeGlob() )
+        rstr += "$";
+    }
+
+    return rstr;
+#undef WB
+  }
+
+  string PoolQuery::Impl::asString() const
+  {
+    ostringstream o;
+
+    o << "kinds: ";
+    if ( _kinds.empty() )
+      o << "ALL";
+    else
+    {
+      for(Kinds::const_iterator it = _kinds.begin();
+          it != _kinds.end(); ++it)
+        o << *it << " ";
+    }
+    o << endl;
+
+    o << "repos: ";
+    if ( _repos.empty() )
+      o << "ALL";
+    else
+    {
+      for(StrContainer::const_iterator it = _repos.begin();
+          it != _repos.end(); ++it)
+        o << *it << " ";
+    }
+    o << endl;
+
+    o << "version: "<< _op << " " << _edition.asString() << endl;
+    o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
+                                       : "ALL" ) << endl;
+
+    o << "string match flags: " << Match(_flags) << endl;
+
+    // raw
+    o << "strings: ";
+    for(StrContainer::const_iterator it = _strings.begin();
+        it != _strings.end(); ++it)
+      o << *it << " ";
+    o << endl;
+
+    o << "attributes: " << endl;
+    for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+    {
+      o << "* " << ai->first << ": ";
+      for(StrContainer::const_iterator vi = ai->second.begin();
+          vi != ai->second.end(); ++vi)
+        o << *vi << " ";
+      o << endl;
+    }
+
+    o << "predicated: " << endl;
+    for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
+    {
+      o << "* " << *it << endl;
+    }
+
+    // compiled
+    o << "last attribute matcher compiled: " << endl;
+    if ( _attrMatchList.empty() )
+    {
+      o << "not yet compiled" << endl;
+    }
+    else
+    {
+      for_( it, _attrMatchList.begin(), _attrMatchList.end() )
+      {
+        o << "* " << *it << endl;
+      }
+    }
+    return o.str();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolQuery
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  PoolQuery::PoolQuery()
+    : _pimpl(new Impl())
+  {}
+
+  PoolQuery::~PoolQuery()
+  {}
+
+  void PoolQuery::addRepo(const std::string &repoalias)
+  {
+    if (repoalias.empty())
+    {
+      WAR << "ignoring an empty repository alias" << endl;
+      return;
+    }
+    _pimpl->_repos.insert(repoalias);
+  }
+
+  void PoolQuery::addKind(const ResKind & kind)
+  { _pimpl->_kinds.insert(kind); }
+
+  void PoolQuery::addString(const string & value)
+  { _pimpl->_strings.insert(value); }
+
+  void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
+  { _pimpl->_attrs[attr].insert(value); }
+
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
+  { return addDependency( attr, name, op, edition, Arch_empty ); }
+
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
+  {
+    switch ( op.inSwitch() )
+    {
+      case Rel::ANY_e: // no additional constraint on edition.
+        if ( arch.empty() )    // no additional constraint on arch.
+       {
+         addAttribute( attr, name );
+         return;
+       }
+       break;
+
+      case Rel::NONE_e:        // will never match.
+        return;
+
+      default: // go and add the predicated query (uncompiled)
+        break;
+    }
+
+    // Match::OTHER indicates need to compile
+    // (merge global search strings into name).
+    AttrMatchData attrMatchData( attr, sat::AttrMatcher( name, Match::OTHER ) );
+
+    if ( isDependencyAttribute( attr ) )
+      attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
+    else
+      attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
+
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
+  }
+
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
+  {
+    CapDetail cap( cap_r );
+    if ( ! cap.isSimple() ) // will never match.
+      return;
+
+    // Matches STRING per default. (won't get compiled!)
+    AttrMatchData attrMatchData( attr, sat::AttrMatcher( cap.name().asString() ) );
+
+    if ( isDependencyAttribute( attr ) )
+      attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
+    else
+      attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
+
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
+  }
+
+  void PoolQuery::setEdition(const Edition & edition, const Rel & op)
+  {
+    _pimpl->_edition = edition;
+    _pimpl->_op = op;
+  }
+
+  void PoolQuery::setMatchSubstring()  { _pimpl->_flags.setModeSubstring(); }
+  void PoolQuery::setMatchExact()      { _pimpl->_flags.setModeString(); }
+  void PoolQuery::setMatchRegex()      { _pimpl->_flags.setModeRegex(); }
+  void PoolQuery::setMatchGlob()       { _pimpl->_flags.setModeGlob(); }
+  void PoolQuery::setMatchWord()
+  {
+    _pimpl->_match_word = true;
+    _pimpl->_flags.setModeRegex();
+  }
+
+  Match PoolQuery::flags() const
+  { return _pimpl->_flags; }
+  void PoolQuery::setFlags( const Match & flags )
+  { _pimpl->_flags = flags; }
+
+
+  void PoolQuery::setInstalledOnly()
+  { _pimpl->_status_flags = INSTALLED_ONLY; }
+  void PoolQuery::setUninstalledOnly()
+  { _pimpl->_status_flags = UNINSTALLED_ONLY; }
+  void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
+  { _pimpl->_status_flags = flags; }
+
+
+  void PoolQuery::setRequireAll(bool require_all)
+  { _pimpl->_require_all = require_all; }
+
+
+  const PoolQuery::StrContainer &
+  PoolQuery::strings() const
+  { return _pimpl->_strings; }
+
+  const PoolQuery::AttrRawStrMap &
+  PoolQuery::attributes() const
+  { return _pimpl->_attrs; }
+
+  const PoolQuery::StrContainer &
+  PoolQuery::attribute(const sat::SolvAttr & attr) const
+  {
+    static const PoolQuery::StrContainer nocontainer;
+    AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
+    return it != _pimpl->_attrs.end() ? it->second : nocontainer;
+  }
+
+  const Edition PoolQuery::edition() const
+  { return _pimpl->_edition; }
+  const Rel PoolQuery::editionRel() const
+  { return _pimpl->_op; }
+
+
+  const PoolQuery::Kinds &
+  PoolQuery::kinds() const
+  { return _pimpl->_kinds; }
+
+  const PoolQuery::StrContainer &
+  PoolQuery::repos() const
+  { return _pimpl->_repos; }
+
+
+  bool PoolQuery::caseSensitive() const
+  { return !_pimpl->_flags.test( Match::NOCASE ); }
+  void PoolQuery::setCaseSensitive( bool value )
+  { _pimpl->_flags.turn( Match::NOCASE, !value ); }
+
+  bool PoolQuery::filesMatchFullPath() const
+  { return _pimpl->_flags.test( Match::FILES ); }
+  void PoolQuery::setFilesMatchFullPath( bool value )
+  { _pimpl->_flags.turn( Match::FILES, value ); }
+
+  bool PoolQuery::matchExact() const           { return _pimpl->_flags.isModeString(); }
+  bool PoolQuery::matchSubstring() const       { return _pimpl->_flags.isModeSubstring(); }
+  bool PoolQuery::matchGlob() const            { return _pimpl->_flags.isModeGlob(); }
+  bool PoolQuery::matchRegex() const           { return _pimpl->_flags.isModeRegex(); }
+
+  bool PoolQuery::matchWord() const
+  { return _pimpl->_match_word; }
+
+  bool PoolQuery::requireAll() const
+  { return _pimpl->_require_all; }
+
+  PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
+  { return _pimpl->_status_flags; }
+
+  bool PoolQuery::empty() const
+  {
+    try { return begin() == end(); }
+    catch (const Exception & ex) {}
+    return true;
+  }
+
+  PoolQuery::size_type PoolQuery::size() const
+  {
+    try
+    {
+      size_type count = 0;
+      for_( it, begin(), end() )
+        ++count;
+      return count;
+    }
+    catch (const Exception & ex) {}
+    return 0;
+  }
+
+  void PoolQuery::execute(ProcessResolvable fnc)
+  { invokeOnEach( begin(), end(), fnc); }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PoolQuery::Attr
+  //
+  /**
+   * represents all atributes in PoolQuery except SolvAtributes, which are
+   * used as is (not needed extend anything if someone adds new solv attr)
+   */
+  struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
+  {
+  private:
+    friend class IdStringType<PoolQueryAttr>;
+    IdString _str;
+  public:
+
+    //noAttr
+    PoolQueryAttr(){}
+
+    explicit PoolQueryAttr( const char* cstr_r )
+        : _str( cstr_r )
+      {}
+
+    explicit PoolQueryAttr( const std::string & str_r )
+        : _str( str_r )
+      {}
+
+    // unknown atributes
+    static const PoolQueryAttr noAttr;
+
+    // PoolQuery's own attributes
+    static const PoolQueryAttr repoAttr;
+    static const PoolQueryAttr kindAttr;
+    static const PoolQueryAttr stringAttr;
+    static const PoolQueryAttr stringTypeAttr;
+    static const PoolQueryAttr requireAllAttr;
+    static const PoolQueryAttr caseSensitiveAttr;
+    static const PoolQueryAttr installStatusAttr;
+    static const PoolQueryAttr editionAttr;
+    static const PoolQueryAttr complexAttr;
+  };
+
+  const PoolQueryAttr PoolQueryAttr::noAttr;
+
+  const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
+  const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
+  const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
+  const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
+  const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
+  const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
+  const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
+  const PoolQueryAttr PoolQueryAttr::editionAttr("version");
+  const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
+
+  class StringTypeAttr : public IdStringType<PoolQueryAttr>
+  {
+    friend class IdStringType<StringTypeAttr>;
+    IdString _str;
+
+  public:
+    StringTypeAttr(){}
+    explicit StringTypeAttr( const char* cstr_r )
+            : _str( cstr_r ){}
+    explicit StringTypeAttr( const std::string & str_r )
+             : _str( str_r ){}
+
+    static const StringTypeAttr noAttr;
+
+    static const StringTypeAttr exactAttr;
+    static const StringTypeAttr substringAttr;
+    static const StringTypeAttr regexAttr;
+    static const StringTypeAttr globAttr;
+    static const StringTypeAttr wordAttr;
+  };
+
+  const StringTypeAttr StringTypeAttr::noAttr;
+
+  const StringTypeAttr StringTypeAttr::exactAttr("exact");
+  const StringTypeAttr StringTypeAttr::substringAttr("substring");
+  const StringTypeAttr StringTypeAttr::regexAttr("regex");
+  const StringTypeAttr StringTypeAttr::globAttr("glob");
+  const StringTypeAttr StringTypeAttr::wordAttr("word");
+
+  ///////////////////////////////////////////////////////////////////
+
+
+  //\TODO maybe ctor with stream can be usefull
+  //\TODO let it throw, let it throw, let it throw.
+  bool PoolQuery::recover( istream &str, char delim )
+  {
+    bool finded_something = false; //indicates some atributes is finded
+    string s;
+    do {
+      if ( str.eof() )
+        break;
+
+      getline( str, s, delim );
+
+      if ((!s.empty()) && s[0]=='#') //comment
+      {
+        continue;
+      }
+
+      string::size_type pos = s.find(':');
+      if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
+      {
+        if (finded_something) //is first blank line after record?
+        {
+          break;
+        }
+        else
+        {
+          continue;
+        }
+      }
+
+      finded_something = true;
+
+      string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
+      string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
+
+      PoolQueryAttr attribute( attrName );
+
+      MIL << "attribute name: " << attrName << endl;
+
+      if ( attribute==PoolQueryAttr::repoAttr )
+      {
+        addRepo( attrValue );
+      }
+      /* some backwards compatibility */
+      else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
+      {
+        addKind( ResKind(attrValue) );
+      }
+      else if ( attribute==PoolQueryAttr::stringAttr
+        || attribute=="global_string")
+      {
+        addString( attrValue );
+      }
+      else if ( attribute==PoolQueryAttr::stringTypeAttr
+        || attribute=="string_type" )
+      {
+        StringTypeAttr s(attrValue);
+        if( s == StringTypeAttr::regexAttr )
+        {
+          setMatchRegex();
+        }
+        else if ( s == StringTypeAttr::globAttr )
+        {
+          setMatchGlob();
+        }
+        else if ( s == StringTypeAttr::exactAttr )
+        {
+          setMatchExact();
+        }
+        else if ( s == StringTypeAttr::substringAttr )
+        {
+          setMatchSubstring();
+        }
+        else if ( s == StringTypeAttr::wordAttr )
+        {
+          setMatchWord();
+        }
+        else if ( s == StringTypeAttr::noAttr )
+        {
+          WAR << "unknown string type " << attrValue << endl;
+        }
+        else
+        {
+          WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::requireAllAttr )
+      {
+        if ( str::strToTrue(attrValue) )
+        {
+          setRequireAll(true);
+        }
+        else if ( !str::strToFalse(attrValue) )
+        {
+          setRequireAll(false);
+        }
+        else
+        {
+          WAR << "unknown boolean value " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
+      {
+        if ( str::strToTrue(attrValue) )
+        {
+          setCaseSensitive(true);
+        }
+        else if ( !str::strToFalse(attrValue) )
+        {
+          setCaseSensitive(false);
+        }
+        else
+        {
+          WAR << "unknown boolean value " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::installStatusAttr )
+      {
+        if( attrValue == "all" )
+        {
+          setStatusFilterFlags( ALL );
+        }
+        else if( attrValue == "installed" )
+        {
+          setInstalledOnly();
+        }
+        else if( attrValue == "not-installed" )
+        {
+          setUninstalledOnly();
+        }
+        else
+        {
+          WAR << "Unknown value for install status " << attrValue << endl;
+        }
+      }
+      else if ( attribute == PoolQueryAttr::editionAttr)
+      {
+        string::size_type pos;
+        Rel rel("==");
+        if (attrValue.find_first_of("=<>!") == 0)
+        {
+          pos = attrValue.find_last_of("=<>");
+          rel = Rel(attrValue.substr(0, pos+1));
+          attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
+        }
+
+        setEdition(Edition(attrValue), rel);
+      }
+      else if ( attribute == PoolQueryAttr::complexAttr )
+      {
+        try
+        {
+          _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
+        }
+        catch ( const Exception & err )
+        {
+          WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
+
+        }
+      }
+      else if ( attribute==PoolQueryAttr::noAttr )
+      {
+        WAR << "empty attribute name" << endl;
+      }
+      else
+      {
+        string s = attrName;
+        str::replaceAll( s,"_",":" );
+        SolvAttr a(s);
+       if ( a == SolvAttr::name || isDependencyAttribute( a ) )
+       {
+         Capability c( attrValue );
+         CapDetail d( c );
+         if ( d.isVersioned() )
+           addDependency( a, d.name().asString(), d.op(), d.ed() );
+         else
+           addDependency( a, attrValue );
+       }
+       else
+         addAttribute( a, attrValue );
+      }
+
+    } while ( true );
+
+    return finded_something;
+  }
+
+  void PoolQuery::serialize( ostream &str, char delim ) const
+  {
+    //separating delim
+    str << delim;
+    //iterate thrue all settings and write it
+    static const zypp::PoolQuery q; //not save default options, so create default query example
+
+    for_( it, repos().begin(), repos().end() )
+    {
+      str << "repo: " << *it << delim ;
+    }
+
+    for_( it, kinds().begin(), kinds().end() )
+    {
+      str << PoolQueryAttr::kindAttr.asString() << ": "
+          << it->idStr() << delim ;
+    }
+
+    if (editionRel() != Rel::ANY && edition() != Edition::noedition)
+      str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
+
+    if (matchMode()!=q.matchMode())
+    {
+      switch( matchMode() )
+      {
+      case Match::STRING:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
+        break;
+      case Match::SUBSTRING:
+        str << PoolQueryAttr::stringTypeAttr.asString()
+            << ": substring" << delim;
+        break;
+      case Match::GLOB:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
+        break;
+      case Match::REGEX:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
+        break;
+      default:
+        WAR << "unknown match type "  << matchMode() << endl;
+      }
+    }
+
+    if( caseSensitive() != q.caseSensitive() )
+    {
+      str << "case_sensitive: ";
+      if (caseSensitive())
+      {
+        str << "on" << delim;
+      }
+      else
+      {
+        str << "off" << delim;
+      }
+    }
+
+    if( requireAll() != q.requireAll() )
+    {
+      str << "require_all: ";
+      if (requireAll())
+      {
+        str << "on" << delim;
+      }
+      else
+      {
+        str << "off" << delim;
+      }
+    }
+
+    if( statusFilterFlags() != q.statusFilterFlags() )
+    {
+      switch( statusFilterFlags() )
+      {
+      case ALL:
+        str << "install_status: all" << delim;
+        break;
+      case INSTALLED_ONLY:
+        str << "install_status: installed" << delim;
+        break;
+      case UNINSTALLED_ONLY:
+        str << "install_status: not-installed" << delim;
+        break;
+      }
+    }
+
+    for_( it, strings().begin(), strings().end() )
+    {
+      str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
+    }
+
+    for_( it, attributes().begin(), attributes().end() )
+    {
+      string s = it->first.asString();
+      str::replaceAll(s,":","_");
+      for_( it2,it->second.begin(),it->second.end() )
+      {
+        str << s <<": "<< *it2 << delim;
+      }
+    }
+
+    for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
+    {
+      str << "complex: "<< it->serialize() << delim;
+    }
+
+    //separating delim - protection
+    str << delim;
+  }
+
+  string PoolQuery::asString() const
+  { return _pimpl->asString(); }
+
+  ostream & operator<<( ostream & str, const PoolQuery & obj )
+  { return str << obj.asString(); }
+
+  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
+  { return dumpRange( str << obj, obj.begin(), obj.end() ); }
+
+  bool PoolQuery::operator==( const PoolQuery & rhs ) const
+  { return *_pimpl == *rhs._pimpl; }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace detail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  CLASS NAME : PoolQueryMatcher
+    //
+    /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
+     *
+     * Basically the matcher performs a base query, which should preselect
+     * candidates for a match. And has some filter conditions on top of it.
+     * Query and fileter depend on the \ref PoolQuery settings.
+     *
+     * Matcher must be stateless, as it is shared between multiple
+     * \ref PoolQueryIterator instances.
+     *
+     * If \ref base_iterator is at the \ref end, \ref advance moves it
+     * to the first match. Otherwise advance moves to the next match, or
+     * to the \ref end, if there is no more match.
+     *
+     * \note The original implementation treated an empty search string as
+     * <it>"match always"</it>. We stay compatible.
+     */
+    class PoolQueryMatcher
+    {
+      public:
+       typedef sat::LookupAttr::iterator base_iterator;
+
+      public:
+       const base_iterator & end() const
+       {
+         static base_iterator _end;
+         return _end;
+       }
+
+       bool advance( base_iterator & base_r ) const
+       {
+         if ( base_r == end() )
+           base_r = startNewQyery(); // first candidate
+         else
+          {
+            base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
+           ++base_r; // advance to next candidate
+          }
+
+         while ( base_r != end() )
+         {
+           if ( isAMatch( base_r ) )
+             return true;
+           // No match: try next
+            ++base_r;
+         }
+         return false;
+       }
+
+        /** Provide all matching attributes within this solvable.
+         *
+         */
+        void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
+        {
+          if ( base_r == end() )
+            return;
+
+          sat::Solvable inSolvable( base_r.inSolvable() );
+
+          if ( _attrMatchList.size() == 1 )
+          {
+            // base_r is already on the 1st matching attribute!
+            // String matching is done by the base iterator. We must check the predicate here.
+            // Let's see if there are more matches for this solvable:
+            base_iterator base( base_r );
+            base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
+            return_r.push_back( base );
+
+            const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+            for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
+            {
+              if ( ! predicate || predicate( base ) )
+                return_r.push_back( base );
+            }
+          }
+          else
+          {
+            // Here: search all attributes ;(
+            for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+            {
+              const AttrMatchData & matchData( *mi );
+              sat::LookupAttr q( matchData.attr, inSolvable );
+              if ( matchData.attrMatcher ) // an empty searchstring matches always
+                q.setAttrMatcher( matchData.attrMatcher );
+
+              if ( ! q.empty() ) // there are matches.
+              {
+                // now check any predicate:
+                const AttrMatchData::Predicate & predicate( matchData.predicate );
+                for_( it, q.begin(), q.end() )
+                {
+                  if ( ! predicate || predicate( it ) )
+                    return_r.push_back( it );
+                }
+              }
+            }
+          }
+        }
+
+      public:
+       /** Ctor stores the \ref PoolQuery settings.
+         * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
+         */
+       PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
+       {
+         query_r->compile();
+
+         // Repo restriction:
+         sat::Pool satpool( sat::Pool::instance() );
+
+         for_( it, query_r->_repos.begin(), query_r->_repos.end() )
+         {
+           Repository r( satpool.reposFind( *it ) );
+           if ( r )
+             _repos.insert( r );
+           else
+             _neverMatchRepo = true;
+         }
+         // _neverMatchRepo: we just need to catch the case that no repo
+         // matched, so we'd interpret the empty list as 'take from all'
+         if ( _neverMatchRepo && ! _repos.empty() )
+           _neverMatchRepo = false;
+
+         // Kind restriction:
+         _kinds = query_r->_kinds;
+         // Edition restriction:
+         _op      = query_r->_op;
+         _edition = query_r->_edition;
+         // Status restriction:
+         _status_flags = query_r->_status_flags;
+          // AttrMatcher
+          _attrMatchList = query_r->_attrMatchList;
+       }
+
+       ~PoolQueryMatcher()
+       {}
+
+      private:
+       /** Initialize a new base query. */
+       base_iterator startNewQyery() const
+       {
+         sat::LookupAttr q;
+
+         if ( _neverMatchRepo )
+           return q.end();
+
+         // Repo restriction:
+         if ( _repos.size() == 1 )
+           q.setRepo( *_repos.begin() );
+         // else: handled in isAMatch.
+
+         // Attribute restriction:
+         if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
+         {
+            const AttrMatchData & matchData( _attrMatchList.front() );
+           q.setAttr( matchData.attr );
+            if ( matchData.attrMatcher ) // empty searchstring matches always
+              q.setAttrMatcher( matchData.attrMatcher );
+         }
+          else // more than 1 attr (but not all)
+          {
+            // no restriction, it's all handled in isAMatch.
+            q.setAttr( sat::SolvAttr::allAttr );
+          }
+
+         return q.begin();
+       }
+
+
+       /** Check whether we are on a match.
+        *
+        * The check covers the whole Solvable, not just the current
+        * attribute \c base_r points to. If there's no match, also
+        * prepare \c base_r to advance appropriately. If there is
+        * a match, simply return \c true. \ref advance always moves
+        * to the next Solvable if there was a match.
+        *
+        * \note: Caller asserts we're not at \ref end.
+       */
+       bool isAMatch( base_iterator & base_r ) const
+       {
+         /////////////////////////////////////////////////////////////////////
+         Repository inRepo( base_r.inRepo() );
+         // Status restriction:
+         if ( _status_flags
+            && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
+         {
+           base_r.nextSkipRepo();
+           return false;
+         }
+         // Repo restriction:
+         if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
+         {
+           base_r.nextSkipRepo();
+           return false;
+         }
+         /////////////////////////////////////////////////////////////////////
+         sat::Solvable inSolvable( base_r.inSolvable() );
+         // Kind restriction:
+         if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
+         {
+            base_r.nextSkipSolvable();
+            return false;
+         }
+
+         // Edition restriction:
+         if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
+         {
+           base_r.nextSkipSolvable();
+           return false;
+         }
+         /////////////////////////////////////////////////////////////////////
+         // string and predicate matching:
+
+          if ( _attrMatchList.size() == 1 )
+          {
+            // String matching was done by the base iterator.
+            // Now check any predicate:
+            const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+            if ( ! predicate || predicate( base_r ) )
+              return true;
+
+            return false; // no skip as there may be more occurrences od this attr.
+          }
+
+          // Here: search all attributes ;(
+          for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+          {
+            const AttrMatchData & matchData( *mi );
+            sat::LookupAttr q( matchData.attr, inSolvable );
+            if ( matchData.attrMatcher ) // an empty searchstring matches always
+              q.setAttrMatcher( matchData.attrMatcher );
+
+            if ( ! q.empty() ) // there are matches.
+            {
+              // now check any predicate:
+              const AttrMatchData::Predicate & predicate( matchData.predicate );
+              if ( predicate )
+              {
+                for_( it, q.begin(), q.end() )
+                {
+                  if ( predicate( it ) )
+                    return true;
+                }
+              }
+              else
+                return true;
+            }
+          }
+          base_r.nextSkipSolvable();
+          return false;
+       }
+
+      private:
+        /** Repositories include in the search. */
+        std::set<Repository> _repos;
+       DefaultIntegral<bool,false> _neverMatchRepo;
+        /** Resolvable kinds to include. */
+        std::set<ResKind> _kinds;
+        /** Edition filter. */
+        Rel _op;
+        Edition _edition;
+        /** Installed status filter flags. \see PoolQuery::StatusFilter */
+        int _status_flags;
+        /** AttrMatcher per attribtue. */
+        AttrMatchList _attrMatchList;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    void PoolQueryIterator::increment()
+    {
+      // matcher restarts if at end! It is called from the ctor
+      // to get the 1st match. But if the end is reached, it should
+      // be deleted, otherwise we'd start over again.
+      if ( !_matcher )
+        return; // at end
+      if ( _matches )
+        _matches.reset(); // invalidate old matches
+      if ( ! _matcher->advance( base_reference() ) )
+        _matcher.reset();
+    }
+
+    const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
+    {
+      if ( _matches )
+        return *_matches;
+
+      if ( !_matcher )
+      {
+        // at end of query:
+        static const Matches _none;
+        return _none;
+      }
+
+      _matches.reset( new Matches );
+      _matcher->matchDetail( base_reference(), *_matches );
+      return *_matches;
+    }
+
+    std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
+    {
+      str << *obj;
+      if ( ! obj.matchesEmpty() )
+      {
+        for_( it, obj.matchesBegin(), obj.matchesEnd() )
+        {
+          str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
+        }
+      }
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+  } //namespace detail
+  ///////////////////////////////////////////////////////////////////
+
+  detail::PoolQueryIterator PoolQuery::begin() const
+  {
+    return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/PoolQuery.h b/zypp/PoolQuery.h
new file mode 100644 (file)
index 0000000..2eabbad
--- /dev/null
@@ -0,0 +1,623 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PoolQuery.h
+ *
+*/
+#ifndef ZYPP_POOLQUERY_H
+#define ZYPP_POOLQUERY_H
+
+#include <iosfwd>
+#include <set>
+#include <map>
+
+#include "zypp/base/Regex.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/AttrMatcher.h"
+#include "zypp/sat/Pool.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace detail
+  {
+    class PoolQueryIterator;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PoolQuery
+  //
+  /**
+   * Meta-data query API. Returns solvables of specified kinds from specified
+   * repositories with attributes matching the specified search strings.
+   *
+   * The search strings can be specified via \ref addString() and
+   * \ref addAttribute() methods. String matching type can be set using the
+   * setMatch*() methods. Multiple search strings for a particular attribute
+   * will be combined into a regex (see \ref addString() and
+   * \ref addAttribute() for more details).
+   *
+   * The begin() and end() methods return a PoolQueryIterator returning
+   * \ref sat::Solvable objects which can easily be turned into \ref Resolvable
+   * objects. Additionally, thanx to the \ref sat::SolvIterMixin, a Selectable
+   * and PoolItem iterators are automatically available.
+   *
+   * \note You will sometimes face the problem, that when using the \ref PoolItem
+   * iterator you hit multiple version of the same package, while when using the
+   * \ref ui::Selectable iterator the information which of the available candidates
+   * actually matched got lost. In this case class \ref PoolItemBest may help you.
+   * Use it to pick the best version only.
+   *
+   * <code>
+   * PoolQuery q;
+   * q.addAttribute(sat::SolvAttr::name, "zypp*");
+   * q.addKind(ResKind::package);
+   * q.setMatchGlob();
+   *
+   * for (PoolQuery::Selectable_iterator it = q.selectableBegin();
+   *     it != q.selectableEnd(); ++it)
+   * {
+   *   ui::Selectable::constPtr s = *it;
+   *   // ...
+   * }
+   * </code>
+   *
+   * Performance considerations
+   *
+   * Results of simple queries like those using one string and/or one attribute
+   * and/or one repository are filtered by sat-solver's Dataiterator directly,
+   * and thus it is fast.
+   *
+   * Queries with multiple strings are implemented using regexes. Queries based
+   * on kinds, multiple repos, and multiple attributes are filtered inside
+   * the PoolQuery, so these tend to be slower.
+   *
+   * \see detail::PoolQueryIterator on how to inspect matches in detail.
+   * \see tests/zypp/PoolQuery_test.cc for more examples
+   * \see sat::SolvIterMixin
+   */
+  class PoolQuery : public sat::SolvIterMixin<PoolQuery, detail::PoolQueryIterator>
+  {
+  public:
+    typedef std::set<ResKind>                               Kinds;
+    typedef std::set<std::string>                           StrContainer;
+    typedef std::map<sat::SolvAttr, StrContainer>           AttrRawStrMap;
+
+    typedef detail::PoolQueryIterator                       const_iterator;
+    typedef unsigned int                                    size_type;
+
+  public:
+    typedef function<bool( const sat::Solvable & )> ProcessResolvable;
+
+    PoolQuery();
+    ~PoolQuery();
+
+    /** Query result accessers. */
+    //@{
+    /**
+     * Compile the query and return an iterator to the result.
+     *
+     * \return An iterator (\ref detail::PoolQueryIterator) returning
+     *         sat::Solvable objects pointing at the beginning of the query result.
+     * \throws sat::MatchInvalidRegexException if the query was about to use a regex which
+     *         failed to compile.
+     *
+     * \note Note that PoolQuery is derived from \ref sat::SolvIterMixin which
+     *       makes PoolItem and Selectable iterators automatically available.
+     * \see \ref sat::SolvIterMixin
+     */
+    const_iterator begin() const;
+
+    /** An iterator pointing to the end of the query result. */
+    const_iterator end() const;
+
+    /** Whether the result is empty. */
+    bool empty() const;
+
+    /** Number of solvables in the query result. */
+    size_type size() const;
+    //@}
+
+    /**
+     * Executes the query with the current settings.
+     * Results are yielded via the \a fnc callback.
+     */
+    void execute(ProcessResolvable fnc);
+
+    /**
+     * Filter by selectable kind.
+     *
+     * By default, all kinds will be returned. If addKind() is used,
+     * only the specified kinds will be returned (multiple kinds will be ORed).
+     *
+     * Pass ResKind constants to this method, (e.g. ResKind::package).
+     */
+    void addKind(const ResKind & kind);
+
+    /**
+     * Filter by repo.
+     *
+     * By default, all repos will be returned. If addRepo() is used,
+     * only the specified repo will be returned (multiple repos will be ORed).
+     */
+    void addRepo(const std::string &repoalias);
+
+    /** Installed status filter setters. */
+    //@{
+
+    /**
+     * Filter by status (installed uninstalled)
+     */
+    enum StatusFilter {
+      ALL = 0, // both install filter and uninstall filter bits are 0
+      INSTALLED_ONLY = 1,
+      UNINSTALLED_ONLY = 2
+    };
+
+    /** Return only @System repo packages */
+    void setInstalledOnly();
+    /** Return only packages from repos other than @System. */
+    void setUninstalledOnly();
+    /** Set status filter directly \see StatusFilter */
+    void setStatusFilterFlags( StatusFilter flags );
+
+    //@}
+
+    /**
+     * Add a global query string. The string added via this method is applied
+     * to all query attributes as if addAttribute(..., \value) was called
+     * for all of them.
+     *
+     * This method can be used multiple times in which case the query strings
+     * will be combined (together with strings added via addAttribute()) into
+     * a regex. Searched attribute value will match this regex if <b>any</b>
+     * of these strings will match the value. This can be changed by
+     * (not yet implemented) \ref setRequireAll() method.
+     */
+    void addString(const std::string & value);
+
+    /**
+     * Filter by the \a value of the specified \a attr attribute. This can
+     * be any of the available solvable attributes.
+     *
+     * This method can be used multiple times with the same \a attr in which
+     * case the query strings will be combined (together with strings added
+     * via addString()) into a regex. Searched attribute value will match
+     * this regex if <b>any</b> of these strings will match the value.
+     * This can be changed by (not yet implemented) \ref setRequireAll()
+     * method.
+     *
+     * \note Though it is possible to use dependency attributes like
+     * \ref Solv::Attr::provides here, note that the query string is
+     * matched against a dependencies \c "name" part only. Any
+     * <tt>"op edition"</tt> part of a \ref Capability is \b not
+     * considered at all. \see \ref addDependency on how to query for
+     * capabilities including edition ranges.
+     *
+     * \note Solvables of a kind not supporting the specified attribute will
+     * <b>not</b> be returned.
+     * \todo check the above
+     *
+     * \param attr Attribute identfier. Use sat::Solvattr::* constants
+     * \param value What to search for.
+     *
+     * \see sat::SolvAttr
+     */
+    void addAttribute( const sat::SolvAttr & attr, const std::string & value = "" );
+
+    /** \name Filter by dependencies matching a broken down capability <tt>name [op edition]</tt> and/or architecture.
+     *
+     * The capabilities \c name part may be defined as query string
+     * like with \ref addAttribute. Globing and regex are supported.
+     * Global query strings defined by \ref addString are considered.
+     *
+     * So without any <tt>op edition arch</tt> addDependency behaves the
+     * same as \ref addAttribute. If an edition range is given, matches
+     * are restricted accordingly. There are various overloads, so pick
+     * the one you like best.
+     *
+     * An optional \c arch argument will additionally require the matching
+     * solvable to be of this arch.
+     *
+     * \code
+     * {
+     *   setMatchGlob();
+     *   setCaseSensitive( false );
+     *   addDependency( sat::SolvAttr::provides, "kde*", Rel::EQ, Edition("2.0") );
+     *   addDependency( sat::SolvAttr::provides, "kde*", Edition("2.0") ); // same as above
+     * }
+     * {
+     *   setMatchGlob();
+     *   setCaseSensitive( false );
+     *   addString( "kde*" );
+     *   addDependency( sat::SolvAttr::provides, Rel::EQ, Edition("2.0") );// same as above
+     *   addDependency( sat::SolvAttr::provides, Edition("2.0") );         // same as above
+     * }
+     * \endcode
+     *
+     * \note Thre's also a version of \ref addDependency provided, that takes a
+     * complete \ref Capability as argument. This always requires an exact match
+     * of the name part (as the resolver would do it).
+     *
+     * This is the list of valid dependency attributes:
+     * \code
+     *   SolvAttr::provides
+     *   SolvAttr::obsoletes
+     *   SolvAttr::conflicts
+     *   SolvAttr::requires
+     *   SolvAttr::recommends
+     *   SolvAttr::suggests
+     *   SolvAttr::supplements
+     *   SolvAttr::enhances
+     * \endcode
+     *
+     * \note <b>What happens if a non dependency attribute is passed?<\b>
+     * If an edition range is given, it is matched against the matching
+     * solvables edition instead. Without edition range it behaves the
+     * same as \ref addAttribute.
+     *
+     * \code
+     *   // Find all packages providing "kernel > 2.0"
+     *   addDependency( sat::SolvAttr::provides, "kernel", Rel::GT, Edition("2.0") );
+     *
+     *   // // Find all packages named "kernel" and with edition "> 2.0"
+     *   addDependency( sat::SolvAttr::name, "kernel", Rel::GT, Edition("2.0") );
+     * \endcode
+     */
+    //@{
+    /** Query <tt>"name|global op edition"</tt>. */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition );
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch );
+
+    /** \overload Query <tt>"name|global == edition"</tt>. */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name, const Edition & edition )
+    { addDependency( attr, name, Rel::EQ, edition ); }
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name, const Edition & edition, const Arch & arch )
+    { addDependency( attr, name, Rel::EQ, edition, arch ); }
+
+    /** \overload Query <tt>"name|global"</tt>. */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name )
+    { addDependency( attr, name, Rel::ANY, Edition() ); }
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const std::string & name, const Arch & arch )
+    { addDependency( attr, name, Rel::ANY, Edition(), arch ); }
+
+    /** \overload Query <tt>"global op edition"</tt>.*/
+    void addDependency( const sat::SolvAttr & attr, const Rel & op, const Edition & edition )
+    { addDependency( attr, std::string(), op, edition ); }
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const Rel & op, const Edition & edition, const Arch & arch )
+    { addDependency( attr, std::string(), op, edition, arch ); }
+
+    /** \overload Query <tt>"global == edition"</tt>. */
+    void addDependency( const sat::SolvAttr & attr, const Edition & edition )
+    { addDependency( attr, std::string(), Rel::EQ, edition ); }
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const Edition & edition, const Arch & arch )
+    { addDependency( attr, std::string(), Rel::EQ, edition, arch ); }
+
+    /** \overload Query <tt>"global"</tt>. */
+    void addDependency( const sat::SolvAttr & attr )
+    { addDependency( attr, std::string(), Rel::ANY, Edition() ); }
+    /**  \overload also restricting architecture */
+    void addDependency( const sat::SolvAttr & attr, const Arch & arch )
+    { addDependency( attr, std::string(), Rel::ANY, Edition(), arch ); }
+
+    /** \overload Query taking a \ref Capability (always exact name match).
+     * \note If a non dependency attribute is passed, the \ref Capability
+     * will always be matched against the Solvables \c name and \c edition.
+    */
+    void addDependency( const sat::SolvAttr & attr, Capability cap_r );
+    //@}
+
+    /**
+     * Set version condition. This will filter out solvables not matching
+     * <tt>solvableEdition \a op \a edition</tt>.
+     *
+     * \param edition Edition to look for.
+     * \param op      Found-wanted relation operator.
+     */
+    void setEdition(const Edition & edition, const Rel & op = Rel::EQ);
+
+    /** \name Text Matching Options
+     * \note The implementation treats an empty search string as
+     * <it>"match always"</it>. So if you want to actually match
+     * an empty value, try <tt>( "^$", setMatchRegex )</tt>.
+     */
+    //@{
+    /**
+     * Turn case sentitivity on or off (unsets or sets \ref SEARCH_NOCASE flag).
+     * PoolQuery defaults to case insensitive search unless this method
+     * is used.
+     *
+     * \param value Whether to turn the case sensitivity on (default) or off.
+     */
+    void setCaseSensitive( bool value = true );
+
+    /**
+     * If set (default), look at the full path when searching in filelists.
+     * Otherwise just match the the basenames.
+     * \see \ref Match::FILES
+     */
+    void setFilesMatchFullPath( bool value = true );
+    /** \overload */
+    void setFilesMatchBasename( bool value = true )
+    { setFilesMatchFullPath( !value ); }
+
+    /** Set to match exact string instead of substring.*/
+    void setMatchExact();
+    /** Set to substring (the default). */
+    void setMatchSubstring();
+    /** Set to match globs. */
+    void setMatchGlob();
+    /** Set to use the query strings as regexes */
+    void setMatchRegex();
+    /** Set to match words (uses regex) */
+    void setMatchWord();
+    //void setLocale(const Locale & locale);
+    //@}
+
+    /**
+     * Require that all of the values set by addString or addAttribute
+     * match the values of respective attributes.
+     *
+     * \todo doesn't work yet, don't use this function
+     */
+    void setRequireAll( bool require_all = true );
+
+
+    /** \name getters */
+    //@{
+
+    /** Search strings added via addString() */
+    const StrContainer & strings() const;
+    /**
+     * Map (map<SolvAttr, StrContainer>) of attribute values added via
+     * addAttribute(), addDep in string form */
+    const AttrRawStrMap & attributes() const;
+
+    const StrContainer & attribute(const sat::SolvAttr & attr) const;
+
+    const Kinds & kinds() const;
+
+    const StrContainer & repos() const;
+
+    const Edition edition() const;
+    const Rel editionRel() const;
+
+    /**
+     * returns true if search is case sensitive
+     */
+    bool caseSensitive() const;
+
+    /** Whether searching in filelists looks at the full path or just at the basenames. */
+    bool filesMatchFullPath() const;
+    /** \overload */
+    bool filesMatchBasename() const
+    { return !filesMatchFullPath(); }
+
+    bool matchExact() const;
+    bool matchSubstring() const;
+    bool matchGlob() const;
+    bool matchRegex() const;
+    bool matchWord() const;
+
+    /** Returns string matching mode as enum.
+     * \see \ref Match::Mode
+     */
+    Match::Mode matchMode() const
+    { return flags().mode(); }
+
+    /**
+     * Whether all values added via addString() or addAttribute() are required
+     * to match the values of the respective attributes.
+     */
+    bool requireAll() const;
+
+    StatusFilter statusFilterFlags() const;
+    //@}
+
+    /**
+     * Reads from stream query. Attributes is sepated by delim. Query is
+     * separated by two delim.
+     *
+     * \param str input stream which contains query
+     * \param delim delimeter for attributes
+     * \return true if non-empty query is recovered
+     *
+     * \see readPoolQueriesFromFile
+     */
+    bool recover( std::istream &str, char delim = '\n' );
+
+    /**
+     * Writes a machine-readable string representation of the query to stream.
+     * Use \a delim as attribute delimiter.
+     *
+     * \param str output stream to write to
+     * \param delim delimiter for attributes
+     *
+     * \see writePoolQueriesToFile
+     */
+    void serialize( std::ostream &str, char delim = '\n' ) const;
+
+    /** Return a human-readable description of the query */
+    std::string asString() const;
+
+    bool operator==(const PoolQuery& b) const;
+    bool operator!=(const PoolQuery& b) const { return !(*this == b ); }
+
+    // low level API
+
+    /**
+     * Free function to get the satsolver repo search
+     * flags.
+     *
+     * \see \ref Match
+     */
+    Match flags() const;
+
+    /**
+     * Free function to set the satsolver repo search
+     * flags.
+     *
+     * \see \ref Match
+     */
+    void setFlags( const Match & flags );
+
+  public:
+    class Impl;
+  private:
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolQuery Stream output. */
+  std::ostream & operator<<( std::ostream & str, const PoolQuery & obj );
+
+  /** \relates PoolQuery Detailed stream output. */
+  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj );
+
+  ///////////////////////////////////////////////////////////////////
+  namespace detail
+  { /////////////////////////////////////////////////////////////////
+
+  class PoolQueryMatcher;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PoolQuery::PoolQueryIterator
+  //
+  /** \ref PoolQuery iterator as returned by \ref PoolQuery::begin.
+   *
+   * The \ref PoolQueryIterator visits sat::Solavables that do contain matches.
+   *
+   * But it also provides an iterator by itself, to allow a detailed inspection of
+   * the individual attribute matches within the current Solvable.
+   */
+  class PoolQueryIterator : public boost::iterator_adaptor<
+    PoolQueryIterator                  // Derived
+    , sat::LookupAttr::iterator        // Base
+    , const sat::Solvable              // Value
+    , boost::forward_traversal_tag     // CategoryOrTraversal
+    , const sat::Solvable              // Reference
+  >
+  {
+      typedef std::vector<sat::LookupAttr::iterator> Matches;
+    public:
+      typedef Matches::size_type size_type;
+      typedef Matches::const_iterator matches_iterator;
+    public:
+      /** Default ctor is also \c end.*/
+      PoolQueryIterator()
+      {}
+
+      /** \Ref PoolQuery ctor. */
+      PoolQueryIterator( const shared_ptr<PoolQueryMatcher> & matcher_r )
+      : _matcher( matcher_r )
+      { increment(); }
+
+      /** \name Detailed inspection of attribute matches within the current Solvable.
+       *
+       * The \ref matches_iterator visits all attribute matches within the current Solvable,
+       * providing a \ref sat::LookupAttr::iterator pointing to attribute. While a
+       * \ref matches_iterator itself becomes invalid if the PoolQueryIterator is advanced,
+       * the \ref sat::LookupAttr::iterator it pointed to stays valid, even after the query
+       * ended.
+       *
+       * \code
+       * // Setup query for "libzypp"  in name or requires:
+       * PoolQuery q;
+       * q.addString( "libzypp" );
+       * q.setMatchSubstring();
+       * q.setCaseSensitive( false );
+       * q.addAttribute( sat::SolvAttr::name );
+       * q.addDependency( sat::SolvAttr::requires );
+       *
+       * // Iterate the result:
+       * for_( solvIter, q.begin(), q.end() )
+       * {
+       *   sat::Solvable solvable( *solvIter );
+       *   cout << "Found matches in " << solvable << endl;
+       *   if ( verbose )
+       *     for_( attrIter, solvIter.matchesBegin(), solvIter.matchesEnd() )
+       *     {
+       *       sat::LookupAttr::iterator attr( *attrIter );
+       *       cout << "    " << attr.inSolvAttr() << "\t\"" << attr.asString() << "\"" << endl;
+       *     }
+       * }
+       *
+       *
+       * Found matches in PackageKit-0.3.11-1.12.i586(@System)
+       *    solvable:requires        "libzypp.so.523"
+       * Found matches in libqdialogsolver1-1.2.6-1.1.2.i586(@System)
+       *    solvable:requires        "libzypp.so.523"
+       *    solvable:requires        "libzypp >= 5.25.3-0.1.2"
+       * Found matches in libzypp-5.30.3-0.1.1.i586(@System)
+       *    solvable:name            "libzypp"
+       * Found matches in libzypp-testsuite-tools-4.2.6-8.1.i586(@System)
+       *    solvable:name            "libzypp-testsuite-tools"
+       *    solvable:requires        "libzypp.so.523"
+       * ...
+       * \endcode
+       */
+      //@{
+      /** \c False unless this is the \c end iterator. */
+      bool matchesEmpty() const                        { return ! _matcher; }
+      /** Number of attribute matches. */
+      size_type matchesSize() const            { return matches().size(); }
+      /** Begin of matches. */
+      matches_iterator matchesBegin() const    { return matches().begin(); }
+      /** End of matches. */
+      matches_iterator matchesEnd() const      { return matches().end(); }
+      //@}
+
+    private:
+      friend class boost::iterator_core_access;
+
+      sat::Solvable dereference() const
+      { return base_reference().inSolvable(); }
+
+      void increment();
+
+    private:
+      const Matches & matches() const;
+
+    private:
+      shared_ptr<PoolQueryMatcher> _matcher;
+      mutable shared_ptr<Matches>  _matches;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolQueryIterator Stream output. */
+  inline std::ostream & operator<<( std::ostream & str, const PoolQueryIterator & obj )
+  { return str << obj.base(); }
+
+  /** \relates PoolQueryIterator Detailed stream output. */
+  std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj );
+
+  ///////////////////////////////////////////////////////////////////
+  } //namespace detail
+  ///////////////////////////////////////////////////////////////////
+
+  inline detail::PoolQueryIterator PoolQuery::end() const
+  { return detail::PoolQueryIterator(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_POOLQUERY_H
diff --git a/zypp/PoolQueryResult.cc b/zypp/PoolQueryResult.cc
new file mode 100644 (file)
index 0000000..c69f69c
--- /dev/null
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolQueryResult.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/PoolQueryResult.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const PoolQueryResult & obj )
+  {
+    return dumpRange( str, obj.begin(), obj.end() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PoolQueryResult.h b/zypp/PoolQueryResult.h
new file mode 100644 (file)
index 0000000..fded69a
--- /dev/null
@@ -0,0 +1,234 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PoolQueryResult.h
+ *
+*/
+#ifndef ZYPP_POOLQUERYRESULT_H
+#define ZYPP_POOLQUERYRESULT_H
+
+#include <iosfwd>
+
+#include "zypp/base/Tr1hash.h"
+#include "zypp/base/Exception.h"
+#include "zypp/sat/SolvIterMixin.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/PoolQuery.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PoolQueryResult
+  //
+  /** Helper class to collect (not only) \ref PoolQuery results.
+   *
+   * \note Unfortunately \ref PoolQuery::begin might throw. Exceptions
+   * are caught and the query is treated as empty.
+   *
+   * \ref PoolQueryResult maintains a set of \ref sat::Solvable. You can
+   * add/remove solvables to/from the set defined by:
+   *
+   * \li a single \ref sat::Solvable
+   * \li a single \ref PoolItem
+   * \li a \ref PoolQuery
+   * \li an other \ref PoolQueryResult
+   * \li any iterator pair with \c value_type \ref sat::Solvable
+   *     or \ref PoolItem or \ref PoolQuery or any type that fits
+   *     \c operator+=.
+   *
+   * The class is a \ref sat::SolvIterMixin, so you can iterate the result
+   * not just as \ref sat::Solvable, but also as \ref PoolItem or
+   * \ref ui::Selectable.
+   *
+   * \code
+   *   // Constructed from PoolItem iterator pair
+   *   PoolQueryResult result( pool.byKindBegin<Package>(), pool.byKindEnd<Package>() );
+   *   MIL << result.size() << endl;
+   *
+   *   {
+   *     // Removing a PoolQuery result
+   *     PoolQuery q;
+   *     q.addAttribute( sat::SolvAttr::name, "[a-zA-Z]*" );
+   *     q.setMatchGlob();
+   *     result -= q;
+   *     MIL << result.size() << endl;
+   *   }
+   *   MIL << result << endl;
+   *
+   *   // Removing a range of sat::Solvables
+   *   sat::WhatProvides poviders( Capability("3ddiag") );
+   *   result -= PoolQueryResult( poviders.begin(), poviders.end() );
+   *
+   *   // packages not starting with a letter, except 3ddiag
+   *   MIL << result << endl;
+   * \endcode
+   */
+  class PoolQueryResult : public sat::SolvIterMixin<PoolQueryResult,std::tr1::unordered_set<sat::Solvable>::const_iterator>
+  {
+    public:
+      typedef std::tr1::unordered_set<sat::Solvable>   ResultSet;
+      typedef ResultSet::size_type                      size_type;
+      typedef ResultSet::const_iterator                 const_iterator;
+
+    public:
+      /** Default ctor (empty result) */
+      PoolQueryResult()
+      {}
+
+      /** Ctor adding one \ref sat::Solvable. */
+      explicit PoolQueryResult( sat::Solvable result_r )
+      { operator+=( result_r ); }
+
+      /** Ctor adding one \ref PoolItem. */
+      explicit PoolQueryResult( const PoolItem & result_r )
+      { operator+=( result_r ); }
+
+      /** Ctor adding one \ref PoolQuery result. */
+      explicit PoolQueryResult( const PoolQuery & query_r )
+      { operator+=( query_r ); }
+
+      /** Ctor adding a range of items for which \ref operator+= is defined. */
+      template<class _QueryResultIter>
+      PoolQueryResult( _QueryResultIter begin_r, _QueryResultIter end_r )
+      {
+        for_( it, begin_r, end_r )
+        {
+          operator+=( *it );
+        }
+      }
+
+    public:
+      /** Whether the result is empty. */
+      bool empty() const
+      { return _result.empty(); }
+      /** The number of \ref sat::Solvables. */
+      size_type size() const
+      { return _result.size(); }
+      /** */
+      const_iterator begin() const
+      { return _result.begin(); }
+      /** */
+      const_iterator end() const
+      { return _result.end(); }
+
+      /** Test whether some item is in the result set. */
+      bool contains(sat::Solvable result_r ) const
+      { return( _result.find( result_r ) != _result.end() ); }
+      /** \overload */
+      bool contains( const PoolItem & result_r ) const
+      { return contains( result_r.satSolvable() ); }
+
+    public:
+      /** Clear the result. */
+      void clear()
+      { _result.clear(); }
+
+      /** Add items to the result. */
+      PoolQueryResult & operator+=( const PoolQueryResult & query_r )
+      {
+        if ( ! query_r.empty() )
+          _result.insert( query_r.begin(), query_r.end() );
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator+=( const PoolQuery & query_r )
+      {
+        try
+        {
+          for_( it, query_r.begin(), query_r.end() )
+            _result.insert( *it );
+        }
+        catch ( const Exception & )
+        {}
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator+=( sat::Solvable result_r )
+      {
+        _result.insert( result_r );
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator+=( const PoolItem & result_r )
+      {
+        _result.insert( result_r.satSolvable() );
+        return *this;
+      }
+
+      /** Remove Items from the result. */
+      PoolQueryResult & operator-=( const PoolQueryResult & query_r )
+      {
+        if ( &query_r == this ) // catch self removal!
+          clear();
+        else
+          for_( it, query_r.begin(), query_r.end() )
+            _result.erase( *it );
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator-=( const PoolQuery & query_r )
+      {
+        try
+        {
+          for_( it, query_r.begin(), query_r.end() )
+            _result.erase( *it );
+        }
+        catch ( const Exception & )
+        {}
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator-=( sat::Solvable result_r )
+      {
+        _result.erase( result_r );
+        return *this;
+      }
+      /** \overload */
+      PoolQueryResult & operator-=( const PoolItem & result_r )
+      {
+        _result.erase( result_r.satSolvable() );
+        return *this;
+      }
+
+    public:
+      /** Combine results. */
+      PoolQueryResult operator+( const PoolQueryResult & query_r ) const
+      { return PoolQueryResult(*this) += query_r; }
+      /** \overload */
+      PoolQueryResult operator+( const PoolQuery & query_r ) const
+      { return PoolQueryResult(*this) += query_r; }
+      /** \overload */
+      PoolQueryResult operator+( sat::Solvable result_r ) const
+      { return PoolQueryResult(*this) += result_r; }
+
+      /** Intersect results. */
+      PoolQueryResult operator-( const PoolQueryResult & query_r ) const
+      { return PoolQueryResult(*this) -= query_r; }
+      /** \overload */
+      PoolQueryResult operator-( const PoolQuery & query_r ) const
+      { return PoolQueryResult(*this) -= query_r; }
+      /** \overload */
+      PoolQueryResult operator-( sat::Solvable result_r ) const
+      { return PoolQueryResult(*this) -= result_r; }
+
+    private:
+      ResultSet _result;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PoolQueryResult Stream output */
+  std::ostream & operator<<( std::ostream & str, const PoolQueryResult & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOLQUERYRESULT_H
diff --git a/zypp/PoolQueryUtil.tcc b/zypp/PoolQueryUtil.tcc
new file mode 100644 (file)
index 0000000..4a0ec40
--- /dev/null
@@ -0,0 +1,78 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PoolQueryUtil.tcc
+ *
+ * including fstream is not hell here because this header only included
+ * by implementation file, header doesn't need include it.
+*/
+#ifndef ZYPP_POOLQUERYUTIL_TCC
+#define ZYPP_POOLQUERYUTIL_TCC
+
+#include <fstream>
+
+#include "zypp/Pathname.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/base/String.h"
+
+namespace zypp
+{
+
+  /**
+   * sends to output iterator all queries readed from file.
+   *
+   * \code
+   *  list<PoolQuery> s;
+   *  insert_iterator<list<PoolQuery> > ii(s, s.end());
+   *  readPoolQueriesFromStream(f,ii);
+   * \endcode
+   */
+  template <class OutputIterator>
+  void readPoolQueriesFromFile(const zypp::filesystem::Pathname &file,
+      OutputIterator out )
+  {
+    bool found;
+    std::ifstream fin( file.c_str() );
+
+    if (!fin)
+      ZYPP_THROW(Exception(str::form("Cannot open file %s",file.c_str())));
+
+    do
+    {
+      zypp::PoolQuery q;
+      found = q.recover( fin );
+      if (found)
+        *out++ = q;
+    } while ( found );
+
+    fin.close();
+  }
+
+  /**
+   * Writes all queries from begin to end.
+   */
+
+  template <class InputIterator>
+  void writePoolQueriesToFile(const zypp::filesystem::Pathname &file,
+      InputIterator begin, InputIterator end )
+  {
+    std::ofstream fout( file.c_str(), std::ios_base::out | std::ios_base::trunc );
+
+    if (!fout)
+      ZYPP_THROW(Exception(str::form("Cannot open file %s",file.c_str())));
+
+    for_( it, begin, end )
+    {
+      it->serialize( fout );
+    }
+
+    fout.close();
+  }
+
+}
+#endif // ZYPP_POOLQUERYUTIL_H
diff --git a/zypp/ProblemSolution.cc b/zypp/ProblemSolution.cc
new file mode 100644 (file)
index 0000000..cc741f3
--- /dev/null
@@ -0,0 +1,125 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* ProblemSolution.cc
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "zypp/solver/detail/Types.h"
+#include "zypp/solver/detail/SolutionAction.h"
+#include "zypp/ProblemSolution.h"
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/Resolver.h"
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(ProblemSolution);
+
+//---------------------------------------------------------------------------
+
+ostream&
+operator<<( ostream& os, const ProblemSolution & solution)
+{
+    os << "Solution:" << endl;
+    os << solution._description << endl;
+    os << solution._details << endl;
+    os << solution._actions;
+    return os;
+}
+
+ostream&
+operator<<( ostream& os, const ProblemSolutionList & solutionlist)
+{
+    for (ProblemSolutionList::const_iterator iter = solutionlist.begin(); iter != solutionlist.end(); ++iter) {
+       os << *(*iter) << endl;
+    }
+    return os;
+}
+
+ostream&
+operator<<( ostream& os, const CProblemSolutionList & solutionlist)
+{
+    for (CProblemSolutionList::const_iterator iter = solutionlist.begin(); iter != solutionlist.end(); ++iter) {
+       os << *(*iter) << endl;
+    }
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+ProblemSolution::ProblemSolution( ResolverProblem_Ptr parent, const string & description, const string & details )
+    : _problem (parent)
+    , _description (description)
+    , _details (details)
+{
+}
+
+
+ProblemSolution::~ProblemSolution()
+{
+}
+
+
+/**
+ * Apply this solution, i.e. execute all of its actions.
+ *
+ * Returns 'true' on success, 'false' if actions could not be performed.
+ **/
+
+bool
+ProblemSolution::apply (solver::detail::Resolver & resolver)
+{
+    DBG << "apply solution " << *this << endl;
+    bool ret = true;
+    for (solver::detail::CSolutionActionList::const_iterator iter = _actions.begin();
+        iter != _actions.end(); ++iter) {
+       solver::detail::SolutionAction_constPtr action = *iter;
+       if (! action->execute (resolver))
+       {
+           ret = false;
+           break;
+       }
+    }
+    return true;
+}
+
+
+/**
+ * Add an action to the actions list.
+ **/ 
+void
+ProblemSolution::addAction (solver::detail::SolutionAction_constPtr action)
+{
+    _actions.push_back (action);
+}
+
+
+void
+ProblemSolution::clear()
+{
+    _actions.clear();
+}
+
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/ProblemSolution.h b/zypp/ProblemSolution.h
new file mode 100644 (file)
index 0000000..5bedec3
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Author: Stefan Hundhammer <sh@suse.de>
+ *
+ **/
+
+#ifndef ZYPP_PROBLEMSOLUTION_H
+#define ZYPP_PROBLEMSOLUTION_H
+
+#include <list>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/ResolverProblem.h"
+#include "zypp/solver/detail/SolutionAction.h"
+#include "zypp/solver/detail/Types.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+    
+    /**
+     * Class representing one possible solution to one problem found during resolving
+     *
+     * All problems should have at least 2-3 (mutually exclusive) solutions:
+     *
+     *    -     Undo: Do not perform the offending transaction
+     *  (do not install the package that had unsatisfied requirements,
+     *   do not remove  the package that would break other packages' requirements)
+     *
+     *    - Remove referrers: Remove all packages that would break because
+     * they depend on the package that is requested to be removed
+     *
+     *    - Ignore: Inject artificial "provides" for a missing requirement
+     * (pretend that requirement is satisfied)
+     **/
+    class ProblemSolution : public base::ReferenceCounted
+    {
+    protected:
+           
+       /**
+        * Clear all data.
+        * In particular, delete all members of _actions.
+        **/
+       void clear();
+           
+       //
+       // Data members
+       //
+       ResolverProblem_Ptr     _problem;
+       solver::detail::CSolutionActionList     _actions;
+       std::string             _description;
+       std::string             _details;
+
+    public:
+
+       /**
+        * Constructor.
+        **/
+       ProblemSolution( ResolverProblem_Ptr parent, const  std::string & description, const std::string & details );
+
+       /**
+        * Destructor.
+        **/
+       ~ProblemSolution();
+
+       // ---------------------------------- I/O
+
+       friend std::ostream& operator<<(std::ostream&, const ProblemSolution & solution);
+       friend std::ostream& operator<<(std::ostream&, const ProblemSolutionList & solutionlist);
+       friend std::ostream& operator<<(std::ostream&, const CProblemSolutionList & solutionlist);
+
+       // ---------------------------------- accessors
+       /**
+        * Return a one-line text description of this solution.
+        **/
+       std::string description() const { return _description; }
+
+       /**
+        * Return a (possibly multi-line) detailed description of this
+        * solution or an empty string if there are no useful details.
+        **/
+       std::string details() const { return _details; }
+
+       /**
+        * Return the parent dependency problem.
+        **/
+       ResolverProblem_Ptr problem() const { return _problem; }
+
+       // ---------------------------------- methods
+
+       /**
+        * Apply this solution, i.e. execute all of its actions.
+        *
+        * Returns 'true' on success, 'false' if actions could not be performed.
+        **/
+       bool apply (solver::detail::Resolver & resolver);
+
+       /**
+        * Add an action to the actions list.
+        **/ 
+       void addAction( solver::detail::SolutionAction_constPtr action );
+
+       solver::detail::CSolutionActionList actions() {return _actions;}
+
+    };
+
+
+    ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_PROBLEMSOLUTION_H
+
diff --git a/zypp/ProblemTypes.h b/zypp/ProblemTypes.h
new file mode 100644 (file)
index 0000000..051b34a
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Types.h
+ *
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_PROBLEMTYPES_H
+#define ZYPP_PROBLEMTYPES_H
+
+#include <iosfwd>
+#include <list>
+#include <set>
+#include <map>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Functional.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp 
+{ ///////////////////////////////////////////////////////////////////////
+    
+    DEFINE_PTR_TYPE(Resolver);
+    
+    DEFINE_PTR_TYPE(ProblemSolution);
+    typedef std::list<ProblemSolution_Ptr> ProblemSolutionList;
+    typedef std::list<ProblemSolution_constPtr> CProblemSolutionList;
+    
+    DEFINE_PTR_TYPE(ResolverProblem);
+    typedef std::list<ResolverProblem_Ptr> ResolverProblemList;
+    typedef std::list<ResolverProblem_constPtr> CResolverProblemList;
+
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_TYPES_H
diff --git a/zypp/Product.cc b/zypp/Product.cc
new file mode 100644 (file)
index 0000000..b8c0441
--- /dev/null
@@ -0,0 +1,232 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Product.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/Product.h"
+#include "zypp/Url.h"
+
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/WhatProvides.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/PoolItem.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(Product);
+
+  namespace
+  {
+    void fillList( std::list<Url> & ret_r, sat::Solvable solv_r, sat::SolvAttr attr_r )
+    {
+      sat::LookupAttr query( attr_r, solv_r );
+      for_( it, query.begin(), query.end() )
+      {
+        try // ignore malformed urls
+        {
+          ret_r.push_back( Url( it.asString() ) );
+        }
+        catch( const url::UrlException & )
+        {}
+      }
+    }
+
+    void fillList( std::list<std::string> & ret_r, sat::Solvable solv_r, sat::SolvAttr attr_r )
+    {
+      sat::LookupAttr query( attr_r, solv_r );
+      for_( it, query.begin(), query.end() )
+      {
+        ret_r.push_back( it.asString() );
+      }
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Product::Product
+  //   METHOD TYPE : Ctor
+  //
+  Product::Product( const sat::Solvable & solvable_r )
+  : ResObject( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Product::~Product
+  //   METHOD TYPE : Dtor
+  //
+  Product::~Product()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+
+  sat::Solvable Product::referencePackage() const
+  {
+    // Look for a  provider of 'product(name) = version' of same
+    // architecture and within the same repo.
+    //
+    // bnc #497696: Update repos may have multiple release package versions
+    // providing the same product. As a workaround we link to the one with
+    // the highest version.
+    Capability identCap( str::form( "product(%s) = %s", name().c_str(), edition().c_str() ) );
+
+    sat::Solvable found;
+    sat::WhatProvides providers( identCap );
+    for_( it, providers.begin(), providers.end() )
+    {
+      if ( it->repository() == repository()
+           && it->arch() == arch() )
+      {
+        if ( ! found || found.edition() < it->edition() )
+          found = *it;
+      }
+    }
+    if ( ! found )
+      WAR << *this << ": no reference package found: " << identCap << endl;
+    return found;
+  }
+
+  std::string Product::referenceFilename() const
+  { return lookupStrAttribute( sat::SolvAttr::productReferenceFile ); }
+
+  Product::ReplacedProducts Product::replacedProducts() const
+  {
+    std::vector<constPtr> ret;
+    // By now we simply collect what is obsoleted by the Product,
+    // or by the products buddy (release-package).
+
+    // Check our own dependencies. We should not have any,
+    // but just to be shure.
+    sat::WhatObsoletes obsoleting( satSolvable() );
+    for_( it, obsoleting.begin(), obsoleting.end() )
+    {
+      if ( it->isKind( ResKind::product ) )
+        ret.push_back( make<Product>( *it ) );
+    }
+
+    // If we have a buddy, we check what product buddies the
+    // buddy replaces.
+    obsoleting = sat::WhatObsoletes( poolItem().buddy() );
+    for_( it, obsoleting.poolItemBegin(), obsoleting.poolItemEnd() )
+    {
+      if ( (*it).buddy().isKind( ResKind::product ) )
+        ret.push_back( make<Product>( (*it).buddy() ) );
+    }
+    return ret;
+  }
+
+  CapabilitySet Product::droplist() const
+  { return poolItem().buddy().valuesOfNamespace( "weakremover" ); }
+
+  std::string Product::productLine() const
+  { return lookupStrAttribute( sat::SolvAttr::productProductLine ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::string Product::shortName() const
+  { return lookupStrAttribute( sat::SolvAttr::productShortlabel ); }
+
+  std::string Product::flavor() const
+  {
+    // Look for a  provider of 'product_flavor(name) = version'
+    // within the same repo. Unlike the reference package, we
+    // can be relaxed and ignore the architecture.
+    Capability identCap( str::form( "product_flavor(%s) = %s", name().c_str(), edition().c_str() ) );
+
+    sat::WhatProvides providers( identCap );
+    for_( it, providers.begin(), providers.end() )
+    {
+      if ( it->repository() == repository() )
+      {
+        // Got the package now try to get the provided 'flavor(...)'
+        Capabilities provides( it->provides() );
+        for_( cap, provides.begin(), provides.end() )
+        {
+          std::string capstr( cap->asString() );
+          if ( str::hasPrefix( capstr, "flavor(" ) )
+          {
+            capstr = str::stripPrefix( capstr, "flavor(" );
+            capstr.erase( capstr.size()-1 ); // trailing ')'
+            return capstr;
+          }
+        }
+      }
+    }
+    return std::string();
+  }
+
+  std::string Product::type() const
+  { return lookupStrAttribute( sat::SolvAttr::productType ); }
+
+  std::list<std::string> Product::flags() const
+  {
+    std::list<std::string> ret;
+    fillList( ret, satSolvable(), sat::SolvAttr::productFlags );
+    return ret;
+  }
+
+  bool Product::isTargetDistribution() const
+  { return isSystem() && lookupStrAttribute( sat::SolvAttr::productType ) == "base"; }
+
+  std::string Product::registerTarget() const
+  { return lookupStrAttribute( sat::SolvAttr::productRegisterTarget ); }
+
+  std::string Product::registerRelease() const
+  { return lookupStrAttribute( sat::SolvAttr::productRegisterRelease ); }
+
+  /////////////////////////////////////////////////////////////////
+
+  Product::UrlList Product::urls( const std::string & key_r ) const
+  {
+    UrlList ret;
+
+    sat::LookupAttr url( sat::SolvAttr::productUrl, *this );
+    sat::LookupAttr url_type( sat::SolvAttr::productUrlType, *this );
+
+    sat::LookupAttr::iterator url_it(url.begin());
+    sat::LookupAttr::iterator url_type_it(url_type.begin());
+
+    for (;url_it != url.end(); ++url_it, ++url_type_it)
+    {
+        /* safety checks, shouldn't happen (tm) */
+        if (url_type_it == url_type.end())
+        {
+            ERR << *this << " : The thing that should not happen, happened." << endl;
+            break;
+        }
+
+        if ( url_type_it.asString() == key_r )
+        {
+            ret._list.push_back(url_it.asString());
+        }
+    } /* while (attribute array) */
+
+    return ret;
+  }
+
+  Product::UrlList Product::releaseNotesUrls() const { return urls( "releasenotes" ); }
+  Product::UrlList Product::registerUrls()     const { return urls( "register" ); }
+  Product::UrlList Product::smoltUrls()        const { return urls( "smolt" ); }
+  Product::UrlList Product::updateUrls()       const { return urls( "update" ); }
+  Product::UrlList Product::extraUrls()        const { return urls( "extra" ); }
+  Product::UrlList Product::optionalUrls()     const { return urls( "optional" ); }
+
+  std::ostream & operator<<( std::ostream & str, const Product::UrlList & obj )
+  { return dumpRange( str << obj.key() << ' ', obj.begin(), obj.end() ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Product.h b/zypp/Product.h
new file mode 100644 (file)
index 0000000..7632803
--- /dev/null
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Product.h
+ *
+*/
+#ifndef ZYPP_PRODUCT_H
+#define ZYPP_PRODUCT_H
+
+#include <list>
+#include <string>
+
+#include "zypp/ResObject.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  DEFINE_PTR_TYPE(Product);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Product
+  //
+  /** Product interface.
+  */
+  class Product : public ResObject
+  {
+  public:
+    typedef Product                  Self;
+    typedef ResTraits<Self>          TraitsType;
+    typedef TraitsType::PtrType      Ptr;
+    typedef TraitsType::constPtrType constPtr;
+
+  public:
+    /** The reference package providing the product metadata,
+     *  if such a package exists.
+     */
+    sat::Solvable referencePackage() const;
+
+    /** For installed products the name of the coddesponding
+     * \c /etc/products.d entry.
+    .*/
+    std::string referenceFilename() const;
+
+    /** List of packages included in older versions of this product and now dropped.
+     *
+     * This evaluates the \ref referencePackage \c weakremover namespace. It actually
+     * returns a \ref CapabilitySet, because we support to drop specific versions or
+     * version ranges of a package. Use \ref sat::WhatProvides to get the actually
+     * installed and available packages matching this list.
+     * \code
+     *   const Product & openSUSE;
+     *   sat::WhatProvides dropped( openSUSE.droplist() );
+     *   for_( it, dropped.poolItemBegin(), dropped.poolItemEnd() )
+     *   {
+     *     if ( it->status().isInstalled() )
+     *     {
+     *       MIL << "Installed but no longer supported package: " << *it << endl;
+     *     }
+     *   }
+     * \endcode
+     */
+    CapabilitySet droplist() const;
+
+  public:
+    /***/
+    typedef std::vector<constPtr> ReplacedProducts;
+
+    /** Array of \b installed Products that would be replaced by
+     *  installing this one.
+     */
+    ReplacedProducts replacedProducts() const;
+
+    /** Vendor specific string denoting the product line. */
+    std::string productLine() const;
+
+  public:
+    /** Untranslated short name like <tt>SLES 10</tt>*/
+    std::string shortName() const;
+
+    /** The product flavor (LiveCD Demo, FTP edition,...). */
+    std::string flavor() const;
+
+    /** Get the product type
+     * Well, in an ideal world there is only one base product.
+     * It's the installed product denoted by a symlink in
+     * \c /etc/products.d.
+     * \deprecated Use isTargetDistribution to test for the installed base product,
+     * other wise type is empty for almost all products.
+    */
+    std::string type() const ZYPP_DEPRECATED;
+
+    /** The product flags */
+    std::list<std::string> flags() const;
+
+  public:
+    /** This is the \b installed product that is also targeted by the
+     *  \c /etc/products.d/baseproduct symlink.
+    */
+    bool isTargetDistribution() const;
+
+    /** This is \c register.target attribute of an \b installed product.
+      * Used for registration.
+      */
+    std::string registerTarget() const;
+
+    /** This is \c register.release attribute of an \b installed product.
+      * Used for registration.
+      */
+    std::string registerRelease() const;
+
+  public:
+    /***/
+    class UrlList;
+
+    /** Rerieve urls flagged with \c key_r for this product.
+     *
+     * This is the most common interface. There are convenience methods for
+     * wellknown flags like \c "releasenotes", \c "register", \c "updateurls",
+     * \c "extraurls", \c "optionalurls" and \c "smolt" below.
+     */
+    UrlList urls( const std::string & key_r ) const;
+
+    /** The URL to download the release notes for this product. */
+    UrlList releaseNotesUrls() const;
+
+    /** The URL for registration. */
+    UrlList registerUrls() const;
+
+    /** The URL for SMOLT \see http://smolts.org/wiki/Main_Page. */
+    UrlList smoltUrls() const;
+
+    /**
+     * Online updates for the product.
+     * They are complementary, not alternatives. #163192
+     */
+    UrlList updateUrls() const;
+
+    /**
+     * Additional software for the product
+     * They are complementary, not alternatives.
+     */
+    UrlList extraUrls() const;
+
+    /**
+     * Optional software for the product.
+     * (for example. Non OSS repositories)
+     * They are complementary, not alternatives.
+     */
+    UrlList optionalUrls() const;
+
+  protected:
+    friend Ptr make<Self>( const sat::Solvable & solvable_r );
+    /** Ctor */
+    Product( const sat::Solvable & solvable_r );
+    /** Dtor */
+    virtual ~Product();
+  };
+
+  /** Helper to iterate a products URL lists.
+   * \ref first is a convenience for 'lists' with just
+   * one entry (e.g. releaseNotesUrls)
+   */
+  class Product::UrlList
+  {
+    private:
+      /** \todo Change to directly iterate the .solv */
+      typedef std::list<Url> ListType;
+
+    public:
+      typedef ListType::value_type     value_type;
+      typedef ListType::size_type      size_type;
+      typedef ListType::const_iterator const_iterator;
+
+      bool empty() const
+      { return _list.empty(); }
+
+      size_type size() const
+      { return _list.size(); }
+
+      const_iterator begin() const
+      { return _list.begin(); }
+
+      const_iterator end() const
+      { return _list.end(); }
+
+      /** The first Url or an empty Url. */
+      Url first() const
+      { return empty() ? value_type() : _list.front(); }
+
+    public:
+      /** The key used to retrieve this list (for debug) */
+      std::string key() const
+      { return _key; }
+
+    private:
+      friend class Product;
+      /** Change to directly iterate the .solv */
+      std::string _key;
+      ListType    _list;
+  };
+
+  /** \relates Product::UrlList Stream output. */
+  std::ostream & operator<<( std::ostream & str, const Product::UrlList & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PRODUCT_H
diff --git a/zypp/ProgressData.cc b/zypp/ProgressData.cc
new file mode 100644 (file)
index 0000000..c08fe0d
--- /dev/null
@@ -0,0 +1,160 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ProgressData.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/ProgressData.h"
+
+using std::endl;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define  ZYPP_BASE_LOGGER_LOGGROUP "Progress"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ProgressData::report
+  //   METHOD TYPE : void
+  //
+  bool ProgressData::report()
+  {
+    bool goOn     = true;  // continue per default
+    bool doReport = false;
+
+    // compute value and check whether to report it
+    if ( hasRange() )
+    {
+      value_type newVal = _d->_val * 100;
+      newVal /= ( _d->_max - _d->_min );
+
+      if ( newVal - _d->_last_val > 20
+          || Date::now() - _d->_last_send > 1
+          || ( newVal == 100 && _d->_last_send != 100 )
+          || _d->_state == END )
+      {
+       _d->_last_val  = newVal;
+       _d->_last_send = Date::now();
+       doReport = true;
+      }
+    }
+    else
+    {
+      if ( Date::now() - _d->_last_send > 1 || _d->_state == END )
+      {
+       _d->_last_val  = _d->_val;
+       _d->_last_send = Date::now();
+       doReport = true;
+      }
+    }
+
+    // report if necessary
+    if ( doReport )
+    {
+      if ( _d->_state == INIT )
+      {
+       _d->_state = RUN;
+      }
+
+      if ( _d->_receiver )
+      {
+       goOn = _d->_receiver( *this );
+      }
+      else
+      {
+       if ( _d->_state != END )
+       {
+         XXX << str::form( "{#%u|%s}(%lld%s)",
+                           numericId(), name().c_str(),
+                           _d->_last_val, ( hasRange() ? "%" : "!" ) ) << endl;
+       }
+       else
+       {
+         DBG << str::form( "{#%u|%s}END", numericId(), name().c_str() ) << endl;
+       }
+      }
+    }
+
+    // log abort request and return
+    if ( ! goOn && _d->_state != END )
+    {
+      WAR << "User request to ABORT pending action. "
+         << str::form( "{#%u|%s}(%lld%s)",
+                       numericId(), name().c_str(),
+                       _d->_last_val, ( hasRange() ? "%" : "!" ) ) << endl;
+    }
+    return goOn;
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const ProgressData & obj )
+  {
+    if ( obj.hasRange() )
+    {
+      return str << str::form( "{%u|%s}[%lld,%lld](%lld)%lld%%)",
+                              obj.numericId(), obj.name().c_str(),
+                              obj.min(), obj.max(), obj.val(), obj.reportValue() );
+    }
+    return str << str::form( "{%u|%s}[-,-](%lld)",
+                            obj.numericId(), obj.name().c_str(),
+                            obj.val() );
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  ProgressData makeProgressData( const InputStream & input_r )
+  {
+    ProgressData ret;
+    ret.name( input_r.name() );
+    if ( input_r.size() > 0 )
+      ret.range( input_r.size() );
+    return ret;
+  }
+
+  CombinedProgressData::CombinedProgressData( ProgressData &pd,
+                                              ProgressData::value_type weight )
+    : _weight(weight),
+      _last_value(0),
+      _pd(pd)
+  {
+
+  }
+
+  bool CombinedProgressData::operator()( const ProgressData &progress )
+  {
+    if ( progress.reportAlive() || ( _weight == 0 ) )
+      return _pd.tick();
+
+    // factor [0,1] of increase in subtask ( ie: before 0,2 now 0.5 )
+    float increment = ((float)(progress.val() - _last_value))/(progress.max() - progress.min());
+    // how much the subtask affects the parent task ie: 0,1
+    float parent_factor = (float)(_weight)/(_pd.max() - _pd.min());
+    // real increment of the parent task
+    float real_increment = parent_factor*increment;
+    _last_value = progress.val();
+    return _pd.incr( (int)( (_pd.max()-_pd.min()) * real_increment) );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ProgressData.h b/zypp/ProgressData.h
new file mode 100644 (file)
index 0000000..818258d
--- /dev/null
@@ -0,0 +1,422 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ProgressData.h
+ *
+*/
+#ifndef ZYPP_PROGRESSDATA_H
+#define ZYPP_PROGRESSDATA_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/ProvideNumericId.h"
+
+#include "zypp/Date.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ProgressData
+  //
+  /** Maintain <tt>[min,max]</tt> and counter <tt>(value)</tt> for progress counting.
+   *
+   * This class should provide everything the producer of progress data
+   * needs. As a convention, a zero sizes range indicates that you are just
+   * able to send <em>'still alive'</em> triggers.
+   *
+   * The counter should be updated in reasonable intervals. Don't mind wheter
+   * the counter value actually increased or not. ProgressData will recognize
+   * your triggers and knows when to actually send notification to a consumer.
+   *
+   * Each ProgressData object provides a unique numeric id and you may assign
+   * it a name.
+   *
+   * \code
+   *      bool exampleReceiver( ProgressData::value_type v )
+   *      {
+   *        DBG << "got ->" << v << endl;
+   *        return( v <= 100 ); // Abort if ( v > 100 )
+   *      }
+   *
+   *      class Example
+   *      {
+   *        public:
+   *
+   *          Example( const ProgressData::ReceiverFnc & fnc_r = ProgressData::ReceiverFnc() )
+   *          : _fnc( fnc_r )
+   *          {}
+   *
+   *          void SendTo( const ProgressData::ReceiverFnc & fnc_r )
+   *          { _fnc = fnc_r; }
+   *
+   *        public:
+   *
+   *          void action()
+   *          {
+   *            ProgressData tics( 10 );    // Expect range 0 -> 10
+   *            tics.name( "test ticks" );  // Some arbitrary name
+   *            tics.sendTo( _fnc );        // Send reports to _fnc
+   *            tics.toMin();               // start sending min (0)
+   *
+   *            for ( int i = 0; i < 10; ++i )
+   *            {
+   *              if ( ! tics.set( i ) )
+   *                return; // user requested abort
+   *            }
+   *
+   *            tics.toMax(); // take care 100% are reported on success
+   *          }
+   *
+   *          void action2()
+   *          {
+   *            ProgressData tics;          // Just send 'still alive' messages
+   *            tics.name( "test ticks" );  // Some arbitrary name
+   *            tics.sendTo( _fnc );        // Send reports to _fnc
+   *            tics.toMin();               // start sending min (0)
+   *
+   *            for ( int i = 0; i < 10; ++i )
+   *            {
+   *              if ( ! tics.set( i ) )
+   *                return; // user requested abort
+   *            }
+   *
+   *            tics.toMax(); //
+   *          }
+   *
+   *        private:
+   *          ProgressData::ReceiverFnc _fnc;
+   *      };
+   * \endcode
+   * \code
+   *   Example t( exampleReceiver );
+   *   DBG << "Reporting %:" << endl;
+   *   t.action();
+   *   DBG << "Reporting 'still alive':" << endl;
+   *   t.action2();
+   * \endcode
+   * \code
+   * Reporting %:
+   * got ->0
+   * got ->10
+   * got ->20
+   * got ->30
+   * got ->40
+   * got ->50
+   * got ->60
+   * got ->70
+   * got ->80
+   * got ->90
+   * got ->100
+   * got ->100
+   * Reporting 'still alive':
+   * got ->0
+   * got ->9
+   * \endcode
+   *
+   * The different ammount of triggers is due to different rules for sending
+   * percent or 'still alive' messages.
+   *
+   * \todo Complete the progess sending.
+   * \todo Tell recipient whether percentage or keepalive is sent,
+   * the id and name might be helpfull, and maybe tell whether task
+   * is abortable or not; i.e extend the ReceiverFnc signature.
+   */
+  class ProgressData : public base::ProvideNumericId<ProgressData,unsigned>
+  {
+    public:
+      typedef long long value_type;
+      /** Most simple version of progress reporting
+       * The percentage in most cases. Sometimes just keepalive.
+       * \p sender ProgressData object who sends the progress info
+       * \p
+       */
+      typedef function<bool( const ProgressData & )> ReceiverFnc;
+
+    private:
+      enum State { INIT, RUN, END };
+
+      class Data
+      {
+       public:
+         Data( value_type min_r, value_type max_r, value_type val_r )
+         : _state( INIT ), _min( min_r ), _max( max_r ), _val( val_r )
+         , _last_val( 0 ), _last_send( 0 )
+         {}
+
+       public:
+         State       _state;
+         std::string _name;
+         value_type  _min;
+         value_type  _max;
+         value_type  _val;
+
+         ReceiverFnc _receiver;
+         value_type  _last_val;
+         Date        _last_send;
+
+       private:
+         /** clone for RWCOW_pointer */
+         friend Data * rwcowClone<Data>( const Data * rhs );
+         Data * clone() const { return new Data( *this ); }
+      };
+
+    public:
+      /** Ctor no range <tt>[0,0](0)</tt>. */
+      ProgressData()
+      : _d( new Data( 0, 0, 0 ) )
+      {}
+
+      /** Ctor <tt>[0,max](0)</tt>. */
+      ProgressData( value_type max_r )
+      : _d( new Data( 0, max_r, 0 ) )
+      {}
+
+      /** Ctor <tt>[min,max](min)</tt>. */
+      ProgressData( value_type min_r, value_type max_r )
+      : _d( new Data( min_r, max_r, min_r ) )
+      {}
+
+      /** Ctor <tt>[min,max](val)</tt>. */
+      ProgressData( value_type min_r, value_type max_r, value_type val_r )
+      : _d( new Data( min_r, max_r, val_r ) )
+      {}
+
+      ~ProgressData()
+      {
+       if ( _d->_state == RUN )
+       {
+         _d->_state = END;
+         report();
+       }
+      }
+
+    public:
+      /** Set new \c min value. */
+      void min( value_type min_r )
+      { _d->_min = min_r; }
+
+      /** Set new \c max value. */
+      void max( value_type max_r )
+      { _d->_max = max_r; }
+
+      /** Set no range <tt>[0,0]</tt>. */
+      void noRange()
+      { range( 0, 0 ); }
+
+      /** Set new <tt>[0,max]</tt>. */
+      void range( value_type max_r )
+      { range( 0, max_r ); }
+
+      /** Set new <tt>[min,max]</tt>. */
+      void range( value_type min_r, value_type max_r )
+      { min( min_r ); max( max_r ); }
+
+    public:
+      /** Set counter name. */
+      void name( const std::string & name_r )
+      { _d->_name = name_r; }
+
+      /** Set ReceiverFnc. */
+      void sendTo( const ReceiverFnc & fnc_r )
+      { _d->_receiver = fnc_r; }
+
+      /** Set no ReceiverFnc. */
+      void noSend()
+      { _d->_receiver = ReceiverFnc(); }
+
+    public:
+      /** \name Progress reporting.
+       *
+       * These methods may actually cause a progress report to be sent.
+       *
+       * All methods return \c bool, because a progress receiver may
+       * return \c false to indicate the desire to abort the pending
+       * action. The incident is logged, but it's finaly up to the caller
+       * to honor this.
+       */
+      //@{
+
+      /** Set new counter \c value. */
+      bool set( value_type val_r )
+      {
+       _d->_val = val_r;
+       return report();
+      }
+
+      /** Increment counter \c value (default by 1). */
+      bool incr( value_type val_r = 1 )
+      { return set( val() + val_r ); }
+
+      /** Decrement counter \c value (default by 1). */
+      bool decr( value_type val_r = 1 )
+      { return set( val() - val_r ); }
+
+      /** Set counter value to current \c min value. */
+      bool toMin()
+      { return set( min() ); }
+
+      /** Set counter value to current \c max value (unless no range). */
+      bool toMax()
+      { return set( hasRange() ? max() : val() ); }
+
+      /** Leave counter value unchanged (still alive). */
+      bool tick()
+      { return set( val() ); }
+
+      //@}
+
+    public:
+      /** \name Progress receiving.
+       */
+      //@{
+      /** @return Current \c min value. */
+      value_type min() const
+      { return _d->_min; }
+
+      /** @return Current \c max value. */
+      value_type max() const
+      { return _d->_max; }
+
+      /** @return Current counter \c value. */
+      value_type val() const
+      { return _d->_val; }
+
+      /** @return Wheter <tt>[min,max]</tt> defines a nonempty range. */
+      bool hasRange() const
+      { return min() != max(); }
+
+      /** @return Wheter \ref reportValue will return a percent value.
+       * Same as \ref hasRange.
+       *  \see \ref reportAlive
+       */
+      bool reportPercent() const
+      { return hasRange(); }
+
+      /** @return Wheter \ref reportValue always returns -1, because we
+       * trigger 'still alive' messages. I.e. \ref hasrange is \c false.
+       * \see \ref reportPercent
+      */
+      bool reportAlive() const
+      { return ! hasRange(); }
+
+      /** @return Either a a percent value or -1.
+       * \see \ref reportPercent and \ref reportAlive.
+      */
+      value_type reportValue() const
+      {        return hasRange() ? val() * 100 / ( max() - min() ) : -1; }
+
+      /** @return The counters name. */
+      const std::string & name() const
+      { return _d->_name; }
+
+      /** @return The ReceiverFnc. */
+      const ReceiverFnc & receiver() const
+      { return _d->_receiver; }
+
+      /** @return Retrun \c true if this the final report sent by the
+       *  ProgressData dtor.
+      */
+      bool finalReport() const
+      { return( _d->_state == END ); }
+
+      //@}
+
+    private:
+      /** Send report if necessary. */
+      bool report();
+
+      /** Pointer to data. */
+      RWCOW_pointer<Data> _d;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ProgressData Stream output */
+  std::ostream & operator<<( std::ostream & str, const ProgressData & obj );
+
+  ///////////////////////////////////////////////////////////////////
+
+  class InputStream;
+  /** \relates ProgressData Setup from \ref InputStream. */
+  ProgressData makeProgressData( const InputStream & input_r );
+
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * \short Progress callback from another progress
+   *
+   * This class allows you to pass a progress callback to a
+   * subtask based on a current progress data, plus a weight
+   * value. Every progress reported by the subtask via
+   * this callback will be forwarded to the main progress
+   * data, with the corresponding weight.
+   *
+   * Example:
+   *
+   * \code
+   *
+   * // receiver for main task
+   * void task_receiver( ProgressData &progress );
+   *
+   * // subtask prototypes
+   * void do_subtask_one( ProgressData::ReceiverFnc &fnc );
+   * void do_subtask_two( ProgressData::ReceiverFnc &fnc );
+   *
+   * // main task
+   * ProgressData progress;
+   * //progress for subtask 1
+   * // which is 80%
+   * CombinedProgressData sub1(pd, 80);
+   * // the second is only 20%
+   * CombinedProgressData sub2(pd, 20);
+   * do_subtask_one( sub1 );
+   * do_subtask_two( sub2 );
+   *
+   * \endcode
+   */
+  class CombinedProgressData
+  {
+  public:
+    /**
+     * \short Ctor
+     *
+     * Creates a \ref ProgressData::ReceiverFnc
+     * from a \ref ProgressData object
+     *
+     * \param pd \ref ProgressData object
+     * \param weight Weight of the subtask
+     * relative to the main task range.
+     *
+     * If weight is 0, or \param pd only reports
+     * keepalives. then only ticks are sent.
+     *
+     */
+    CombinedProgressData( ProgressData &pd,
+                          ProgressData::value_type weight = 0 );
+
+    /**
+     * Implements the \ref ProgressData::ReceiverFnc
+     * callback interface
+     */
+    bool operator()( const ProgressData &progress );
+
+  private:
+    ProgressData::value_type _weight;
+    ProgressData::value_type _last_value;
+    ProgressData &_pd;
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PROGRESSDATA_H
diff --git a/zypp/ProvideFilePolicy.cc b/zypp/ProvideFilePolicy.cc
new file mode 100644 (file)
index 0000000..9eff514
--- /dev/null
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/ProvideFilePolicy.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ProvideFilePolicy
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    bool yes() { return true; }
+    bool no()  { return false; }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ProvideFilePolicy & ProvideFilePolicy::failOnChecksumErrorCB( bool yesno_r )
+  {
+    _failOnChecksumErrorCB = (yesno_r ? &yes : &no);
+    return *this;
+  }
+
+  bool ProvideFilePolicy::progress( int value ) const
+  {
+    if ( _progressCB )
+      return _progressCB( value );
+    return true;
+  }
+
+  bool ProvideFilePolicy::failOnChecksumError() const
+  {
+    if ( _failOnChecksumErrorCB )
+      return _failOnChecksumErrorCB();
+    return true;
+  }
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ProvideFilePolicy.h b/zypp/ProvideFilePolicy.h
new file mode 100644 (file)
index 0000000..d57d048
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_PROVIDEFILEPOLICY_H
+#define ZYPP_PROVIDEFILEPOLICY_H
+
+#include <iosfwd>
+
+#include "zypp/base/Function.h"
+#include "zypp/base/Functional.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+// CLASS NAME : ProvideFilePolicy
+  
+  /** Policy for \ref provideFile.
+    * Provides callback hooks for e.g progress reporting or
+    * behaviour on checksum failure. Provides default
+    * implementations if no callback is set.
+    */
+  class ProvideFilePolicy
+  {
+  public:
+    /** Progress callback signature. */
+    typedef function<bool ( int )> ProgressCB;
+
+    /** Set callback. */
+    ProvideFilePolicy & progressCB( ProgressCB progressCB_r )
+    { _progressCB = progressCB_r; return *this; }
+
+    /** Evaluate callback. */
+    bool progress( int value ) const;
+
+  public:
+    /** FailOnChecksumError callback signature. */
+    typedef function<bool ()> FailOnChecksumErrorCB;
+
+    /** Set callback. */
+    ProvideFilePolicy & failOnChecksumErrorCB( FailOnChecksumErrorCB failOnChecksumErrorCB_r )
+    { _failOnChecksumErrorCB = failOnChecksumErrorCB_r; return *this; }
+
+    /** Set callback convenience.
+      * Let callback return \c yesno_r.
+    */
+    ProvideFilePolicy & failOnChecksumErrorCB( bool yesno_r );
+
+    /** Evaluate callback. */
+    bool failOnChecksumError() const;
+
+  private:
+    FailOnChecksumErrorCB _failOnChecksumErrorCB;
+    ProgressCB            _progressCB;
+  };
+
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PROVIDEFILEPOLICY_H
diff --git a/zypp/PublicKey.cc b/zypp/PublicKey.cc
new file mode 100644 (file)
index 0000000..aea5bad
--- /dev/null
@@ -0,0 +1,361 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PublicKey.cc
+ *
+*/
+#include <climits>
+
+#include <iostream>
+#include <vector>
+
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/PublicKey.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/TmpPath.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/Logger.h"
+#include "zypp/Date.h"
+#include "zypp/TmpPath.h"
+
+#include <ctime>
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PublicKey::Impl
+  //
+  /** PublicKey implementation. */
+  struct PublicKey::Impl
+  {
+    Impl()
+    {}
+
+    Impl( const Pathname & keyfile )
+    {
+      PathInfo info( keyfile );
+      MIL << "Takeing pubkey from " << keyfile << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyfile, "sha1") << endl;
+
+      if ( !info.isExist() )
+        ZYPP_THROW(Exception("Can't read public key from " + keyfile.asString() + ", file not found"));
+
+      if ( copy( keyfile, _data_file.path() ) != 0 )
+        ZYPP_THROW(Exception("Can't copy public key data from " + keyfile.asString() + " to " +  _data_file.path().asString() ));
+
+      readFromFile();
+    }
+
+    Impl( const filesystem::TmpFile & sharedfile )
+      : _data_file( sharedfile )
+    { readFromFile(); }
+
+    public:
+      /** Offer default Impl. */
+      static shared_ptr<Impl> nullimpl()
+      {
+        static shared_ptr<Impl> _nullimpl( new Impl );
+        return _nullimpl;
+      }
+
+      std::string asString() const
+      {
+       return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
+                         id().c_str(), str::hexstring(created(),8).substr(2).c_str(),
+                         name().c_str(),
+                         fingerprint().c_str(),
+                         daysToLive() );
+      }
+
+      std::string id() const
+      { return _id; }
+
+      std::string name() const
+      { return _name; }
+
+      std::string fingerprint() const
+      { return _fingerprint; }
+
+      std::string gpgPubkeyVersion() const
+      { return _id.empty() ? _id : str::toLower( _id.substr(8,8) ); }
+
+      std::string gpgPubkeyRelease() const
+      { return _created ? str::hexstring( _created ).substr(2) : std::string(); }
+
+      Date created() const
+      { return _created; }
+
+      Date expires() const
+      { return _expires; }
+
+      std::string expiresAsString() const
+      {
+       if ( !_expires )
+       { // translators: an annotation to a gpg keys expiry date
+         return _("(does not expire)");
+       }
+       std::string ret( _expires.asString() );
+       int ttl( daysToLive() );
+       if ( ttl <= 90 )
+       {
+         ret += " ";
+         if ( ttl < 0 )
+         { // translators: an annotation to a gpg keys expiry date
+           ret += _("(EXPIRED)");
+         }
+         else if ( ttl == 0 )
+         { // translators: an annotation to a gpg keys expiry date
+           ret += _("(expires within 24h)");
+         }
+         else
+         { // translators: an annotation to a gpg keys expiry date
+           ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
+         }
+       }
+       return ret;
+      }
+
+      Pathname path() const
+      { return _data_file.path(); }
+
+      bool expired() const
+      {
+       Date exp( expires() );
+       return( exp && exp < Date::now() );
+      }
+
+      int daysToLive() const
+      {
+       Date exp( expires() );
+       if ( ! expires() )
+         return INT_MAX;
+       exp -= Date::now();
+       return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
+      }
+
+    protected:
+
+      void readFromFile()
+      {
+        PathInfo info( _data_file.path() );
+        MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
+
+        static filesystem::TmpDir dir;
+        const char* argv[] =
+        {
+          "gpg",
+          "-v",
+          "--no-default-keyring",
+          "--fixed-list-mode",
+          "--with-fingerprint",
+          "--with-colons",
+          "--homedir",
+          dir.path().asString().c_str(),
+          "--quiet",
+          "--no-tty",
+          "--no-greeting",
+          "--batch",
+          "--status-fd",
+          "1",
+          _data_file.path().asString().c_str(),
+          NULL
+        };
+
+        ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+        std::string line;
+        bool sawpub = false;
+        bool sawsig = false;
+
+        // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
+        // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
+        // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
+        // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
+        // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
+        // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
+        // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
+
+        for ( line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
+        {
+          // trim trailing NL.
+          if ( line.empty() )
+            continue;
+          if ( line[line.size()-1] == '\n' )
+            line.erase( line.size()-1 );
+
+          // split at ':'
+          std::vector<std::string> words;
+          str::splitFields( line, std::back_inserter(words), ":" );
+          if( words.empty() )
+            continue;
+
+          if ( words[0] == "pub" )
+          {
+            if ( sawpub )
+              continue;
+            sawpub = true;
+            // take default from pub
+            _id      = words[4];
+            _name    = words[9];
+            _created = Date(str::strtonum<Date::ValueType>(words[5]));
+            _expires = Date(str::strtonum<Date::ValueType>(words[6]));
+
+          }
+          else if ( words[0] == "sig" )
+          {
+            if ( sawsig || words[words.size()-2] != "13x"  )
+              continue;
+            sawsig = true;
+            // update creation and expire dates from 1st signature type "13x"
+            if ( ! words[5].empty() )
+              _created = Date(str::strtonum<Date::ValueType>(words[5]));
+            if ( ! words[6].empty() )
+              _expires = Date(str::strtonum<Date::ValueType>(words[6]));
+          }
+          else if ( words[0] == "fpr" )
+          {
+            _fingerprint = words[9];
+          }
+          else if ( words[0] == "uid" )
+          {
+            if ( ! words[9].empty() )
+              _name = words[9];
+          }
+        }
+        prog.close();
+
+        if ( _id.size() == 0 )
+          ZYPP_THROW( BadKeyException( "File " + _data_file.path().asString() + " doesn't contain public key data" , _data_file.path() ) );
+
+        //replace all escaped semicolon with real ':'
+        str::replaceAll( _name, "\\x3a", ":" );
+
+        MIL << "Read pubkey from " << info.path() << ": " << asString() << endl;
+      }
+
+    private:
+      filesystem::TmpFile _data_file;
+
+      std::string _id;
+      std::string _name;
+      std::string _fingerprint;
+      Date        _created;
+      Date        _expires;
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const
+      { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PublicKey::PublicKey
+  //   METHOD TYPE : Ctor
+  //
+  PublicKey::PublicKey()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  PublicKey::PublicKey( const Pathname & file )
+  : _pimpl( new Impl(file) )
+  {}
+
+  PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
+  : _pimpl( new Impl(sharedfile) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : PublicKey::~PublicKey
+  //   METHOD TYPE : Dtor
+  //
+  PublicKey::~PublicKey()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to implementation:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  std::string PublicKey::asString() const
+  { return _pimpl->asString(); }
+
+  std::string PublicKey::id() const
+  { return _pimpl->id(); }
+
+  std::string PublicKey::name() const
+  { return _pimpl->name(); }
+
+  std::string PublicKey::fingerprint() const
+  { return _pimpl->fingerprint(); }
+
+  std::string PublicKey::gpgPubkeyVersion() const
+  { return _pimpl->gpgPubkeyVersion(); }
+
+  std::string PublicKey::gpgPubkeyRelease() const
+  { return _pimpl->gpgPubkeyRelease(); }
+
+  Date PublicKey::created() const
+  { return _pimpl->created(); }
+
+  Date PublicKey::expires() const
+  { return _pimpl->expires(); }
+
+  std::string PublicKey::expiresAsString() const
+  { return _pimpl->expiresAsString(); }
+
+  bool PublicKey::expired() const
+  { return _pimpl->expired(); }
+
+  int PublicKey::daysToLive() const
+  { return _pimpl->daysToLive(); }
+
+  Pathname PublicKey::path() const
+  { return _pimpl->path(); }
+
+  bool PublicKey::operator==( PublicKey b ) const
+  {
+    return (   b.id() == id()
+            && b.fingerprint() == fingerprint()
+            && b.created() == created() );
+  }
+
+  bool PublicKey::operator==( std::string sid ) const
+  {
+    return sid == id();
+  }
+
+  std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
+  {
+    str << "[" << obj.name() << "]" << endl;
+    str << "  fpr " << obj.fingerprint() << endl;
+    str << "   id " << obj.id() << endl;
+    str << "  cre " << obj.created() << endl;
+    str << "  exp " << obj.expiresAsString() << endl;
+    str << "  ttl " << obj.daysToLive() << endl;
+    str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
+    str << "]";
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/PublicKey.h b/zypp/PublicKey.h
new file mode 100644 (file)
index 0000000..480d925
--- /dev/null
@@ -0,0 +1,202 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PublicKey.h
+ *
+*/
+#ifndef ZYPP_PUBLICKEY_H
+#define ZYPP_PUBLICKEY_H
+
+#include <iosfwd>
+#include <map>
+#include <list>
+#include <set>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace filesystem
+  {
+    class TmpFile;
+  }
+
+  /**
+   * Exception thrown when the supplied key is
+   * not a valid gpg key
+   */
+  class BadKeyException : public Exception
+  {
+    public:
+      /** Ctor taking message.
+     * Use \ref ZYPP_THROW to throw exceptions.
+       */
+      BadKeyException()
+      : Exception( "Bad Key Exception" )
+      {}
+
+      Pathname keyFile() const
+      { return _keyfile; }
+
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+       */
+      BadKeyException( const std::string & msg_r, const Pathname &keyfile = Pathname() )
+      : Exception( msg_r ), _keyfile(keyfile)
+      {}
+      /** Dtor. */
+      virtual ~BadKeyException() throw() {};
+    private:
+      Pathname _keyfile;
+  };
+
+
+  // forward declaration of class Date
+  class Date;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : PublicKey
+  //
+  /**
+   * Class that represent a GPG Public Key.
+   *
+   *
+   */
+  class PublicKey
+  {
+    friend std::ostream & operator<<( std::ostream & str, const PublicKey & obj );
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor. */
+    PublicKey();
+
+    /** Ctor taking the key from a file.
+     *
+     * This is quite expensive, as a copy of the file is created and
+     * used. If you can construct PublicKey from a \ref filesystem::TmpFile,
+     * this prevents copying.
+     *
+     * \throws when data does not make a key
+     */
+    explicit
+    PublicKey( const Pathname & file );
+
+    /** Ctor reading the key from a \ref TmpFile.
+     *
+     * PublicKey holds a reference on the TmpFile providing the key.
+     *
+     * \throws when data does not make a key
+     */
+    explicit
+    PublicKey( const filesystem::TmpFile & sharedfile );
+
+    ~PublicKey();
+
+    bool isValid() const
+    { return ( ! id().empty() && ! fingerprint().empty() && !path().empty() ); }
+
+    std::string asString() const;
+    std::string id() const;
+    std::string name() const;
+    std::string fingerprint() const;
+
+    /** Version rpm would assign to this key if imported into the rpm database.
+     * Rpm uses the lowercased trailing 8 byte from \ref id as \c version, and the
+     * creations dates lowercased hexadecimal representation as \c release.
+     * \see \ref gpgPubkeyRelease
+     * \code
+     * [zypp OBS Project <zypp@build.opensuse.org>]
+     *   fpr 47D7CE1DD600935B3B90365733D38EBC7FB7F464
+     *    id 33D38EBC7FB7F464           <-- trailing 8 byte
+     *   cre Thu Mar 13 19:15:40 2008   <-- converted to hex
+     *   exp Sat May 22 20:15:40 2010
+     * ]
+     *
+     * Converting the creation date to its hexadecimal representation:
+     * $ bc <<<"obase=16;$(date -d 'Thu Mar 13 19:15:40 2008' +%s)"
+     * 47D96F4C
+     *
+     * Rpms name for this key: gpg-pubkey-7fb7f464-47d96f4c
+     * \endcode
+     */
+    std::string gpgPubkeyVersion() const;
+
+    /** Release rpm would assign to this key if imported into the rpm database.
+     * This is the creations dates hexadecimal representation as \c release lowercased.
+     * \see \ref gpgPubkeyVersion
+     */
+    std::string gpgPubkeyRelease() const;
+
+    /**
+     * Date when the key was created.
+     */
+    Date created() const;
+
+    /**
+     * Date when the key expires.
+     * If the key never expires the date is Date() (i.e. 0 seconds since the epoch (1.1.1970))
+     */
+    Date expires() const;
+
+    /**
+     * Expiry info in a human readable form.
+     * The exipry daye plus an annotation if the key has expired, or will
+     * expire within 90 days.
+     * \code
+     * (does not expire)
+     * Tue May 11 13:37:33 CEST 2010
+     * Tue May 11 13:37:33 CEST 2010 (expires in 90 days)
+     * Tue May 11 13:37:33 CEST 2010 (expires in 1 day)
+     * Tue May 11 13:37:33 CEST 2010 (expires within 24h)
+     * Tue May 11 13:37:33 CEST 2010 (EXPIRED)
+     * \endcode
+     */
+    std::string expiresAsString() const;
+
+    /** Whether the key has expired. */
+    bool expired() const;
+
+    /** Number of days (24h) until the key expires (or since it exired).
+     * A value of \c 0 means the key will expire within the next 24h.
+     * Negative values indicate the key has expired less than \c N days ago.
+     * For keys without expiration date \c INT_MAX is returned.
+     */
+    int daysToLive() const;
+
+    Pathname path() const;
+
+    bool operator==( PublicKey b ) const;
+    bool operator==( std::string sid ) const;
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PublicKey Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PublicKey & obj )
+  { return str << obj.asString(); }
+
+  /** \relates PublicKey Detailed stream output */
+  std::ostream & dumpOn( std::ostream & str, const PublicKey & obj );
+
+ /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PUBLICKEY_H
diff --git a/zypp/Range.cc b/zypp/Range.cc
new file mode 100644 (file)
index 0000000..34d11bc
--- /dev/null
@@ -0,0 +1,114 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Range.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/Range.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace range_detail
+  {
+    /** Compute Range overlaps.
+     * Takes the \a lhs and \a rhs operator and the result
+     * of comparing \a lhs and \a rhs (<tt>-1,0,1</tt>).
+     *
+    */
+    bool overlaps( Rel lhs, Rel rhs, int cmp )
+    {
+      if ( lhs == Rel::NONE || rhs == Rel::NONE )
+        return false;
+      if ( lhs == Rel::ANY || rhs == Rel::ANY )
+        return true;
+
+      if ( lhs == Rel::NE )
+      {
+         if ( cmp < 0 )
+         {
+             // lhs < rhs
+             return( rhs == Rel::GE 
+                     || rhs == Rel::EQ );
+         } else if ( cmp > 0)
+         {
+             // lhs > rhs
+             return( rhs == Rel::LT
+                     || rhs == Rel::EQ );            
+         } else 
+         {
+             //lhs == rhs
+             return ( rhs == Rel::GT
+                      || rhs == Rel::LT );
+         }
+      }
+      
+      if ( rhs == Rel::NE )
+      {
+         if ( cmp < 0 )
+         {
+             // lhs < rhs
+             return(  lhs == Rel::LE
+                      || lhs == Rel::EQ );
+         } else if ( cmp > 0)
+         {
+             // lhs > rhs
+             return(  lhs == Rel::GT
+                      || lhs == Rel::EQ );           
+         } else
+         {
+             //lhs == rhs
+             return ( lhs == Rel::GT
+                      || lhs == Rel::LT );
+         }
+      }
+
+      if ( cmp < 0 )
+        {
+          // lhs < rhs: either lhs includes greater values or rhs includes lower.
+          return(    lhs == Rel::GT
+                  || lhs == Rel::GE
+                  || rhs == Rel::LT
+                  || rhs == Rel::LE );
+        }
+
+      if ( cmp > 0 )
+        {
+          // lhs > rhs: either lhs includes lower values or rhs includes greater.
+          return(    lhs == Rel::LT
+                  || lhs == Rel::LE
+                  || rhs == Rel::GT
+                  || rhs == Rel::GE );
+        }
+
+      // lhs == rhs: either both ranges include Rel::EQ, or both head
+      // into the same direction.
+      if (    ( lhs == Rel::LE || lhs == Rel::EQ || lhs == Rel::GE )
+           && ( rhs == Rel::LE || rhs == Rel::EQ || rhs == Rel::GE ) )
+        return true;
+      if (    ( lhs == Rel::LT && ( rhs == Rel::LT || rhs == Rel::LE ) )
+           || ( lhs == Rel::GT && ( rhs == Rel::GT || rhs == Rel::GE ) )
+           || ( rhs == Rel::LT && ( lhs == Rel::LT || lhs == Rel::LE ) )
+           || ( rhs == Rel::GT && ( lhs == Rel::GT || lhs == Rel::GE ) ) )
+        return true;
+      // else
+      return false;
+
+    }
+  }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Range.h b/zypp/Range.h
new file mode 100644 (file)
index 0000000..41878e3
--- /dev/null
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Range.h
+ *
+*/
+#ifndef ZYPP_RANGE_H
+#define ZYPP_RANGE_H
+
+#include "zypp/RelCompare.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace range_detail
+  {
+    bool overlaps( Rel lhs, Rel rhs, int cmp );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Range
+  //
+  /**
+   *
+  */
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct Range
+    {
+      /** */
+      Rel op;
+      /** */
+      _Tp value;
+
+      /** Default ctor: \ref Rel::ANY. */
+      Range()
+      : op( Rel::ANY )
+      {}
+
+      /** Ctor taking \a _Tp (\ref Rel::EQ). */
+      Range( const _Tp & value_r )
+      : op( Rel::EQ )
+      , value( value_r )
+      {}
+
+      /** Ctor taking \ref Rel and \a _Tp. */
+      Range( Rel op_r, const _Tp & value_r )
+      : op( op_r )
+      , value( value_r )
+      {}
+
+      /** Return whether two Ranges overlap. */
+      bool overlaps( const Range & rhs ) const
+      {
+        return range_detail::overlaps( op, rhs.op,
+                                       _Compare()( value, rhs.value ) );
+      }
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  template<class _Tp, class _Compare>
+    inline bool overlaps( const Range<_Tp,_Compare> & lhs,
+                          const Range<_Tp,_Compare> & rhs )
+    { return lhs.overlaps( rhs ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  template<class _Tp, class _Compare>
+    inline bool operator==( const Range<_Tp,_Compare> & lhs,
+                            const Range<_Tp,_Compare> & rhs )
+    {
+      return( lhs.op == rhs.op
+              && (    lhs.op == Rel::ANY
+                   || lhs.op == Rel::NONE
+                   || relCompare( Rel::EQ, lhs.value, rhs.value,
+                                  _Compare() )
+                 )
+            );
+    }
+
+  template<class _Tp, class _Compare>
+    inline bool operator!=( const Range<_Tp,_Compare> & lhs,
+                            const Range<_Tp,_Compare> & rhs )
+    { return ! ( lhs == rhs ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RANGE_H
diff --git a/zypp/Rel.cc b/zypp/Rel.cc
new file mode 100644 (file)
index 0000000..1839c63
--- /dev/null
@@ -0,0 +1,125 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Rel.cc
+ *
+*/
+#include <iostream>
+#include <map>
+
+#include "zypp/base/Exception.h"
+
+#include "zypp/Rel.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace
+  {
+    std::map<std::string,Rel::for_use_in_switch> _table;
+
+    std::map<std::string,Rel::for_use_in_switch>::const_iterator findStr( const std::string & strval_r )
+    {
+      if ( _table.empty() )
+      {
+        // initialize it
+        _table["EQ"]   = _table["eq"]  = _table["=="]    = _table["="] = Rel::EQ_e;
+        _table["NE"]   = _table["ne"]  = _table["!="]                  = Rel::NE_e;
+        _table["LT"]   = _table["lt"]  = _table["<"]                   = Rel::LT_e;
+        _table["LE"]   = _table["le"]  = _table["lte"]  = _table["<="] = Rel::LE_e;
+        _table["GT"]   = _table["gt"]  = _table[">"]                   = Rel::GT_e;
+        _table["GE"]   = _table["ge"]  = _table["gte"]  = _table[">="] = Rel::GE_e;
+        _table["ANY"]  = _table["any"] = _table["(any)"] = _table[""]  = Rel::ANY_e;
+        _table["NONE"] = _table["none"]                                = Rel::NONE_e;
+      }
+
+      return _table.find( strval_r );
+    }
+
+    Rel::for_use_in_switch parse( const std::string & strval_r )
+    {
+      std::map<std::string,Rel::for_use_in_switch>::const_iterator it = findStr( strval_r );
+      if ( it == _table.end() )
+      {
+        ZYPP_THROW( Exception("Rel parse: illegal string value '"+strval_r+"'") );
+      }
+      return it->second;
+    }
+
+    Rel::for_use_in_switch parse( const std::string & strval_r, const Rel & default_r )
+    {
+      std::map<std::string,Rel::for_use_in_switch>::const_iterator it = findStr( strval_r );
+      if ( it == _table.end() )
+      {
+        return default_r.inSwitch();
+      }
+      return it->second;
+    }
+  }
+  ///////////////////////////////////////////////////////////////////
+
+  const Rel Rel::EQ( Rel::EQ_e );
+  const Rel Rel::NE( Rel::NE_e );
+  const Rel Rel::LT( Rel::LT_e );
+  const Rel Rel::LE( Rel::LE_e );
+  const Rel Rel::GT( Rel::GT_e );
+  const Rel Rel::GE( Rel::GE_e );
+  const Rel Rel::ANY( Rel::ANY_e );
+  const Rel Rel::NONE( Rel::NONE_e );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Rel::Rel
+  //   METHOD TYPE : Constructor
+  //
+  Rel::Rel( const std::string & strval_r )
+  : _op( parse( strval_r ) )
+  {}
+
+  Rel::Rel( const std::string & strval_r, const Rel & default_r )
+  : _op( parse( strval_r, default_r ) )
+  {}
+
+  bool Rel::parseFrom( const std::string & strval_r )
+  {
+    std::map<std::string,Rel::for_use_in_switch>::const_iterator it = findStr( strval_r );
+    if ( it == _table.end() )
+    {
+      return false;
+    }
+    _op = it->second;
+    return true;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Rel::asString
+  //   METHOD TYPE : const std::string &
+  //
+  const std::string & Rel::asString() const
+  {
+    static std::map<for_use_in_switch,std::string> _table;
+    if ( _table.empty() )
+      {
+        // initialize it
+        _table[EQ_e]   = "==";
+        _table[NE_e]   = "!=";
+        _table[LT_e]   = "<";
+        _table[LE_e]   = "<=";
+        _table[GT_e]   = ">";
+        _table[GE_e]   = ">=";
+        _table[ANY_e]  = "ANY";
+        _table[NONE_e] = "NONE";
+      }
+    return _table[_op];
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Rel.h b/zypp/Rel.h
new file mode 100644 (file)
index 0000000..1162abe
--- /dev/null
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Rel.h
+ *
+*/
+#ifndef ZYPP_REL_H
+#define ZYPP_REL_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Rel
+  //
+  /** Relational operators.
+   * Yes, it could as well be simply an \c enum.<BR>
+   * Yes, you can use the relational operators as if it was an \c enum.<BR>
+   * Except for use in a \c switch statement; see \ref inSwitch for this.
+   *
+   * But we want to construct them from a string representation, as well as
+   * providing one. And this way they are wrapped into a namespace, which is
+   * a good idea anyway.
+   *
+   * \ref ANY and \ref NONE are somewhat special. \ref ANY is the
+   * operator created by the default ctor, and it should always resolve
+   * to \c true. This may be handy in queries when you're looking for a
+   * Resolvable in \c ANY Edition if no operator was specified.
+   * While \ref NONE should always resolve to \c false.
+   *
+   * \ingroup g_EnumerationClass
+  */
+  struct Rel
+  {
+    /** \name Relational operators
+     * These are the \em real relational operator contants to
+     * use. Don't mind that it's not an enum. See also: \ref zypp::Rel::inSwitch
+    */
+    //@{
+    static const Rel EQ;
+    static const Rel NE;
+    static const Rel LT;
+    static const Rel LE;
+    static const Rel GT;
+    static const Rel GE;
+    static const Rel ANY;
+    static const Rel NONE;
+    //@}
+
+    /** Enumarators provided \b only for use \ref inSwitch statement.
+     * \see inSwitch
+     * \note Enumarator values also correspond to the values libsatsolver
+     * uses to encode these relations.
+    */
+    enum for_use_in_switch {
+      NONE_e = 0U,
+      GT_e   = 1U,
+      EQ_e   = 2U,
+      LT_e   = 4U,
+      GE_e   = GT_e|EQ_e,
+      LE_e   = LT_e|EQ_e,
+      NE_e   = GT_e|LT_e,
+      ANY_e  = GT_e|EQ_e|LT_e,
+    };
+
+    /** DefaultCtor ANY. */
+    Rel()
+    : _op( ANY_e )
+    {}
+
+    /** Ctor from string.
+     * Legal values for \a strval_r are: "==", "!=", "<", "<=", ">", ">=",<BR>
+     * as well as "EQ", "NE", "LT", "LE", "GT", "GE", "ANY", "NONE"<BR>
+     * and "" (empty string resolves to ANY).
+     *
+     * Lower case names are accepted as well.
+     *
+     * \throw PARSE if \a strval_r is not legal.
+     * \todo refine exceptions and check throw.
+     */
+    explicit
+    Rel( const std::string & strval_r );
+
+    /** Ctor from string (non-throwing).
+     * Illegal string values resolve to \c default_r
+     */
+    Rel( const std::string & strval_r, const Rel & default_r );
+
+    /** Assign from string IFF it contains a legal value.
+     * \return Whether \a strval_r contained a legal value.
+    */
+    bool parseFrom( const std::string & strval_r );
+
+    /** Ctor from bits. */
+    explicit
+    Rel( unsigned bits_r )
+    : _op( for_use_in_switch(bits_r & ANY_e) )
+    {}
+
+    /** Test whether \a bits_r is a valid \ref Rel (no extra bits set). */
+    static bool isRel( unsigned bits_r )
+    { return (bits_r & ANY_e) == bits_r; }
+
+    /** String representation of relational operator.
+     * \return "==", "!=", "<", "<=", ">", ">=", "ANY" or "NONE"
+    */
+    const std::string & asString() const;
+    /** \overload */
+    const char * c_str() const
+    { return asString().c_str(); }
+
+    /** Enumarator provided for use in \c switch statement.
+     * The sole reason for providing enum \ref for_use_in_switch is,
+     * that we may want to use the relational operators in a \c switch
+     * statement. Tht's the only case where you should have to use the
+     * enumarator.
+     * \code
+     *   Rel op;
+     *   switch ( op.inSwitch() )
+     *     {
+     *     case Rel::EQ_e:
+     *       ...
+     *       break;
+     *     case Rel::NE_e:
+     *       ...
+     *
+     *     // No default! Let compiler warn if case is missing
+     *     }
+     * \endcode
+    */
+    for_use_in_switch inSwitch() const
+    { return _op; }
+
+    /** Enumarator values suitable for libsatsolver. */
+    unsigned bits() const
+    { return _op; }
+
+  private:
+    /** Ctor to initialize the relational operator contants. */
+    Rel( for_use_in_switch op_r )
+    : _op( op_r )
+    {}
+    /** The operator. */
+    for_use_in_switch _op;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Rel Stream output. */
+  inline std::ostream & operator<<( std::ostream & str, const Rel & obj )
+  { return str << obj.asString(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Rel */
+  inline bool operator==( const Rel & lhs, const Rel & rhs )
+  { return lhs.inSwitch() == rhs.inSwitch(); }
+
+  /** \relates Rel */
+  inline bool operator!=( const Rel & lhs, const Rel & rhs )
+  { return ! ( lhs == rhs ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REL_H
diff --git a/zypp/RelCompare.h b/zypp/RelCompare.h
new file mode 100644 (file)
index 0000000..ac3dc1f
--- /dev/null
@@ -0,0 +1,233 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RelCompare.h
+ *
+*/
+#ifndef ZYPP_RELCOMPARE_H
+#define ZYPP_RELCOMPARE_H
+
+#include <functional>
+
+#include "zypp/Rel.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** \defgroup RelCompare Comparison using relational operator zypp::Rel.
+   *
+   * . Take a class like zypp::Edition. Editions are comaprable.
+   * You can compare them lexicographical, or according to their
+   * version and release values, or match them (i.e. taking empty
+   * version or release values as wildcard).
+   *
+   * No matter which way is appropriate within a certain context.
+   * You need functions to compare, and may want to use classes like
+   * zypp::Range, based on the desired comparison.
+   *
+   * All the class has to do, is providing a general comparison
+   * method (preferably static)
+   * \code
+   *     // Compare two elements returning -1, 0, 1
+   *     //  if the elemants compare <,==,>.
+   *     static int compare( const _Tp & lhs, const _Tp & rhs );
+   * \endcode
+   *
+   * <tt>Compare\<_Tp\></tt> provides a functor wrapping \c compare.
+   * In case the general comparison method is named differently, the
+   * class, or you, have to provide an approriate functor.
+   *
+   * <tt>compareByRel</tt> then compares two elements using a certain
+   * operator and general comparison method.
+   * \code
+   * compareByRel( Rel::EQ, lhs, rhs, Edition::compare );
+   * compareByRel( Rel::EQ, lhs, rhs, Edition::match );
+   * compareByRel( Rel::EQ, lhs, rhs ); // defaults to Compare\<Edition\>
+   *                                    // thus Edition::compare
+   * \endcode
+   *
+   * Furthermore a bunch of functors using a certain opertator is
+   * defined. All templated by type and general comparison
+   * method (defaults to Compare\<_Tp\>).
+   * \code
+   * // Editions sets use lexicographical order per default:
+   * std::set<Edition>
+   *
+   * // An Edition set using Edition::compare as order:
+   * std::set<Edition,CompareByLT<Edition> >;
+   *
+   * // Edition::match is not transitive, thus not an appropriate
+   * // order relation for std::set or std::map.
+   * \endcode
+   *
+   * Classes like zypp:Range are templated by  by type and general
+   * comparison method as well. Thus you may use Edition ranges based
+   * on Edition::Compare, as well as ranges based on Edition::Match
+   * (Edition provides these two functors).
+   *
+   * Again: Everything a class has to provide is the general
+   * comparison method. Comparison functors and ranges are then
+   * immediately available.
+  */
+  //@{
+
+  /** General compare functor returning <tt>-1, 0, 1</tt>.
+   * Expects _Tp::compare to be a static comaprison method
+   * returning <tt>-1, 0, 1</tt> if the elements compare
+   * <tt>\<,==,\></tt>.
+  */
+  template<class _Tp>
+    struct Compare : public std::binary_function<_Tp,_Tp,int>
+    {
+      int operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return _Tp::compare( lhs, rhs ); }
+    };
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** Comparison of two elements using relational operator \a op.
+   * Expects \a _Compare to be a binary operator returning
+   * <tt>-1, 0, 1</tt> if the elemants compare <tt>\<,==,\></tt>.
+   * \code
+   *     // Signature of compare function or functor:
+   *     int compare( const _Tp & lhs, const _Tp & rhs );
+   * \endcode
+   * \li If \a op is Rel::ANY, the expression is always \c true.
+   * \li If \a op is Rel::NONE, the expression is always \c false.
+   * \li Otherwise the expression is evaluated using \a compare.
+   *
+   * \ingroup RelCompare
+  */
+  template<class _Tp, class _Compare>
+    inline bool compareByRel( Rel op, const _Tp & lhs, const _Tp & rhs,
+                              _Compare compare )
+    {
+      switch ( op.inSwitch() )
+      {
+      case Rel::EQ_e:
+        return compare( lhs, rhs ) == 0;
+        break;
+      case Rel::NE_e:
+        return compare( lhs, rhs ) != 0;
+        break;
+      case Rel::LT_e:
+        return compare( lhs, rhs ) < 0;
+        break;
+      case Rel::LE_e:
+        return compare( lhs, rhs ) <= 0;
+        break;
+      case Rel::GT_e:
+        return compare( lhs, rhs ) > 0;
+        break;
+      case Rel::GE_e:
+        return compare( lhs, rhs ) >= 0;
+        break;
+      case Rel::ANY_e:
+        return true;
+        break;
+      case Rel::NONE_e:
+        return false;
+        break;
+      }
+      return false;
+    }
+
+  /** \ref compareByRel convenience using Compare<_Tp> as general compare
+   *  functor.
+  */
+  template<class _Tp>
+    inline bool compareByRel( Rel op, const _Tp & lhs, const _Tp & rhs )
+    { return compareByRel( op, lhs, rhs, Compare<_Tp>() ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** Functor to compare two elements by \ref Rel based on
+   * a general \a _Compare functor.
+   *
+   * Expects \a _Compare to be suitable for use in \ref compareByRel.
+   * Defaults to Compare\<_Tp\>.
+  */
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareBy : public std::binary_function<_Tp,_Tp,bool>
+    {
+      CompareBy( Rel op_r )
+      : _op( op_r )
+      {}
+
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( _op, lhs, rhs, _Compare() ); }
+
+      Rel _op;
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByEQ : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::EQ, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByNE : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::NE, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByLT : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::LT, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByLE : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::LE, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByGT : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::GT, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByGE : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::GE, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByANY : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::ANY, lhs, rhs, _Compare() ); }
+    };
+
+  template<class _Tp, class _Compare = Compare<_Tp> >
+    struct CompareByNONE : public std::binary_function<_Tp,_Tp,bool>
+    {
+      bool operator()( const _Tp & lhs, const _Tp & rhs ) const
+      { return compareByRel( Rel::NONE, lhs, rhs, _Compare() ); }
+    };
+
+  ///////////////////////////////////////////////////////////////////
+
+  //@}
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RELCOMPARE_H
diff --git a/zypp/RepoInfo.cc b/zypp/RepoInfo.cc
new file mode 100644 (file)
index 0000000..3cbdfb3
--- /dev/null
@@ -0,0 +1,536 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoInfo.cc
+ *
+*/
+#include <iostream>
+#include <vector>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/parser/xml/XmlEscape.h"
+
+#include "zypp/RepoInfo.h"
+#include "zypp/repo/RepoInfoBaseImpl.h"
+#include "zypp/repo/RepoMirrorList.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/media/MediaAccess.h"
+
+using namespace std;
+using zypp::xml::escape;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoInfo::Impl
+  //
+  /** RepoInfo implementation. */
+  struct RepoInfo::Impl : public repo::RepoInfoBase::Impl
+  {
+    Impl()
+      : repo::RepoInfoBase::Impl()
+      , gpgcheck(indeterminate)
+      ,        keeppackages(indeterminate)
+      , type(repo::RepoType::NONE_e)
+      , emptybaseurls(false)
+    {}
+
+    ~Impl()
+    {}
+
+  public:
+    static const unsigned defaultPriority = 99;
+
+    void setProbedType( const repo::RepoType & t ) const
+    {
+      if ( type == repo::RepoType::NONE
+           && t != repo::RepoType::NONE )
+      {
+        // lazy init!
+        const_cast<Impl*>(this)->type = t;
+      }
+    }
+
+  public:
+    Pathname licenseTgz() const
+    { return metadatapath.empty() ? Pathname() : metadatapath / path / "license.tar.gz"; }
+
+    Url getmirrorListUrl() const
+    {
+      return replacer(mirrorlist_url);
+    }
+
+    Url &setmirrorListUrl()
+    {
+      return mirrorlist_url;
+    }
+
+    const std::set<Url> &baseUrls() const
+    {
+      if ( _baseUrls.empty() && ! (getmirrorListUrl().asString().empty()) )
+      {
+        emptybaseurls = true;
+        repo::RepoMirrorList *rmirrorlist = NULL;
+
+        DBG << "MetadataPath: " << metadatapath << endl;
+        if( metadatapath.empty() )
+          rmirrorlist = new repo::RepoMirrorList (getmirrorListUrl() );
+        else
+          rmirrorlist = new repo::RepoMirrorList (getmirrorListUrl(), metadatapath );
+
+        std::vector<Url> rmurls = rmirrorlist->getUrls();
+        delete rmirrorlist;
+        rmirrorlist = NULL;
+        _baseUrls.insert(rmurls.begin(), rmurls.end());
+      }
+      return _baseUrls;
+    }
+
+    std::set<Url> &baseUrls()
+    {
+      return _baseUrls;
+    }
+
+    bool baseurl2dump() const
+    {
+      return !emptybaseurls && !_baseUrls.empty();
+    }
+
+    /** Compute a resonable default for keepPackages based on URL scheme. */
+    bool keepPackagesDefault() const
+    {
+      if (indeterminate(keeppackages))
+      {
+        if (_baseUrls.empty())
+          return mirrorlist_url.schemeIsDownloading();
+        else
+          return _baseUrls.begin()->schemeIsDownloading();
+      }
+      return (bool) keeppackages;
+    }
+
+  public:
+    TriBool gpgcheck;
+    TriBool keeppackages;
+    Url gpgkey_url;
+    repo::RepoType type;
+    Pathname path;
+    std::string service;
+    std::string targetDistro;
+    Pathname metadatapath;
+    Pathname packagespath;
+    DefaultIntegral<unsigned,defaultPriority> priority;
+    mutable bool emptybaseurls;
+    repo::RepoVariablesUrlReplacer replacer;
+
+  private:
+    Url mirrorlist_url;
+    mutable std::set<Url> _baseUrls;
+
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoInfo::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const RepoInfo::Impl & obj )
+  {
+    return str << "RepoInfo::Impl";
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoInfo
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const RepoInfo RepoInfo::noRepo;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : RepoInfo::RepoInfo
+  //   METHOD TYPE : Ctor
+  //
+  RepoInfo::RepoInfo()
+  : _pimpl( new Impl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : RepoInfo::~RepoInfo
+  //   METHOD TYPE : Dtor
+  //
+  RepoInfo::~RepoInfo()
+  {
+    //MIL << std::endl;
+  }
+
+  unsigned RepoInfo::priority() const
+  { return _pimpl->priority; }
+  unsigned RepoInfo::defaultPriority()
+  { return Impl::defaultPriority; }
+  void RepoInfo::setPriority( unsigned newval_r )
+  {
+    _pimpl->priority = newval_r ? newval_r : Impl::defaultPriority;
+  }
+
+  void RepoInfo::setGpgCheck( bool check )
+  {
+    _pimpl->gpgcheck = check;
+  }
+
+  void RepoInfo::setMirrorListUrl( const Url &url )
+  {
+    _pimpl->setmirrorListUrl() = url;
+  }
+
+  void RepoInfo::setGpgKeyUrl( const Url &url )
+  {
+    _pimpl->gpgkey_url = url;
+  }
+
+  void RepoInfo::addBaseUrl( const Url &url )
+  {
+    _pimpl->baseUrls().insert(url);
+  }
+
+  void RepoInfo::setBaseUrl( const Url &url )
+  {
+    _pimpl->baseUrls().clear();
+    addBaseUrl(url);
+  }
+
+  void RepoInfo::setPath( const Pathname &path )
+  {
+    _pimpl->path = path;
+  }
+
+  void RepoInfo::setType( const repo::RepoType &t )
+  {
+    _pimpl->type = t;
+  }
+
+  void RepoInfo::setProbedType( const repo::RepoType &t ) const
+  { _pimpl->setProbedType( t ); }
+
+
+  void RepoInfo::setMetadataPath( const Pathname &path )
+  {
+    _pimpl->metadatapath = path;
+  }
+
+  void RepoInfo::setPackagesPath( const Pathname &path )
+  {
+    _pimpl->packagespath = path;
+  }
+
+  void RepoInfo::setKeepPackages( bool keep )
+  {
+    _pimpl->keeppackages = keep;
+  }
+
+  void RepoInfo::setService( const std::string& name )
+  {
+    _pimpl->service = name;
+  }
+
+  void RepoInfo::setTargetDistribution(
+      const std::string & targetDistribution)
+  {
+    _pimpl->targetDistro = targetDistribution;
+  }
+
+  bool RepoInfo::gpgCheck() const
+  { return indeterminate(_pimpl->gpgcheck) ? true : (bool) _pimpl->gpgcheck; }
+
+  Pathname RepoInfo::metadataPath() const
+  { return _pimpl->metadatapath; }
+
+  Pathname RepoInfo::packagesPath() const
+  { return _pimpl->packagespath; }
+
+  repo::RepoType RepoInfo::type() const
+  { return _pimpl->type; }
+
+  Url RepoInfo::mirrorListUrl() const
+  {
+    return _pimpl->getmirrorListUrl();
+  }
+
+  Url RepoInfo::gpgKeyUrl() const
+  { return _pimpl->gpgkey_url; }
+
+  std::set<Url> RepoInfo::baseUrls() const
+  {
+    RepoInfo::url_set replaced_urls;
+    for ( url_set::const_iterator it = _pimpl->baseUrls().begin();
+          it != _pimpl->baseUrls().end();
+          ++it )
+    {
+      replaced_urls.insert(_pimpl->replacer(*it));
+    }
+    return replaced_urls;
+  }
+
+  Pathname RepoInfo::path() const
+  { return _pimpl->path; }
+
+  std::string RepoInfo::service() const
+  { return _pimpl->service; }
+
+  std::string RepoInfo::targetDistribution() const
+  { return _pimpl->targetDistro; }
+
+  RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
+  {
+    return make_transform_iterator( _pimpl->baseUrls().begin(),
+                                    _pimpl->replacer );
+    //return _pimpl->baseUrls.begin();
+  }
+
+  RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
+  {
+    //return _pimpl->baseUrls.end();
+    return make_transform_iterator( _pimpl->baseUrls().end(),
+                                    _pimpl->replacer );
+  }
+
+  RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
+  { return _pimpl->baseUrls().size(); }
+
+  bool RepoInfo::baseUrlsEmpty() const
+  { return _pimpl->baseUrls().empty(); }
+
+  bool RepoInfo::baseUrlSet() const
+  { return _pimpl->baseurl2dump(); }
+
+  // false by default (if not set by setKeepPackages)
+  bool RepoInfo::keepPackages() const
+  {
+    return _pimpl->keepPackagesDefault();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  bool RepoInfo::hasLicense() const
+  {
+    Pathname licenseTgz( _pimpl->licenseTgz() );
+    SEC << licenseTgz << endl;
+    SEC << PathInfo(licenseTgz) << endl;
+
+    return ! licenseTgz.empty() &&  PathInfo(licenseTgz).isFile();
+  }
+
+  std::string RepoInfo::getLicense( const Locale & lang_r )
+  {
+    LocaleSet avlocales( getLicenseLocales() );
+    if ( avlocales.empty() )
+      return std::string();
+
+    Locale getLang( Locale::bestMatch( avlocales, lang_r ) );
+    if ( getLang == Locale::noCode
+         && avlocales.find( Locale::noCode ) == avlocales.end() )
+    {
+      WAR << "License.tar.gz contains no fallback text! " << *this << endl;
+      // Using the fist locale instead of returning no text at all.
+      // So the user might recognize that there is a license, even if he
+      // can't read it.
+      getLang = *avlocales.begin();
+    }
+
+    // now extract the license file.
+    static const std::string licenseFileFallback( "license.txt" );
+    std::string licenseFile( getLang == Locale::noCode
+                             ? licenseFileFallback
+                             : str::form( "license.%s.txt", getLang.code().c_str() ) );
+
+    ExternalProgram::Arguments cmd;
+    cmd.push_back( "tar" );
+    cmd.push_back( "-x" );
+    cmd.push_back( "-z" );
+    cmd.push_back( "-O" );
+    cmd.push_back( "-f" );
+    cmd.push_back( _pimpl->licenseTgz().asString() ); // if it not exists, avlocales was empty.
+    cmd.push_back( licenseFile );
+
+    std::string ret;
+    ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
+    for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
+    {
+      ret += output;
+    }
+    prog.close();
+    return ret;
+  }
+
+  LocaleSet RepoInfo::getLicenseLocales() const
+  {
+    Pathname licenseTgz( _pimpl->licenseTgz() );
+    if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
+      return LocaleSet();
+
+    ExternalProgram::Arguments cmd;
+    cmd.push_back( "tar" );
+    cmd.push_back( "-t" );
+    cmd.push_back( "-z" );
+    cmd.push_back( "-f" );
+    cmd.push_back( licenseTgz.asString() );
+
+    LocaleSet ret;
+    ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+    for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
+    {
+      static const C_Str license( "license." );
+      static const C_Str dotTxt( ".txt\n" );
+      if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
+      {
+        if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
+          ret.insert( Locale() );
+        else
+          ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
+      }
+      else
+      {
+        WAR << "  " << output;
+      }
+    }
+    prog.close();
+    return ret;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
+  {
+    RepoInfoBase::dumpOn(str);
+    if ( _pimpl->baseurl2dump() )
+    {
+      for ( urls_const_iterator it = baseUrlsBegin();
+            it != baseUrlsEnd();
+            ++it )
+      {
+        str << "- url         : " << *it << std::endl;
+      }
+    }
+    if ( ! (_pimpl->getmirrorListUrl().asString().empty())  )
+    {
+      str << "- mirrorlist  : " << _pimpl->getmirrorListUrl() << std::endl;
+    }
+    str << "- path        : " << path() << std::endl;
+    str << "- type        : " << type() << std::endl;
+    str << "- priority    : " << priority() << std::endl;
+
+    str << "- gpgcheck    : " << gpgCheck() << std::endl;
+    str << "- gpgkey      : " << gpgKeyUrl() << std::endl;
+
+    if (!indeterminate(_pimpl->keeppackages))
+      str << "- keeppackages: " << keepPackages() << std::endl;
+
+    str << "- service     : " << service() << std::endl;
+
+    if (!targetDistribution().empty())
+      str << "- targetdistro: " << targetDistribution() << std::endl;
+
+    if (!metadataPath().empty())
+      str << "- metadataPath: " << metadataPath() << std::endl;
+
+    if (!packagesPath().empty())
+      str << "- packagesPath: " << packagesPath() << std::endl;
+
+    return str;
+  }
+
+  std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
+  {
+    RepoInfoBase::dumpAsIniOn(str);
+
+    if ( _pimpl->baseurl2dump() )
+    {
+      str << "baseurl=";
+      for ( url_set::const_iterator it = _pimpl->baseUrls().begin();
+            it != _pimpl->baseUrls().end();
+            ++it )
+      {
+        str << *it << endl;
+      }
+    }
+
+    if ( ! _pimpl->path.empty() )
+      str << "path="<< path() << endl;
+
+    if ( ! (_pimpl->getmirrorListUrl().asString().empty()) )
+      str << "mirrorlist=" << _pimpl->getmirrorListUrl() << endl;
+
+    str << "type=" << type().asString() << endl;
+
+    if ( priority() != defaultPriority() )
+      str << "priority=" << priority() << endl;
+
+    if (!indeterminate(_pimpl->gpgcheck))
+      str << "gpgcheck=" << (gpgCheck() ? "1" : "0") << endl;
+    if ( ! (gpgKeyUrl().asString().empty()) )
+      str << "gpgkey=" <<gpgKeyUrl() << endl;
+
+    if (!indeterminate(_pimpl->keeppackages))
+      str << "keeppackages=" << keepPackages() << endl;
+
+    if( ! service().empty() )
+      str << "service=" << service() << endl;
+
+    return str;
+  }
+
+  std::ostream & RepoInfo::dumpAsXMLOn( std::ostream & str) const
+  { return dumpAsXMLOn(str, ""); }
+
+  std::ostream & RepoInfo::dumpAsXMLOn( std::ostream & str, const std::string & content) const
+  {
+    string tmpstr;
+    str
+      << "<repo"
+      << " alias=\"" << escape(alias()) << "\""
+      << " name=\"" << escape(name()) << "\"";
+    if (type() != repo::RepoType::NONE)
+      str << " type=\"" << type().asString() << "\"";
+    str
+      << " enabled=\"" << enabled() << "\""
+      << " autorefresh=\"" << autorefresh() << "\""
+      << " gpgcheck=\"" << gpgCheck() << "\"";
+    if (!(tmpstr = gpgKeyUrl().asString()).empty())
+      str << " gpgkey=\"" << escape(tmpstr) << "\"";
+    if (!(tmpstr = mirrorListUrl().asString()).empty())
+      str << " mirrorlist=\"" << escape(tmpstr) << "\"";
+    str << ">" << endl;
+
+    if ( _pimpl->baseurl2dump() )
+    {
+      for (RepoInfo::urls_const_iterator urlit = baseUrlsBegin();
+           urlit != baseUrlsEnd(); ++urlit)
+        str << "<url>" << escape(urlit->asString()) << "</url>" << endl;
+    }
+
+    str << "</repo>" << endl;
+    return str;
+  }
+
+
+  std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
+  {
+    return obj.dumpOn(str);
+  }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/RepoInfo.h b/zypp/RepoInfo.h
new file mode 100644 (file)
index 0000000..a9c61fa
--- /dev/null
@@ -0,0 +1,378 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoInfo.h
+ *
+*/
+#ifndef ZYPP2_REPOSITORYINFO_H
+#define ZYPP2_REPOSITORYINFO_H
+
+#include <list>
+#include <set>
+
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/Url.h"
+#include "zypp/Locale.h"
+#include "zypp/repo/RepoType.h"
+#include "zypp/repo/RepoVariables.h"
+
+#include "zypp/repo/RepoInfoBase.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoInfo
+  //
+  /**
+   * \short What is known about a repository
+   *
+   * The class RepoInfo represents everything that
+   * is known about a software repository.
+   *
+   * It can be used to store information about known
+   * sources.
+   *
+   * This class tries to be compatible with the
+   * concept of a .repo file used by YUM and
+   * also available in the openSUSE build service.
+   * See <tt>man yum.conf</tt>.
+   *
+   * Example file
+   *
+   * \code
+   * [ruby]
+   * name=Ruby repository (openSUSE_10.2)
+   * type=rpm-md
+   * baseurl=http://software.opensuse.org/download/ruby/openSUSE_10.2/
+   * gpgcheck=1
+   * gpgkey=http://software.opensuse.org/openSUSE-Build-Service.asc
+   * enabled=1
+   * priority=10
+   * \endcode
+   *
+   * \note A RepoInfo is a hint about how
+   * to create a Repository.
+   */
+  class RepoInfo : public repo::RepoInfoBase
+  {
+    friend std::ostream & operator<<( std::ostream & str, const RepoInfo & obj );
+
+    public:
+      RepoInfo();
+      virtual ~RepoInfo();
+
+      /** Represents no Repository (one with an empty alias). */
+      static const RepoInfo noRepo;
+
+    public:
+      /**
+       * The default priority (\c 99).
+       */
+      static unsigned defaultPriority();
+      /**
+       * Repository priority for solver.
+       * Some number between \c 1 (highest priority) and \c 99 (\ref defaultPriority).
+       */
+      unsigned priority() const;
+      /**
+       * Set repository priority for solver.
+       * A \c newval_r of \c 0 sets the default priority.
+       * \see \ref priority.
+       */
+      void setPriority( unsigned newval_r );
+
+      typedef std::set<Url>           url_set;
+      typedef url_set::size_type      urls_size_type;
+      typedef transform_iterator<repo::RepoVariablesUrlReplacer, url_set::const_iterator> urls_const_iterator;
+      /**
+       * whether repository urls are available
+       */
+      bool baseUrlsEmpty() const;
+      /**
+       * whether there are manualy configured repository urls
+       */
+      bool baseUrlSet() const;
+      /**
+       * number of repository urls
+       */
+      urls_size_type baseUrlsSize() const;
+      /**
+       * iterator that points at begin of repository urls
+       */
+      urls_const_iterator baseUrlsBegin() const;
+      /**
+       * iterator that points at end of repository urls
+       */
+      urls_const_iterator baseUrlsEnd() const;
+      /**
+       * Pars pro toto: The first repository url
+       */
+      Url url() const
+      { return( baseUrlsEmpty() ? Url() : *baseUrlsBegin()); }
+      /**
+       * A Url under which the metadata are located, or a set of mirrors.
+       *
+       * This can't be empty in order the repository to be valid
+       * unless the download of the mirror list succeeds and it
+       * contains a valid url.
+       *
+       * \deprecated IMO superfluous as we provide begin/end iterator.
+       */
+      std::set<Url> baseUrls() const;
+      /**
+       * Add a base url. \see baseUrls
+       * \param url The base url for the repository.
+       * \note can change keepPackages,so change it after this call
+       *
+       * To recreate the base URLs list, use \ref setBaseUrl(const Url &) followed
+       * by addBaseUrl().
+       */
+      void addBaseUrl( const Url &url );
+      /**
+       * Clears current base URL list and adds \a url.
+       * \note can change keepPackages,so change it after this call
+       */
+      void setBaseUrl( const Url &url );
+
+      /**
+       * \short Repository path
+       *
+       * Pathname relative to the base Url where the product/repository
+       * is located
+       *
+       * For media containing more than one product, or repositories not
+       * located at the root of the media it is important to know the path
+       * to the product directory relative to the media root. So a media
+       * verifier can be set for that media. You may also read it as
+       * <tt>baseUrl = url to mount</tt> and <tt>path = path on the
+       * mounted media</tt>.
+       *
+       * It is not mandatory, and the default is \c /.
+       *
+       * \note As a repository can have multiple Urls, the path is unique and
+       * the same for all Urls, so it is assumed all the Urls have the
+       * same media layout.
+       *
+       */
+      Pathname path() const;
+      /**
+       * set the product path. \see path()
+       * \param path the path to the product
+       */
+      void setPath( const Pathname &path );
+
+      /**
+       * Url of a file which contains a list of Urls
+       * If empty, the base url will be used.
+       */
+      Url mirrorListUrl() const;
+      /**
+       * Set mirror list url. \see mirrorListUrl
+       * \param url The base url for the list
+       */
+      void setMirrorListUrl( const Url &url );
+
+      /**
+       * Type of repository,
+       *
+       */
+      repo::RepoType type() const;
+      /**
+       * This allows to adjust the \ref  RepoType lazy, from \c NONE to
+       * some probed value, even for const objects.
+       *
+       * This is a NOOP if the current type is not \c NONE.
+       */
+      void setProbedType( const repo::RepoType &t ) const;
+      /**
+       * set the repository type \see type
+       * \param t
+       */
+      void setType( const repo::RepoType &t );
+
+      /**
+       * \short Path where this repo metadata was read from
+       *
+       * \note could be an empty pathname for repo
+       * infos created in memory.
+       */
+      Pathname metadataPath() const;
+      /**
+       * \short set the path where the local metadata is stored
+       *
+       * The path to the metadata of this repository
+       * was defined, or empty if nowhere.
+       *
+       * \param path directory path
+       */
+      void setMetadataPath( const Pathname &path );
+
+      /**
+       * \short Path where this repo packages are cached
+       */
+      Pathname packagesPath() const;
+      /**
+       * \short set the path where the local packages are stored
+       *
+       * \param path directory path
+       */
+      void setPackagesPath( const Pathname &path );
+
+      /**
+       * \short Whether to check or not this repository with gpg
+       *
+       * \note This is a just a hint to the application and can
+       * be ignored.
+       *
+       */
+      bool gpgCheck() const;
+      /**
+       * \short Whether to check or not this repository with gpg
+       *
+       * \param check true (check) or false (dont'check)
+       *
+       * \note This is a just a hint to the application and can
+       * be ignored.
+       *
+       */
+      void setGpgCheck( bool check );
+
+      /**
+       * \short Key to use for gpg checking of this repository
+       *
+       * \param url Url to the key in ASCII armored format
+       *
+       * \note This is a just a hint to the application and can
+       * be ignored.
+       *
+       */
+      Url gpgKeyUrl() const;
+      /**
+       * \short Key to use for gpg checking of this repository
+       *
+       * \param url Url to the key in ASCII armored format
+       *
+       * \note This is a just a hint to the application and can
+       * be ignored.
+       *
+       */
+      void setGpgKeyUrl( const Url &gpgkey );
+
+      /**
+       * \short Whether to keep the packages downloaded from this repository will be kept in local cache
+       */
+      bool keepPackages() const;
+      /**
+       * \short Set if the packaqes downloaded from this repository will be kept in local cache
+       *
+       * If the setting is true, all downloaded packages from this repository will be
+       * copied to the local raw cache.
+       *
+       * \param keep true (keep the downloaded packages) or false (delete them after installation)
+       *
+       */
+      void setKeepPackages( bool keep );
+
+      /**
+       * Gets name of the service to which this repository belongs or empty string
+       * if it has been added manually.
+       */
+      std::string service() const;
+      /**
+       * sets service which added this repository
+       */
+      void setService( const std::string& name );
+
+      /**
+       * Distribution for which is this repository meant.
+       */
+      std::string targetDistribution() const;
+      /**
+       * Sets the distribution for which is this repository meant. This is
+       * an in-memory value only, does not get written to the .repo file upon
+       * saving.
+       */
+      void setTargetDistribution(const std::string & targetDistribution);
+
+    public:
+      /** \name Repository license
+      */
+      //@{
+      /** Whether there is a license associated with the repo. */
+      bool hasLicense() const;
+
+      /** Return the best license for the current (or a specified) locale. */
+      std::string getLicense( const Locale & lang_r = Locale() );
+
+      /** Return the locales the license is available for.
+       * \ref Locale::noCode is included in case of \c license.txt which does
+       * not specify a specific locale.
+       */
+      LocaleSet getLicenseLocales() const;
+      //@}
+
+      /** \name Repository global unique id
+       *
+       *
+       */
+      //@{
+      //@}
+
+    public:
+      /**
+       * Write a human-readable representation of this RepoInfo object
+       * into the \a str stream. Useful for logging.
+       */
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      /**
+       * Write this RepoInfo object into \a str in a <tr>.repo</tt> file format.
+       */
+      virtual std::ostream & dumpAsIniOn( std::ostream & str ) const;
+
+      /**
+       * Write an XML representation of this RepoInfo object.
+       */
+      virtual std::ostream & dumpAsXMLOn(std::ostream & str) const;
+
+      /**
+       * Write an XML representation of this RepoInfo object.
+       *
+       * \param str
+       * \param content this argument is ignored (used in other classed derived
+       *                from RepoInfoBase.
+       */
+      virtual std::ostream & dumpAsXMLOn( std::ostream & str, const std::string & content ) const;
+
+      class Impl;
+    private:
+      /** Pointer to implementation */
+      RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoInfo */
+  typedef shared_ptr<RepoInfo> RepoInfo_Ptr;
+  /** \relates RepoInfo */
+  typedef shared_ptr<const RepoInfo> RepoInfo_constPtr;
+  /** \relates RepoInfo */
+  typedef std::list<RepoInfo> RepoInfoList;
+
+  /** \relates RepoInfo Stream output */
+  std::ostream & operator<<( std::ostream & str, const RepoInfo & obj );
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP2_REPOSITORYINFO_H
diff --git a/zypp/RepoManager.cc b/zypp/RepoManager.cc
new file mode 100644 (file)
index 0000000..8d704fe
--- /dev/null
@@ -0,0 +1,2240 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoManager.cc
+ *
+*/
+
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <list>
+#include <map>
+#include <algorithm>
+
+#include "zypp/base/InputStream.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Regex.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+
+#include "zypp/ServiceInfo.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/RepoManager.h"
+
+#include "zypp/media/MediaManager.h"
+#include "zypp/media/CredentialManager.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/ManagedFile.h"
+
+#include "zypp/parser/RepoFileReader.h"
+#include "zypp/parser/ServiceFileReader.h"
+#include "zypp/repo/ServiceRepos.h"
+#include "zypp/repo/yum/Downloader.h"
+#include "zypp/repo/susetags/Downloader.h"
+#include "zypp/parser/plaindir/RepoParser.h"
+#include "zypp/repo/PluginServices.h"
+
+#include "zypp/Target.h" // for Target::targetDistribution() for repo index services
+#include "zypp/ZYppFactory.h" // to get the Target from ZYpp instance
+#include "zypp/HistoryLog.h" // to write history :O)
+
+#include "zypp/ZYppCallbacks.h"
+
+#include "sat/Pool.h"
+
+using std::endl;
+using std::string;
+using namespace zypp::repo;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace
+  {
+    /** Simple media mounter to access non-downloading URLs e.g. for non-local plaindir repos.
+     * \ingroup g_RAII
+    */
+    class MediaMounter
+    {
+      public:
+        /** Ctor provides media access. */
+        MediaMounter( const Url & url_r )
+        {
+          media::MediaManager mediamanager;
+          _mid = mediamanager.open( url_r );
+          mediamanager.attach( _mid );
+        }
+
+        /** Ctor releases the media. */
+        ~MediaMounter()
+        {
+          media::MediaManager mediamanager;
+          mediamanager.release( _mid );
+          mediamanager.close( _mid );
+        }
+
+        /** Convert a path relative to the media into an absolute path.
+         *
+         * Called without argument it returns the path to the medias root directory.
+        */
+        Pathname getPathName( const Pathname & path_r = Pathname() ) const
+        {
+          media::MediaManager mediamanager;
+          return mediamanager.localPath( _mid, path_r );
+        }
+
+      private:
+        media::MediaAccessId _mid;
+    };
+
+    /** Check if alias_r is present in repo/service container. */
+    template <class Iterator>
+    inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r )
+    {
+      for_( it, begin_r, end_r )
+        if ( it->alias() == alias_r )
+          return true;
+      return false;
+    }
+    /** \overload */
+    template <class Container>
+    inline bool foundAliasIn( const std::string & alias_r, const Container & cont_r )
+    { return foundAliasIn( alias_r, cont_r.begin(), cont_r.end() ); }
+
+    /** Find alias_r in repo/service container. */
+    template <class Iterator>
+    inline Iterator findAlias( const std::string & alias_r, Iterator begin_r, Iterator end_r )
+    {
+      for_( it, begin_r, end_r )
+        if ( it->alias() == alias_r )
+          return it;
+      return end_r;
+    }
+    /** \overload */
+    template <class Container>
+    inline typename Container::iterator findAlias( const std::string & alias_r, Container & cont_r )
+    { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
+    /** \overload */
+    template <class Container>
+    inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r )
+    { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoManagerOptions
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
+  {
+    repoCachePath         = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
+    repoRawCachePath      = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
+    repoSolvCachePath     = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
+    repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
+    knownReposPath        = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
+    knownServicesPath     = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
+    pluginsPath           = Pathname::assertprefix( root_r, ZConfig::instance().pluginsPath() );
+    probe                 = ZConfig::instance().repo_add_probe();
+
+    rootDir = root_r;
+  }
+
+  RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
+  {
+    RepoManagerOptions ret;
+    ret.repoCachePath         = root_r;
+    ret.repoRawCachePath      = root_r/"raw";
+    ret.repoSolvCachePath     = root_r/"solv";
+    ret.repoPackagesCachePath = root_r/"packages";
+    ret.knownReposPath        = root_r/"repos.d";
+    ret.knownServicesPath     = root_r/"services.d";
+    ret.pluginsPath           = root_r/"plugins";
+    ret.rootDir = root_r;
+    return ret;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /**
+    * \short Simple callback to collect the results
+    *
+    * Classes like RepoFileParser call the callback
+    * once per each repo in a file.
+    *
+    * Passing this functor as callback, you can collect
+    * all results at the end, without dealing with async
+    * code.
+    *
+    * If targetDistro is set, all repos with non-empty RepoInfo::targetDistribution()
+    * will be skipped.
+    *
+    * \todo do this through a separate filter
+    */
+    struct RepoCollector : private base::NonCopyable
+    {
+      RepoCollector()
+      {}
+
+      RepoCollector(const std::string & targetDistro_)
+        : targetDistro(targetDistro_)
+      {}
+
+      bool collect( const RepoInfo &repo )
+      {
+        // skip repositories meant for other distros than specified
+        if (!targetDistro.empty()
+            && !repo.targetDistribution().empty()
+            && repo.targetDistribution() != targetDistro)
+        {
+          MIL
+            << "Skipping repository meant for '" << targetDistro
+            << "' distribution (current distro is '"
+            << repo.targetDistribution() << "')." << endl;
+
+          return true;
+        }
+
+        repos.push_back(repo);
+        return true;
+      }
+
+      RepoInfoList repos;
+      std::string targetDistro;
+    };
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Reads RepoInfo's from a repo file.
+   *
+   * \param file pathname of the file to read.
+   */
+  static std::list<RepoInfo> repositories_in_file( const Pathname & file )
+  {
+    MIL << "repo file: " << file << endl;
+    RepoCollector collector;
+    parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
+    return collector.repos;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * \short List of RepoInfo's from a directory
+   *
+   * Goes trough every file ending with ".repo" in a directory and adds all
+   * RepoInfo's contained in that file.
+   *
+   * \param dir pathname of the directory to read.
+   */
+  static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
+  {
+    MIL << "directory " << dir << endl;
+    std::list<RepoInfo> repos;
+    std::list<Pathname> entries;
+    if ( filesystem::readdir( entries, dir, false ) != 0 )
+    {
+      // TranslatorExplanation '%s' is a pathname
+      ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+    }
+
+    str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
+    for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+    {
+      if (str::regex_match(it->extension(), allowedRepoExt))
+      {
+        std::list<RepoInfo> tmp = repositories_in_file( *it );
+        repos.insert( repos.end(), tmp.begin(), tmp.end() );
+
+        //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
+        //MIL << "ok" << endl;
+      }
+    }
+    return repos;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+   std::list<RepoInfo> readRepoFile(const Url & repo_file)
+   {
+     // no interface to download a specific file, using workaround:
+     //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
+     Url url(repo_file);
+     Pathname path(url.getPathName());
+     url.setPathName ("/");
+     MediaSetAccess access(url);
+     Pathname local = access.provideFile(path);
+
+     DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
+
+     return repositories_in_file(local);
+   }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  inline void assert_alias( const RepoInfo & info )
+  {
+    if ( info.alias().empty() )
+      ZYPP_THROW( RepoNoAliasException() );
+    // bnc #473834. Maybe we can match the alias against a regex to define
+    // and check for valid aliases
+    if ( info.alias()[0] == '.')
+      ZYPP_THROW(RepoInvalidAliasException(
+         info, _("Repository alias cannot start with dot.")));
+  }
+
+  inline void assert_alias( const ServiceInfo & info )
+  {
+    if ( info.alias().empty() )
+      ZYPP_THROW( ServiceNoAliasException() );
+    // bnc #473834. Maybe we can match the alias against a regex to define
+    // and check for valid aliases
+    if ( info.alias()[0] == '.')
+      ZYPP_THROW(ServiceInvalidAliasException(
+         info, _("Service alias cannot start with dot.")));
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  inline void assert_urls( const RepoInfo & info )
+  {
+    if ( info.baseUrlsEmpty() )
+      ZYPP_THROW( RepoNoUrlException( info ) );
+  }
+
+  inline void assert_url( const ServiceInfo & info )
+  {
+    if ( ! info.url().isValid() )
+      ZYPP_THROW( ServiceNoUrlException( info ) );
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * \short Calculates the raw cache path for a repository, this is usually
+   * /var/cache/zypp/alias
+   */
+  inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+  {
+    assert_alias(info);
+    return opt.repoRawCachePath / info.escaped_alias();
+  }
+
+  /**
+   * \short Calculates the raw product metadata path for a repository, this is
+   * inside the raw cache dir, plus an optional path where the metadata is.
+   *
+   * It should be different only for repositories that are not in the root of
+   * the media.
+   * for example /var/cache/zypp/alias/addondir
+   */
+  inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+  {
+    assert_alias(info);
+    return opt.repoRawCachePath / info.escaped_alias() / info.path();
+  }
+
+
+  /**
+   * \short Calculates the packages cache path for a repository
+   */
+  inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+  {
+    assert_alias(info);
+    return opt.repoPackagesCachePath / info.escaped_alias();
+  }
+
+  /**
+   * \short Calculates the solv cache path for a repository
+   */
+  inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
+  {
+    assert_alias(info);
+    return opt.repoSolvCachePath / info.escaped_alias();
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /** Functor collecting ServiceInfos into a ServiceSet. */
+  class ServiceCollector
+  {
+    public:
+      typedef std::set<ServiceInfo> ServiceSet;
+
+      ServiceCollector( ServiceSet & services_r )
+      : _services( services_r )
+      {}
+
+      bool operator()( const ServiceInfo & service_r ) const
+      {
+        _services.insert( service_r );
+        return true;
+      }
+
+    private:
+      ServiceSet & _services;
+  };
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoManager::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * \short RepoManager implementation.
+   */
+  struct RepoManager::Impl
+  {
+    Impl( const RepoManagerOptions &opt )
+      : options(opt)
+    {
+      init_knownServices();
+      init_knownRepositories();
+    }
+
+
+    RepoManagerOptions options;
+
+    RepoSet repos;
+
+    ServiceSet services;
+
+  public:
+
+    void saveService( ServiceInfo & service ) const;
+
+    Pathname generateNonExistingName( const Pathname &dir,
+                                      const std::string &basefilename ) const;
+
+    std::string generateFilename( const RepoInfo & info ) const;
+    std::string generateFilename( const ServiceInfo & info ) const;
+
+
+  private:
+    void init_knownServices();
+    void init_knownRepositories();
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoManager::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
+  {
+    return str << "RepoManager::Impl";
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  void RepoManager::Impl::saveService( ServiceInfo & service ) const
+  {
+    filesystem::assert_dir( options.knownServicesPath );
+    Pathname servfile = generateNonExistingName( options.knownServicesPath,
+                                                 generateFilename( service ) );
+    service.setFilepath( servfile );
+
+    MIL << "saving service in " << servfile << endl;
+
+    std::ofstream file( servfile.c_str() );
+    if ( !file )
+    {
+      // TranslatorExplanation '%s' is a filename
+      ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
+    }
+    service.dumpAsIniOn( file );
+    MIL << "done" << endl;
+  }
+
+  /**
+   * Generate a non existing filename in a directory, using a base
+   * name. For example if a directory contains 3 files
+   *
+   * |-- bar
+   * |-- foo
+   * `-- moo
+   *
+   * If you try to generate a unique filename for this directory,
+   * based on "ruu" you will get "ruu", but if you use the base
+   * "foo" you will get "foo_1"
+   *
+   * \param dir Directory where the file needs to be unique
+   * \param basefilename string to base the filename on.
+   */
+  Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir,
+                                                       const std::string & basefilename ) const
+  {
+    std::string final_filename = basefilename;
+    int counter = 1;
+    while ( PathInfo(dir + final_filename).isExist() )
+    {
+      final_filename = basefilename + "_" + str::numstring(counter);
+      counter++;
+    }
+    return dir + Pathname(final_filename);
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * \short Generate a related filename from a repo info
+   *
+   * From a repo info, it will try to use the alias as a filename
+   * escaping it if necessary. Other fallbacks can be added to
+   * this function in case there is no way to use the alias
+   */
+  std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const
+  {
+    std::string filename = info.alias();
+    // replace slashes with underscores
+    str::replaceAll( filename, "/", "_" );
+
+    filename = Pathname(filename).extend(".repo").asString();
+    MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
+    return filename;
+  }
+
+  std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const
+  {
+    std::string filename = info.alias();
+    // replace slashes with underscores
+    str::replaceAll( filename, "/", "_" );
+
+    filename = Pathname(filename).extend(".service").asString();
+    MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl;
+    return filename;
+  }
+
+
+  void RepoManager::Impl::init_knownServices()
+  {
+    Pathname dir = options.knownServicesPath;
+    std::list<Pathname> entries;
+    if (PathInfo(dir).isExist())
+    {
+      if ( filesystem::readdir( entries, dir, false ) != 0 )
+      {
+        // TranslatorExplanation '%s' is a pathname
+        ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+      }
+
+      //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
+      for_(it, entries.begin(), entries.end() )
+      {
+        parser::ServiceFileReader(*it, ServiceCollector(services));
+      }
+    }
+
+    repo::PluginServices(options.pluginsPath/"services", ServiceCollector(services));
+  }
+
+  void RepoManager::Impl::init_knownRepositories()
+  {
+    MIL << "start construct known repos" << endl;
+
+    if ( PathInfo(options.knownReposPath).isExist() )
+    {
+      RepoInfoList repol = repositories_in_dir(options.knownReposPath);
+      std::list<string> repo_esc_aliases;
+      std::list<string> entries;
+      for ( RepoInfoList::iterator it = repol.begin();
+            it != repol.end();
+            ++it )
+      {
+        // set the metadata path for the repo
+        Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
+        (*it).setMetadataPath(metadata_path);
+
+       // set the downloaded packages path for the repo
+       Pathname packages_path = packagescache_path_for_repoinfo(options, (*it));
+       (*it).setPackagesPath(packages_path);
+
+        repos.insert(*it);
+        repo_esc_aliases.push_back(it->escaped_alias());
+      }
+
+      // delete metadata folders without corresponding repo (e.g. old tmp directories)
+      if ( filesystem::readdir( entries, options.repoRawCachePath, false ) == 0 )
+      {
+        std::set<string> oldfiles;
+        repo_esc_aliases.sort();
+        entries.sort();
+        set_difference(entries.begin(), entries.end(), repo_esc_aliases.begin(), repo_esc_aliases.end(), std::inserter(oldfiles, oldfiles.end()));
+        for_(it, oldfiles.begin(), oldfiles.end())
+        {
+          filesystem::recursive_rmdir(options.repoRawCachePath / *it);
+        }
+      }
+    }
+
+    MIL << "end construct known repos" << endl;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoManager
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  RepoManager::RepoManager( const RepoManagerOptions &opt )
+  : _pimpl( new Impl(opt) )
+  {}
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  RepoManager::~RepoManager()
+  {}
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  bool RepoManager::repoEmpty() const
+  { return _pimpl->repos.empty(); }
+
+  RepoManager::RepoSizeType RepoManager::repoSize() const
+  { return _pimpl->repos.size(); }
+
+  RepoManager::RepoConstIterator RepoManager::repoBegin() const
+  { return _pimpl->repos.begin(); }
+
+  RepoManager::RepoConstIterator RepoManager::repoEnd() const
+  { return _pimpl->repos.end(); }
+
+  RepoInfo RepoManager::getRepo( const std::string & alias ) const
+  {
+    for_( it, repoBegin(), repoEnd() )
+      if ( it->alias() == alias )
+        return *it;
+    return RepoInfo::noRepo;
+  }
+
+  bool RepoManager::hasRepo( const std::string & alias ) const
+  {
+    for_( it, repoBegin(), repoEnd() )
+      if ( it->alias() == alias )
+        return true;
+    return false;
+  }
+
+  std::string RepoManager::makeStupidAlias( const Url & url_r )
+  {
+    std::string ret( url_r.getScheme() );
+    if ( ret.empty() )
+      ret = "repo-";
+    else
+      ret += "-";
+
+    std::string host( url_r.getHost() );
+    if ( ! host.empty() )
+    {
+      ret += host;
+      ret += "-";
+    }
+
+    static Date::ValueType serial = Date::now();
+    ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8);
+    return ret;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  Pathname RepoManager::metadataPath( const RepoInfo &info ) const
+  {
+    return rawcache_path_for_repoinfo(_pimpl->options, info );
+  }
+
+  Pathname RepoManager::packagesPath( const RepoInfo &info ) const
+  {
+    return packagescache_path_for_repoinfo(_pimpl->options, info );
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
+  {
+    Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
+    Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
+    RepoType repokind = info.type();
+    RepoStatus status;
+
+    switch ( repokind.toEnum() )
+    {
+      case RepoType::NONE_e:
+      // unknown, probe the local metadata
+        repokind = probe( productdatapath.asUrl() );
+      break;
+      default:
+      break;
+    }
+
+    switch ( repokind.toEnum() )
+    {
+      case RepoType::RPMMD_e :
+      {
+        status = RepoStatus( productdatapath + "/repodata/repomd.xml");
+      }
+      break;
+
+      case RepoType::YAST2_e :
+      {
+        status = RepoStatus( productdatapath + "/content") && (RepoStatus( mediarootpath + "/media.1/media"));
+      }
+      break;
+
+      case RepoType::RPMPLAINDIR_e :
+      {
+        if ( PathInfo(Pathname(productdatapath + "/cookie")).isExist() )
+          status = RepoStatus( productdatapath + "/cookie");
+      }
+      break;
+
+      case RepoType::NONE_e :
+       // Return default RepoStatus in case of RepoType::NONE
+       // indicating it should be created?
+        // ZYPP_THROW(RepoUnknownTypeException());
+       break;
+    }
+    return status;
+  }
+
+  void RepoManager::touchIndexFile(const RepoInfo & info)
+  {
+    Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
+
+    RepoType repokind = info.type();
+    if ( repokind.toEnum() == RepoType::NONE_e )
+      // unknown, probe the local metadata
+      repokind = probe( productdatapath.asUrl() );
+    // if still unknown, just return
+    if (repokind == RepoType::NONE_e)
+      return;
+
+    Pathname p;
+    switch ( repokind.toEnum() )
+    {
+      case RepoType::RPMMD_e :
+        p = Pathname(productdatapath + "/repodata/repomd.xml");
+        break;
+
+      case RepoType::YAST2_e :
+        p = Pathname(productdatapath + "/content");
+        break;
+
+      case RepoType::RPMPLAINDIR_e :
+        p = Pathname(productdatapath + "/cookie");
+        break;
+
+      case RepoType::NONE_e :
+      default:
+        break;
+    }
+
+    // touch the file, ignore error (they are logged anyway)
+    filesystem::touch(p);
+  }
+
+  RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata(
+                                              const RepoInfo &info,
+                                              const Url &url,
+                                              RawMetadataRefreshPolicy policy )
+  {
+    assert_alias(info);
+
+    RepoStatus oldstatus;
+    RepoStatus newstatus;
+
+    try
+    {
+      MIL << "Going to try to check whether refresh is needed for " << url << endl;
+
+      // first check old (cached) metadata
+      Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
+      filesystem::assert_dir(mediarootpath);
+      oldstatus = metadataStatus(info);
+
+      if ( oldstatus.empty() )
+      {
+        MIL << "No cached metadata, going to refresh" << endl;
+        return REFRESH_NEEDED;
+      }
+
+      {
+        std::string scheme( url.getScheme() );
+        if ( scheme == "cd" || scheme == "dvd" )
+        {
+          MIL << "never refresh CD/DVD" << endl;
+          return REPO_UP_TO_DATE;
+        }
+      }
+
+      // now we've got the old (cached) status, we can decide repo.refresh.delay
+      if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
+      {
+        // difference in seconds
+        double diff = difftime(
+          (Date::ValueType)Date::now(),
+          (Date::ValueType)oldstatus.timestamp()) / 60;
+
+        DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
+        DBG << "current time: " << (Date::ValueType)Date::now() << endl;
+        DBG << "last refresh = " << diff << " minutes ago" << endl;
+
+        if ( diff < ZConfig::instance().repo_refresh_delay() )
+        {
+         if ( diff < 0 )
+         {
+           WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl;
+         }
+         else
+         {
+           MIL << "Repository '" << info.alias()
+               << "' has been refreshed less than repo.refresh.delay ("
+               << ZConfig::instance().repo_refresh_delay()
+               << ") minutes ago. Advising to skip refresh" << endl;
+           return REPO_CHECK_DELAYED;
+         }
+        }
+      }
+
+      // To test the new matadta create temp dir as sibling of mediarootpath
+      filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
+
+      repo::RepoType repokind = info.type();
+      // if the type is unknown, try probing.
+      switch ( repokind.toEnum() )
+      {
+        case RepoType::NONE_e:
+          // unknown, probe it \todo respect productdir
+          repokind = probe( url, info.path() );
+        break;
+        default:
+        break;
+      }
+
+      if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
+           ( repokind.toEnum() == RepoType::YAST2_e ) )
+      {
+        MediaSetAccess media(url);
+        shared_ptr<repo::Downloader> downloader_ptr;
+
+        if ( repokind.toEnum() == RepoType::RPMMD_e )
+          downloader_ptr.reset(new yum::Downloader(info, mediarootpath));
+        else
+          downloader_ptr.reset( new susetags::Downloader(info, mediarootpath));
+
+        RepoStatus newstatus = downloader_ptr->status(media);
+        bool refresh = false;
+        if ( oldstatus.checksum() == newstatus.checksum() )
+        {
+          MIL << "repo has not changed" << endl;
+          if ( policy == RefreshForced )
+          {
+            MIL << "refresh set to forced" << endl;
+            refresh = true;
+          }
+        }
+        else
+        {
+          MIL << "repo has changed, going to refresh" << endl;
+          refresh = true;
+        }
+
+        if (!refresh)
+          touchIndexFile(info);
+
+        return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+      }
+      else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
+      {
+        MediaMounter media( url );
+        RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
+        bool refresh = false;
+        if ( oldstatus.checksum() == newstatus.checksum() )
+        {
+          MIL << "repo has not changed" << endl;
+          if ( policy == RefreshForced )
+          {
+            MIL << "refresh set to forced" << endl;
+            refresh = true;
+          }
+        }
+        else
+        {
+          MIL << "repo has changed, going to refresh" << endl;
+          refresh = true;
+        }
+
+        if (!refresh)
+          touchIndexFile(info);
+
+        return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+      }
+      else
+      {
+        ZYPP_THROW(RepoUnknownTypeException(info));
+      }
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      ERR << "refresh check failed for " << url << endl;
+      ZYPP_RETHROW(e);
+    }
+
+    return REFRESH_NEEDED; // default
+  }
+
+  void RepoManager::refreshMetadata( const RepoInfo &info,
+                                     RawMetadataRefreshPolicy policy,
+                                     const ProgressData::ReceiverFnc & progress )
+  {
+    assert_alias(info);
+    assert_urls(info);
+
+    // we will throw this later if no URL checks out fine
+    RepoException rexception(_("Valid metadata not found at specified URL(s)"));
+
+    // try urls one by one
+    for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
+    {
+      try
+      {
+        Url url(*it);
+
+        // check whether to refresh metadata
+        // if the check fails for this url, it throws, so another url will be checked
+        if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
+          return;
+
+        MIL << "Going to refresh metadata from " << url << endl;
+
+        repo::RepoType repokind = info.type();
+
+        // if the type is unknown, try probing.
+        switch ( repokind.toEnum() )
+        {
+          case RepoType::NONE_e:
+            // unknown, probe it
+            repokind = probe( *it, info.path() );
+
+            if (repokind.toEnum() != RepoType::NONE_e)
+            {
+              // Adjust the probed type in RepoInfo
+              info.setProbedType( repokind ); // lazy init!
+              //save probed type only for repos in system
+              for_( it, repoBegin(), repoEnd() )
+              {
+                if ( info.alias() == (*it).alias() )
+                {
+                  RepoInfo modifiedrepo = info;
+                  modifiedrepo.setType( repokind );
+                  modifyRepository( info.alias(), modifiedrepo );
+                  break;
+                }
+              }
+            }
+          break;
+          default:
+          break;
+        }
+
+        Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
+        if( filesystem::assert_dir(mediarootpath) )
+        {
+          Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) );
+          ZYPP_THROW(ex);
+        }
+
+        // create temp dir as sibling of mediarootpath
+        filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
+        if( tmpdir.path().empty() )
+        {
+          Exception ex(_("Can't create metadata cache directory."));
+          ZYPP_THROW(ex);
+        }
+
+        if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
+             ( repokind.toEnum() == RepoType::YAST2_e ) )
+        {
+          MediaSetAccess media(url);
+          shared_ptr<repo::Downloader> downloader_ptr;
+
+          MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
+
+          if ( repokind.toEnum() == RepoType::RPMMD_e )
+            downloader_ptr.reset(new yum::Downloader(info, mediarootpath));
+          else
+            downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) );
+
+          /**
+           * Given a downloader, sets the other repos raw metadata
+           * path as cache paths for the fetcher, so if another
+           * repo has the same file, it will not download it
+           * but copy it from the other repository
+           */
+          for_( it, repoBegin(), repoEnd() )
+          {
+            Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
+            if ( PathInfo(cachepath).isExist() )
+              downloader_ptr->addCachePath(cachepath);
+          }
+
+          downloader_ptr->download( media, tmpdir.path() );
+        }
+        else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
+        {
+          MediaMounter media( url );
+          RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
+
+          Pathname productpath( tmpdir.path() / info.path() );
+          filesystem::assert_dir( productpath );
+          std::ofstream file( (productpath/"cookie").c_str() );
+          if ( !file )
+          {
+            // TranslatorExplanation '%s' is a filename
+            ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), (productpath/"cookie").c_str() )));
+          }
+          file << url;
+          if ( ! info.path().empty() && info.path() != "/" )
+            file << " (" << info.path() << ")";
+          file << endl;
+          file << newstatus.checksum() << endl;
+
+          file.close();
+        }
+        else
+        {
+          ZYPP_THROW(RepoUnknownTypeException());
+        }
+
+        // ok we have the metadata, now exchange
+        // the contents
+       filesystem::exchange( tmpdir.path(), mediarootpath );
+
+        // we are done.
+        return;
+      }
+      catch ( const Exception &e )
+      {
+        ZYPP_CAUGHT(e);
+        ERR << "Trying another url..." << endl;
+
+        // remember the exception caught for the *first URL*
+        // if all other URLs fail, the rexception will be thrown with the
+        // cause of the problem of the first URL remembered
+        if (it == info.baseUrlsBegin())
+          rexception.remember(e);
+      }
+    } // for every url
+    ERR << "No more urls..." << endl;
+    ZYPP_THROW(rexception);
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::cleanMetadata( const RepoInfo &info,
+                                   const ProgressData::ReceiverFnc & progressfnc )
+  {
+    ProgressData progress(100);
+    progress.sendTo(progressfnc);
+
+    filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
+    progress.toMax();
+  }
+
+  void RepoManager::cleanPackages( const RepoInfo &info,
+                                   const ProgressData::ReceiverFnc & progressfnc )
+  {
+    ProgressData progress(100);
+    progress.sendTo(progressfnc);
+
+    filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info));
+    progress.toMax();
+  }
+
+  void RepoManager::buildCache( const RepoInfo &info,
+                                CacheBuildPolicy policy,
+                                const ProgressData::ReceiverFnc & progressrcv )
+  {
+    assert_alias(info);
+    Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
+    Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
+
+    if( filesystem::assert_dir(_pimpl->options.repoCachePath) )
+    {
+      Exception ex(str::form( _("Can't create %s"), _pimpl->options.repoCachePath.c_str()) );
+      ZYPP_THROW(ex);
+    }
+    RepoStatus raw_metadata_status = metadataStatus(info);
+    if ( raw_metadata_status.empty() )
+    {
+       /* if there is no cache at this point, we refresh the raw
+          in case this is the first time - if it's !autorefresh,
+          we may still refresh */
+      refreshMetadata(info, RefreshIfNeeded, progressrcv );
+      raw_metadata_status = metadataStatus(info);
+    }
+
+    bool needs_cleaning = false;
+    if ( isCached( info ) )
+    {
+      MIL << info.alias() << " is already cached." << endl;
+      RepoStatus cache_status = cacheStatus(info);
+
+      if ( cache_status.checksum() == raw_metadata_status.checksum() )
+      {
+        MIL << info.alias() << " cache is up to date with metadata." << endl;
+        if ( policy == BuildIfNeeded ) {
+          return;
+        }
+        else {
+          MIL << info.alias() << " cache rebuild is forced" << endl;
+        }
+      }
+
+      needs_cleaning = true;
+    }
+
+    ProgressData progress(100);
+    callback::SendReport<ProgressReport> report;
+    progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
+    progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str()));
+    progress.toMin();
+
+    if (needs_cleaning)
+    {
+      cleanCache(info);
+    }
+
+    MIL << info.alias() << " building cache..." << info.type() << endl;
+
+    Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
+
+    if( filesystem::assert_dir(base) )
+    {
+      Exception ex(str::form( _("Can't create %s"), base.c_str()) );
+      ZYPP_THROW(ex);
+    }
+
+    if( ! PathInfo(base).userMayW() )
+    {
+      Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) );
+      ZYPP_THROW(ex);
+    }
+    Pathname solvfile = base / "solv";
+
+    // do we have type?
+    repo::RepoType repokind = info.type();
+
+    // if the type is unknown, try probing.
+    switch ( repokind.toEnum() )
+    {
+      case RepoType::NONE_e:
+        // unknown, probe the local metadata
+        repokind = probe( productdatapath.asUrl() );
+      break;
+      default:
+      break;
+    }
+
+    MIL << "repo type is " << repokind << endl;
+
+    switch ( repokind.toEnum() )
+    {
+      case RepoType::RPMMD_e :
+      case RepoType::YAST2_e :
+      case RepoType::RPMPLAINDIR_e :
+      {
+        // Take care we unlink the solvfile on exception
+        ManagedFile guard( solvfile, filesystem::unlink );
+        scoped_ptr<MediaMounter> forPlainDirs;
+
+        ExternalProgram::Arguments cmd;
+        cmd.push_back( "repo2solv.sh" );
+
+        // repo2solv expects -o as 1st arg!
+        cmd.push_back( "-o" );
+        cmd.push_back( solvfile.asString() );
+
+        if ( repokind == RepoType::RPMPLAINDIR )
+        {
+          forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) );
+          // recusive for plaindir as 2nd arg!
+          cmd.push_back( "-R" );
+          // FIXME this does only work form dir: URLs
+          cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() );
+        }
+        else
+          cmd.push_back( productdatapath.asString() );
+
+        ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+        std::string errdetail;
+
+        for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+          WAR << "  " << output;
+          if ( errdetail.empty() ) {
+            errdetail = prog.command();
+            errdetail += '\n';
+          }
+          errdetail += output;
+        }
+
+        int ret = prog.close();
+        if ( ret != 0 )
+        {
+          RepoException ex(str::form( _("Failed to cache repo (%d)."), ret ));
+          ex.remember( errdetail );
+          ZYPP_THROW(ex);
+        }
+
+        // We keep it.
+        guard.resetDispose();
+      }
+      break;
+      default:
+        ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") ));
+      break;
+    }
+    // update timestamp and checksum
+    setCacheStatus(info, raw_metadata_status);
+    MIL << "Commit cache.." << endl;
+    progress.toMax();
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  repo::RepoType RepoManager::probe( const Url & url ) const
+  { return probe( url, Pathname() ); }
+
+  repo::RepoType RepoManager::probe( const Url & url, const Pathname & path  ) const
+  {
+    MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
+
+    if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() )
+    {
+      // Handle non existing local directory in advance, as
+      // MediaSetAccess does not support it.
+      MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
+      return repo::RepoType::NONE;
+    }
+
+    // prepare exception to be thrown if the type could not be determined
+    // due to a media exception. We can't throw right away, because of some
+    // problems with proxy servers returning an incorrect error
+    // on ftp file-not-found(bnc #335906). Instead we'll check another types
+    // before throwing.
+
+    // TranslatorExplanation '%s' is an URL
+    RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
+    bool gotMediaException = false;
+    try
+    {
+      MediaSetAccess access(url);
+      try
+      {
+        if ( access.doesFileExist(path/"/repodata/repomd.xml") )
+        {
+          MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl;
+          return repo::RepoType::RPMMD;
+        }
+      }
+      catch ( const media::MediaException &e )
+      {
+        ZYPP_CAUGHT(e);
+        DBG << "problem checking for repodata/repomd.xml file" << endl;
+        enew.remember(e);
+        gotMediaException = true;
+      }
+
+      try
+      {
+        if ( access.doesFileExist(path/"/content") )
+        {
+          MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl;
+          return repo::RepoType::YAST2;
+        }
+      }
+      catch ( const media::MediaException &e )
+      {
+        ZYPP_CAUGHT(e);
+        DBG << "problem checking for content file" << endl;
+        enew.remember(e);
+        gotMediaException = true;
+      }
+
+      // if it is a non-downloading URL denoting a directory
+      if ( ! url.schemeIsDownloading() )
+      {
+        MediaMounter media( url );
+        if ( PathInfo(media.getPathName()/path).isDir() )
+        {
+          // allow empty dirs for now
+          MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl;
+          return repo::RepoType::RPMPLAINDIR;
+        }
+      }
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      // TranslatorExplanation '%s' is an URL
+      Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
+      enew.remember(e);
+      ZYPP_THROW(enew);
+    }
+
+    if (gotMediaException)
+      ZYPP_THROW(enew);
+
+    MIL << "Probed type NONE at " << url << " (" << path << ")" << endl;
+    return repo::RepoType::NONE;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
+  {
+    MIL << "Going to clean up garbage in cache dirs" << endl;
+
+    ProgressData progress(300);
+    progress.sendTo(progressrcv);
+    progress.toMin();
+
+    std::list<Pathname> cachedirs;
+    cachedirs.push_back(_pimpl->options.repoRawCachePath);
+    cachedirs.push_back(_pimpl->options.repoPackagesCachePath);
+    cachedirs.push_back(_pimpl->options.repoSolvCachePath);
+
+    for_( dir, cachedirs.begin(), cachedirs.end() )
+    {
+      if ( PathInfo(*dir).isExist() )
+      {
+        std::list<Pathname> entries;
+        if ( filesystem::readdir( entries, *dir, false ) != 0 )
+          // TranslatorExplanation '%s' is a pathname
+          ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
+
+        unsigned sdircount   = entries.size();
+        unsigned sdircurrent = 1;
+        for_( subdir, entries.begin(), entries.end() )
+        {
+          // if it does not belong known repo, make it disappear
+          bool found = false;
+          for_( r, repoBegin(), repoEnd() )
+            if ( subdir->basename() == r->escaped_alias() )
+            { found = true; break; }
+
+          if ( ! found )
+            filesystem::recursive_rmdir( *subdir );
+
+          progress.set( progress.val() + sdircurrent * 100 / sdircount );
+          ++sdircurrent;
+        }
+      }
+      else
+        progress.set( progress.val() + 100 );
+    }
+    progress.toMax();
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::cleanCache( const RepoInfo &info,
+                                const ProgressData::ReceiverFnc & progressrcv )
+  {
+    ProgressData progress(100);
+    progress.sendTo(progressrcv);
+    progress.toMin();
+
+    MIL << "Removing raw metadata cache for " << info.alias() << endl;
+    filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
+
+    progress.toMax();
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  bool RepoManager::isCached( const RepoInfo &info ) const
+  {
+    return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
+  }
+
+  RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
+  {
+
+    Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
+
+    return RepoStatus::fromCookieFile(cookiefile);
+  }
+
+  void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
+  {
+    Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
+    filesystem::assert_dir(base);
+    Pathname cookiefile = base / "cookie";
+
+    status.saveToCookieFile(cookiefile);
+  }
+
+  void RepoManager::loadFromCache( const RepoInfo & info,
+                                   const ProgressData::ReceiverFnc & progressrcv )
+  {
+    assert_alias(info);
+    Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
+
+    if ( ! PathInfo(solvfile).isExist() )
+      ZYPP_THROW(RepoNotCachedException(info));
+
+    try
+    {
+      Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
+      // test toolversion in order to rebuild solv file in case
+      // it was written by an old satsolver-tool parser.
+      //
+      // Known version strings used:
+      //  - <no string>
+      //  - "1.0"
+      //
+      sat::LookupRepoAttr toolversion( sat::SolvAttr::repositoryToolVersion, repo );
+      if ( toolversion.begin().asString().empty() )
+      {
+        repo.eraseFromPool();
+        ZYPP_THROW(Exception("Solv-file was created by old parser."));
+      }
+      // else: up-to-date (or even newer).
+    }
+    catch ( const Exception & exp )
+    {
+      ZYPP_CAUGHT( exp );
+      MIL << "Try to handle exception by rebuilding the solv-file" << endl;
+      cleanCache( info, progressrcv );
+      buildCache( info, BuildIfNeeded, progressrcv );
+
+      sat::Pool::instance().addRepoSolv( solvfile, info );
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::addRepository( const RepoInfo &info,
+                                   const ProgressData::ReceiverFnc & progressrcv )
+  {
+    assert_alias(info);
+
+    ProgressData progress(100);
+    callback::SendReport<ProgressReport> report;
+    progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
+    progress.name(str::form(_("Adding repository '%s'"), info.label().c_str()));
+    progress.toMin();
+
+    MIL << "Try adding repo " << info << endl;
+
+    RepoInfo tosave = info;
+    if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
+        ZYPP_THROW(RepoAlreadyExistsException(info));
+
+    // check the first url for now
+    if ( _pimpl->options.probe )
+    {
+      DBG << "unknown repository type, probing" << endl;
+
+      RepoType probedtype;
+      probedtype = probe( *tosave.baseUrlsBegin(), info.path() );
+      if ( tosave.baseUrlsSize() > 0 )
+      {
+        if ( probedtype == RepoType::NONE )
+          ZYPP_THROW(RepoUnknownTypeException());
+        else
+          tosave.setType(probedtype);
+      }
+    }
+
+    progress.set(50);
+
+    // assert the directory exists
+    filesystem::assert_dir(_pimpl->options.knownReposPath);
+
+    Pathname repofile = _pimpl->generateNonExistingName(
+        _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
+    // now we have a filename that does not exists
+    MIL << "Saving repo in " << repofile << endl;
+
+    std::ofstream file(repofile.c_str());
+    if (!file)
+    {
+      // TranslatorExplanation '%s' is a filename
+      ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
+    }
+
+    tosave.dumpAsIniOn(file);
+    tosave.setFilepath(repofile);
+    tosave.setMetadataPath( metadataPath( tosave ) );
+    tosave.setPackagesPath( packagesPath( tosave ) );
+    {
+      // We chould fix the API as we must injet those paths
+      // into the repoinfo in order to keep it usable.
+      RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
+      oinfo.setMetadataPath( metadataPath( tosave ) );
+      oinfo.setPackagesPath( packagesPath( tosave ) );
+    }
+    _pimpl->repos.insert(tosave);
+
+    progress.set(90);
+
+    // check for credentials in Urls
+    bool havePasswords = false;
+    for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() )
+      if ( urlit->hasCredentialsInAuthority() )
+      {
+        havePasswords = true;
+        break;
+      }
+    // save the credentials
+    if ( havePasswords )
+    {
+      media::CredentialManager cm(
+          media::CredManagerOptions(_pimpl->options.rootDir) );
+
+      for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd())
+        if (urlit->hasCredentialsInAuthority())
+          //! \todo use a method calling UI callbacks to ask where to save creds?
+          cm.saveInUser(media::AuthData(*urlit));
+    }
+
+    HistoryLog().addRepository(tosave);
+
+    progress.toMax();
+    MIL << "done" << endl;
+  }
+
+  void RepoManager::addRepositories( const Url &url,
+                                     const ProgressData::ReceiverFnc & progressrcv )
+  {
+    std::list<RepoInfo> repos = readRepoFile(url);
+    for ( std::list<RepoInfo>::const_iterator it = repos.begin();
+          it != repos.end();
+          ++it )
+    {
+      // look if the alias is in the known repos.
+      for_ ( kit, repoBegin(), repoEnd() )
+      {
+        if ( (*it).alias() == (*kit).alias() )
+        {
+          ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
+          ZYPP_THROW(RepoAlreadyExistsException(*it));
+        }
+      }
+    }
+
+    std::string filename = Pathname(url.getPathName()).basename();
+
+    if ( filename == Pathname() )
+    {
+      // TranslatorExplanation '%s' is an URL
+      ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
+    }
+
+    // assert the directory exists
+    filesystem::assert_dir(_pimpl->options.knownReposPath);
+
+    Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
+    // now we have a filename that does not exists
+    MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
+
+    std::ofstream file(repofile.c_str());
+    if (!file)
+    {
+      // TranslatorExplanation '%s' is a filename
+      ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
+    }
+
+    for ( std::list<RepoInfo>::iterator it = repos.begin();
+          it != repos.end();
+          ++it )
+    {
+      MIL << "Saving " << (*it).alias() << endl;
+      it->setFilepath(repofile.asString());
+      it->dumpAsIniOn(file);
+      _pimpl->repos.insert(*it);
+
+      HistoryLog(_pimpl->options.rootDir).addRepository(*it);
+    }
+
+    MIL << "done" << endl;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::removeRepository( const RepoInfo & info,
+                                      const ProgressData::ReceiverFnc & progressrcv)
+  {
+    ProgressData progress;
+    callback::SendReport<ProgressReport> report;
+    progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
+    progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
+
+    MIL << "Going to delete repo " << info.alias() << endl;
+
+    for_( it, repoBegin(), repoEnd() )
+    {
+      // they can be the same only if the provided is empty, that means
+      // the provided repo has no alias
+      // then skip
+      if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
+        continue;
+
+      // TODO match by url
+
+      // we have a matcing repository, now we need to know
+      // where it does come from.
+      RepoInfo todelete = *it;
+      if (todelete.filepath().empty())
+      {
+        ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
+      }
+      else
+      {
+        // figure how many repos are there in the file:
+        std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
+        if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
+        {
+          // easy, only this one, just delete the file
+          if ( filesystem::unlink(todelete.filepath()) != 0 )
+          {
+            // TranslatorExplanation '%s' is a filename
+            ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
+          }
+          MIL << todelete.alias() << " sucessfully deleted." << endl;
+        }
+        else
+        {
+          // there are more repos in the same file
+          // write them back except the deleted one.
+          //TmpFile tmp;
+          //std::ofstream file(tmp.path().c_str());
+
+          // assert the directory exists
+          filesystem::assert_dir(todelete.filepath().dirname());
+
+          std::ofstream file(todelete.filepath().c_str());
+          if (!file)
+          {
+            // TranslatorExplanation '%s' is a filename
+            ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
+          }
+          for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
+                fit != filerepos.end();
+                ++fit )
+          {
+            if ( (*fit).alias() != todelete.alias() )
+              (*fit).dumpAsIniOn(file);
+          }
+        }
+
+        CombinedProgressData subprogrcv(progress, 70);
+        CombinedProgressData cleansubprogrcv(progress, 30);
+        // now delete it from cache
+        if ( isCached(todelete) )
+          cleanCache( todelete, subprogrcv);
+        // now delete metadata (#301037)
+        cleanMetadata( todelete, cleansubprogrcv);
+        _pimpl->repos.erase(todelete);
+        MIL << todelete.alias() << " sucessfully deleted." << endl;
+        HistoryLog(_pimpl->options.rootDir).removeRepository(todelete);
+        return;
+      } // else filepath is empty
+
+    }
+    // should not be reached on a sucess workflow
+    ZYPP_THROW(RepoNotFoundException(info));
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::modifyRepository( const std::string &alias,
+                                      const RepoInfo & newinfo_r,
+                                      const ProgressData::ReceiverFnc & progressrcv )
+  {
+    RepoInfo toedit = getRepositoryInfo(alias);
+    RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
+
+    // check if the new alias already exists when renaming the repo
+    if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
+    {
+      ZYPP_THROW(RepoAlreadyExistsException(newinfo));
+    }
+
+    if (toedit.filepath().empty())
+    {
+      ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
+    }
+    else
+    {
+      // figure how many repos are there in the file:
+      std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
+
+      // there are more repos in the same file
+      // write them back except the deleted one.
+      //TmpFile tmp;
+      //std::ofstream file(tmp.path().c_str());
+
+      // assert the directory exists
+      filesystem::assert_dir(toedit.filepath().dirname());
+
+      std::ofstream file(toedit.filepath().c_str());
+      if (!file)
+      {
+        // TranslatorExplanation '%s' is a filename
+        ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
+      }
+      for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
+            fit != filerepos.end();
+            ++fit )
+      {
+          // if the alias is different, dump the original
+          // if it is the same, dump the provided one
+          if ( (*fit).alias() != toedit.alias() )
+            (*fit).dumpAsIniOn(file);
+          else
+            newinfo.dumpAsIniOn(file);
+      }
+
+      newinfo.setFilepath(toedit.filepath());
+      _pimpl->repos.erase(toedit);
+      _pimpl->repos.insert(newinfo);
+      HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo);
+      MIL << "repo " << alias << " modified" << endl;
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
+                                           const ProgressData::ReceiverFnc & progressrcv )
+  {
+    RepoInfo info;
+    info.setAlias(alias);
+    RepoConstIterator it = _pimpl->repos.find( info );
+    if( it == repoEnd() )
+      ZYPP_THROW(RepoNotFoundException(info));
+    else
+      return *it;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  RepoInfo RepoManager::getRepositoryInfo( const Url & url,
+                                           const url::ViewOption & urlview,
+                                           const ProgressData::ReceiverFnc & progressrcv )
+  {
+    for_( it, repoBegin(), repoEnd() )
+    {
+      for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
+          urlit != (*it).baseUrlsEnd();
+          ++urlit)
+      {
+        if ((*urlit).asString(urlview) == url.asString(urlview))
+          return *it;
+      }
+    }
+    RepoInfo info;
+    info.setBaseUrl(url);
+    ZYPP_THROW(RepoNotFoundException(info));
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  //
+  // Services
+  //
+  ////////////////////////////////////////////////////////////////////////////
+
+  bool RepoManager::serviceEmpty() const
+  { return _pimpl->services.empty(); }
+
+  RepoManager::ServiceSizeType RepoManager::serviceSize() const
+  { return _pimpl->services.size(); }
+
+  RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
+  { return _pimpl->services.begin(); }
+
+  RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
+  { return _pimpl->services.end(); }
+
+  ServiceInfo RepoManager::getService( const std::string & alias ) const
+  {
+    for_( it, serviceBegin(), serviceEnd() )
+      if ( it->alias() == alias )
+        return *it;
+    return ServiceInfo::noService;
+  }
+
+  bool RepoManager::hasService( const std::string & alias ) const
+  {
+    for_( it, serviceBegin(), serviceEnd() )
+      if ( it->alias() == alias )
+        return true;
+    return false;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::addService( const std::string & alias, const Url & url )
+  {
+    addService( ServiceInfo(alias, url) );
+  }
+
+  void RepoManager::addService( const ServiceInfo & service )
+  {
+    assert_alias( service );
+
+    // check if service already exists
+    if ( hasService( service.alias() ) )
+      ZYPP_THROW( ServiceAlreadyExistsException( service ) );
+
+    // Writable ServiceInfo is needed to save the location
+    // of the .service file. Finaly insert into the service list.
+    ServiceInfo toSave( service );
+    _pimpl->saveService( toSave );
+    _pimpl->services.insert( toSave );
+
+    // check for credentials in Url (username:password, not ?credentials param)
+    if ( toSave.url().hasCredentialsInAuthority() )
+    {
+      media::CredentialManager cm(
+          media::CredManagerOptions(_pimpl->options.rootDir) );
+
+      //! \todo use a method calling UI callbacks to ask where to save creds?
+      cm.saveInUser(media::AuthData(toSave.url()));
+    }
+
+    MIL << "added service " << toSave.alias() << endl;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::removeService( const std::string & alias )
+  {
+    MIL << "Going to delete repo " << alias << endl;
+
+    const ServiceInfo & service = getService( alias );
+
+    Pathname location = service.filepath();
+    if( location.empty() )
+    {
+      ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
+    }
+
+    ServiceSet tmpSet;
+    parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
+
+    // only one service definition in the file
+    if ( tmpSet.size() == 1 )
+    {
+      if ( filesystem::unlink(location) != 0 )
+      {
+        // TranslatorExplanation '%s' is a filename
+        ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), location.c_str() )));
+      }
+      MIL << alias << " sucessfully deleted." << endl;
+    }
+    else
+    {
+      filesystem::assert_dir(location.dirname());
+
+      std::ofstream file(location.c_str());
+      if( !file )
+      {
+        // TranslatorExplanation '%s' is a filename
+        ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
+      }
+
+      for_(it, tmpSet.begin(), tmpSet.end())
+      {
+        if( it->alias() != alias )
+          it->dumpAsIniOn(file);
+      }
+
+      MIL << alias << " sucessfully deleted from file " << location <<  endl;
+    }
+
+    // now remove all repositories added by this service
+    RepoCollector rcollector;
+    getRepositoriesInService( alias,
+      boost::make_function_output_iterator(
+          bind( &RepoCollector::collect, &rcollector, _1 ) ) );
+    // cannot do this directly in getRepositoriesInService - would invalidate iterators
+    for_(rit, rcollector.repos.begin(), rcollector.repos.end())
+      removeRepository(*rit);
+  }
+
+  void RepoManager::removeService( const ServiceInfo & service )
+  { removeService(service.alias()); }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::refreshServices()
+  {
+    // copy the set of services since refreshService
+    // can eventually invalidate the iterator
+    ServiceSet services( serviceBegin(), serviceEnd() );
+    for_( it, services.begin(), services.end() )
+    {
+      if ( !it->enabled() )
+        continue;
+
+      try {
+       refreshService(*it);
+      }
+      catch ( const repo::ServicePluginInformalException & e )
+      { ;/* ignore ServicePluginInformalException */ }
+    }
+  }
+
+  void RepoManager::refreshService( const ServiceInfo & service )
+  { refreshService( service.alias() ); }
+
+  void RepoManager::refreshService( const std::string & alias )
+  {
+    ServiceInfo service( getService( alias ) );
+    assert_alias( service );
+    assert_url( service );
+    // NOTE: It might be necessary to modify and rewrite the service info.
+    // Either when probing the type, or when adjusting the repositories
+    // enable/disable state.:
+    bool serviceModified = false;
+    MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
+
+    //! \todo add callbacks for apps (start, end, repo removed, repo added, repo changed)
+
+    // if the type is unknown, try probing.
+    if ( service.type() == repo::ServiceType::NONE )
+    {
+      repo::ServiceType type = probeService( service.url() );
+      if ( type != ServiceType::NONE )
+      {
+        service.setProbedType( type ); // lazy init!
+        serviceModified = true;
+      }
+    }
+
+    // get target distro identifier
+    std::string servicesTargetDistro = _pimpl->options.servicesTargetDistro;
+    if ( servicesTargetDistro.empty() )
+    {
+      servicesTargetDistro = Target::targetDistribution( Pathname() );
+    }
+    DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl;
+
+    // parse it
+    RepoCollector collector(servicesTargetDistro);
+    // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException
+    // which is actually a notification. Using an exception for this
+    // instead of signal/callback is bad. Needs to be fixed here, in refreshServices()
+    // and in zypper.
+    std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
+    try {
+      ServiceRepos repos(service, bind( &RepoCollector::collect, &collector, _1 ));
+    }
+    catch ( const repo::ServicePluginInformalException & e )
+    {
+      /* ignore ServicePluginInformalException and throw later */
+      uglyHack.first = true;
+      uglyHack.second = e;
+    }
+
+    // set service alias and base url for all collected repositories
+    for_( it, collector.repos.begin(), collector.repos.end() )
+    {
+      // if the repo url was not set by the repoindex parser, set service's url
+      Url url;
+
+      if ( it->baseUrlsEmpty() )
+        url = service.url();
+      else
+      {
+        // service repo can contain only one URL now, so no need to iterate.
+        url = *it->baseUrlsBegin();
+      }
+
+      // libzypp currently has problem with separate url + path handling
+      // so just append the path to the baseurl
+      if ( !it->path().empty() )
+      {
+        Pathname path(url.getPathName());
+        path /= it->path();
+        url.setPathName( path.asString() );
+        it->setPath("");
+      }
+
+      // Prepend service alias:
+      it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
+
+      // save the url
+      it->setBaseUrl( url );
+      // set refrence to the parent service
+      it->setService( service.alias() );
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Now compare collected repos with the ones in the system...
+    //
+    RepoInfoList oldRepos;
+    getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
+
+    // find old repositories to remove...
+    for_( it, oldRepos.begin(), oldRepos.end() )
+    {
+      if ( ! foundAliasIn( it->alias(), collector.repos ) )
+      {
+        if ( it->enabled() && ! service.repoToDisableFind( it->alias() ) )
+        {
+          DBG << "Service removes enabled repo " << it->alias() << endl;
+          service.addRepoToEnable( it->alias() );
+          serviceModified = true;
+        }
+        else
+        {
+          DBG << "Service removes disabled repo " << it->alias() << endl;
+        }
+        removeRepository( *it );
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // create missing repositories and modify exising ones if needed...
+    for_( it, collector.repos.begin(), collector.repos.end() )
+    {
+      // Service explicitly requests the repo being enabled?
+      // Service explicitly requests the repo being disabled?
+      // And hopefully not both ;) If so, enable wins.
+      bool beEnabled = service.repoToEnableFind( it->alias() );
+      bool beDisabled = service.repoToDisableFind( it->alias() );
+
+      // Make sure the service repo is created with the
+      // appropriate enable
+      if ( beEnabled ) it->setEnabled(true);
+      if ( beDisabled ) it->setEnabled(false);
+
+      if ( beEnabled )
+      {
+        // Remove from enable request list.
+        // NOTE: repoToDisable is handled differently.
+        //       It gets cleared on each refresh.
+        service.delRepoToEnable( it->alias() );
+        serviceModified = true;
+      }
+
+      RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
+      if ( oldRepo == oldRepos.end() )
+      {
+        // Not found in oldRepos ==> a new repo to add
+
+        // At that point check whether a repo with the same alias
+        // exists outside this service. Maybe forcefully re-alias
+        // the existing repo?
+        DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
+        addRepository( *it );
+
+        // save repo credentials
+        // ma@: task for modifyRepository?
+      }
+      else
+      {
+        // ==> an exising repo to check
+        bool oldRepoModified = false;
+
+        // changed enable?
+        if ( beEnabled )
+        {
+          if ( ! oldRepo->enabled() )
+          {
+            DBG << "Service repo " << it->alias() << " gets enabled" << endl;
+            oldRepo->setEnabled( true );
+            oldRepoModified = true;
+          }
+          else
+          {
+            DBG << "Service repo " << it->alias() << " stays enabled" << endl;
+          }
+        }
+        else if ( beDisabled )
+        {
+          if ( oldRepo->enabled() )
+          {
+            DBG << "Service repo " << it->alias() << " gets disabled" << endl;
+            oldRepo->setEnabled( false );
+            oldRepoModified = true;
+          }
+          else
+          {
+            DBG << "Service repo " << it->alias() << " stays disabled" << endl;
+          }
+        }
+        else
+        {
+          DBG << "Service repo " << it->alias() << " stays " <<  (oldRepo->enabled()?"enabled":"disabled") << endl;
+        }
+
+        // changed url?
+        // service repo can contain only one URL now, so no need to iterate.
+        if ( oldRepo->url() != it->url() )
+        {
+          DBG << "Service repo " << it->alias() << " gets new URL " << it->url() << endl;
+          oldRepo->setBaseUrl( it->url() );
+          oldRepoModified = true;
+        }
+
+        // save if modified:
+        if ( oldRepoModified )
+        {
+          modifyRepository( oldRepo->alias(), *oldRepo );
+        }
+      }
+    }
+
+    // Unlike reposToEnable, reposToDisable is always cleared after refresh.
+    if ( ! service.reposToDisableEmpty() )
+    {
+      service.clearReposToDisable();
+      serviceModified = true;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // save service if modified:
+    if ( serviceModified )
+    {
+      // write out modified service file.
+      modifyService( service.alias(), service );
+    }
+
+    if ( uglyHack.first )
+    {
+      throw( uglyHack.second ); // intentionally not ZYPP_THROW
+    }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & newService)
+  {
+    MIL << "Going to modify service " << oldAlias << endl;
+
+    // we need a writable copy to link it to the file where
+    // it is saved if we modify it
+    ServiceInfo service(newService);
+
+    if ( service.type() == ServiceType::PLUGIN )
+    {
+        MIL << "Not modifying plugin service '" << oldAlias << "'" << endl;
+        return;
+    }
+
+    const ServiceInfo & oldService = getService(oldAlias);
+
+    Pathname location = oldService.filepath();
+    if( location.empty() )
+    {
+      ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
+    }
+
+    // remember: there may multiple services being defined in one file:
+    ServiceSet tmpSet;
+    parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
+
+    filesystem::assert_dir(location.dirname());
+    std::ofstream file(location.c_str());
+    for_(it, tmpSet.begin(), tmpSet.end())
+    {
+      if( *it != oldAlias )
+        it->dumpAsIniOn(file);
+    }
+    service.dumpAsIniOn(file);
+    file.close();
+    service.setFilepath(location);
+
+    _pimpl->services.erase(oldAlias);
+    _pimpl->services.insert(service);
+
+    // changed properties affecting also repositories
+    if( oldAlias != service.alias()                    // changed alias
+        || oldService.enabled() != service.enabled()   // changed enabled status
+      )
+    {
+      std::vector<RepoInfo> toModify;
+      getRepositoriesInService(oldAlias, std::back_inserter(toModify));
+      for_( it, toModify.begin(), toModify.end() )
+      {
+        if (oldService.enabled() && !service.enabled())
+          it->setEnabled(false);
+        else if (!oldService.enabled() && service.enabled())
+        {
+          //! \todo do nothing? the repos will be enabled on service refresh
+          //! \todo how to know the service needs a (auto) refresh????
+        }
+        else
+          it->setService(service.alias());
+        modifyRepository(it->alias(), *it);
+      }
+    }
+
+    //! \todo refresh the service automatically if url is changed?
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  repo::ServiceType RepoManager::probeService( const Url &url ) const
+  {
+    try
+    {
+      MediaSetAccess access(url);
+      if ( access.doesFileExist("/repo/repoindex.xml") )
+        return repo::ServiceType::RIS;
+    }
+    catch ( const media::MediaException &e )
+    {
+      ZYPP_CAUGHT(e);
+      // TranslatorExplanation '%s' is an URL
+      RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
+      enew.remember(e);
+      ZYPP_THROW(enew);
+    }
+    catch ( const Exception &e )
+    {
+      ZYPP_CAUGHT(e);
+      // TranslatorExplanation '%s' is an URL
+      Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
+      enew.remember(e);
+      ZYPP_THROW(enew);
+    }
+
+    return repo::ServiceType::NONE;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
+  {
+    return str << *obj._pimpl;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/RepoManager.h b/zypp/RepoManager.h
new file mode 100644 (file)
index 0000000..5d4119f
--- /dev/null
@@ -0,0 +1,712 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoManager.h
+ *
+*/
+#ifndef ZYPP_REPOMANAGER_H
+#define ZYPP_REPOMANAGER_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Iterator.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/ZConfig.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/repo/RepoType.h"
+#include "zypp/repo/ServiceType.h"
+#include "zypp/ServiceInfo.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/ProgressData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+   /**
+    * Parses \a repo_file and returns a list of \ref RepoInfo objects
+    * corresponding to repositories found within the file.
+    *
+    * \param repo_file Valid URL of the repo file.
+    * \return found list<RepoInfo>
+    *
+    * \throws MediaException If the access to the url fails
+    * \throws ParseException If the file parsing fails
+    * \throws Exception On other errors.
+    */
+   std::list<RepoInfo> readRepoFile(const Url & repo_file);
+
+  /**
+   * Repo manager settings.
+   * Settings default to ZYpp global settings.
+   */
+  struct RepoManagerOptions
+  {
+    /** Default ctor following \ref ZConfig global settings.
+     * If an optional \c root_r directory is given, all paths  will
+     * be prefixed accordingly.
+     * \code
+     *    root_r\repoCachePath
+     *          \repoRawCachePath
+     *          \repoSolvCachePath
+     *          \repoPackagesCachePath
+     *          \knownReposPath
+     * \endcode
+     */
+    RepoManagerOptions( const Pathname & root_r = Pathname() );
+
+    /** Test setup adjusting all paths to be located below one \c root_r directory.
+     * \code
+     *    root_r\          - repoCachePath
+     *          \raw       - repoRawCachePath
+     *          \solv      - repoSolvCachePath
+     *          \packages  - repoPackagesCachePath
+     *          \repos.d   - knownReposPath
+     * \endcode
+     */
+    static RepoManagerOptions makeTestSetup( const Pathname & root_r );
+
+    Pathname repoCachePath;
+    Pathname repoRawCachePath;
+    Pathname repoSolvCachePath;
+    Pathname repoPackagesCachePath;
+    Pathname knownReposPath;
+    Pathname knownServicesPath;
+    Pathname pluginsPath;
+    bool probe;
+    /**
+     * Target distro ID to be used when refreshing repo index services.
+     * Repositories not maching this ID will be skipped/removed.
+     *
+     * The value is initialized upon construction to
+     * \ref Target::targetDistribution() if the Target is already initialized,
+     * otherwise the value is initially empty.
+     *
+     * If empty, no repositories contained in the index will be skipped.
+     */
+    std::string servicesTargetDistro;
+
+    /** remembers root_r value for later use */
+    Pathname rootDir;
+  };
+
+
+
+  /**
+   * \short creates and provides information about known sources.
+   *
+   */
+  class RepoManager
+  {
+    friend std::ostream & operator<<( std::ostream & str, const RepoManager & obj );
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+    /** ServiceInfo typedefs */
+    typedef std::set<ServiceInfo> ServiceSet;
+    typedef ServiceSet::const_iterator ServiceConstIterator;
+    typedef ServiceSet::size_type ServiceSizeType;
+
+    /** RepoInfo typedefs */
+    typedef std::set<RepoInfo> RepoSet;
+    typedef RepoSet::const_iterator RepoConstIterator;
+    typedef RepoSet::size_type RepoSizeType;
+
+  public:
+   RepoManager( const RepoManagerOptions &options = RepoManagerOptions() );
+   /** Dtor */
+    ~RepoManager();
+
+    enum RawMetadataRefreshPolicy
+    {
+      RefreshIfNeeded,
+      RefreshForced,
+      RefreshIfNeededIgnoreDelay
+    };
+
+    enum CacheBuildPolicy
+    {
+      BuildIfNeeded,
+      BuildForced
+    };
+
+    enum RepoRemovePolicy
+    {
+
+    };
+
+    /** \name Known repositories.
+     *
+     * The known repositories are read from
+     * \ref RepoManagerOptions::knownReposPath passed on the Ctor.
+     * Which defaults to ZYpp global settings.
+     */
+   //@{
+    bool repoEmpty() const;
+    RepoSizeType repoSize() const;
+    RepoConstIterator repoBegin() const;
+    RepoConstIterator repoEnd() const;
+
+    /** List of known repositories. */
+    std::list<RepoInfo> knownRepositories() const
+    { return std::list<RepoInfo>(repoBegin(),repoEnd()); }
+
+    /** Find RepoInfo by alias or return \ref RepoInfo::noRepo. */
+    RepoInfo getRepo( const std::string & alias ) const;
+    /** \overload Take alias from RepoInfo. */
+    RepoInfo getRepo( const RepoInfo & info_r ) const
+    { return getRepo( info_r.alias() ); }
+
+    /** Return whether there is a known repository for \c alias. */
+    bool hasRepo( const std::string & alias ) const;
+    /** \overload Take alias from RepoInfo. */
+    bool hasRepo( const RepoInfo & info_r ) const
+    { return hasRepo( info_r.alias() ); }
+
+    /** Some stupid string but suitable as alias for your url if nothing better is available.
+     * Something like \c "http-download.opensuse.org-83df67e5"
+    */
+    static std::string makeStupidAlias( const Url & url_r = Url() );
+   //@}
+
+   /**
+    * \short Status of local metadata
+    */
+    RepoStatus metadataStatus( const RepoInfo &info ) const;
+
+    /**
+     * Possibly return state of checkIfRefreshMEtadata function
+     */
+    enum RefreshCheckStatus {
+      REFRESH_NEEDED,  /**< refresh is needed */
+      REPO_UP_TO_DATE, /**< repository not changed */
+      REPO_CHECK_DELAYED     /**< refresh is delayed due to settings */
+    };
+
+    /**
+     * Checks whether to refresh metadata for specified repository and url.
+     * <p>
+     * The need for refresh is evaluated according to the following conditions,
+     * in that order:
+     * <ul>
+     * <li>the refresh policy (refresh may be forced)
+     * <li>the repo.refresh.delay ZConfig value compared to the difference between
+     *   cached index file timestamp and actual time
+     * <li>the timestamp of cached repo index file compared to the remote
+     *   index file timestamp.
+     * </ul>
+     * <p>
+     * This method checks the status against the specified url only. If more
+     * baseurls are defined for in the RepoInfo, each one must be check
+     * individually. Example:
+     *
+     * <code>
+     *
+     * RepoInfo info;
+     * // try urls one by one
+     * for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin();
+     *       it != info.baseUrlsEnd(); ++it )
+     * {
+     *   try
+     *   {
+     *     // check whether to refresh metadata
+     *     // if the check fails for this url, it throws, so another url will be checked
+     *     if (checkIfToRefreshMetadata(info, *it, policy)!=RepoInfo::REFRESH_NEEDED)
+     *       return;
+     *
+     *     // do the actual refresh
+     *   }
+     *   catch (const Exception & e)
+     *   {
+     *     ZYPP_CAUGHT(e);
+     *     ERR << *it << " doesn't look good. Trying another url." << endl;
+     *   }
+     * } // for all urls
+     *
+     * handle("No more URLs.");
+     *
+     * </code>
+     *
+     * \param info
+     * \param url
+     * \param policy
+     * \return state of repository
+     * \see RefreshCheckStatus
+     * \throws RepoUnknownTypeException
+     * \throws repo::RepoNoAliasException if can't figure an alias
+     * \throws Exception on unknown error
+     *
+     */
+    RefreshCheckStatus checkIfToRefreshMetadata( const RepoInfo &info,
+                                   const Url &url,
+                                   RawMetadataRefreshPolicy policy = RefreshIfNeeded);
+
+    /**
+     * \short Path where the metadata is downloaded and kept
+     *
+     * Given a repoinfo, tells where \ref RepoManager will download
+     * and keep the raw metadata.
+     *
+     * \param info Repository information
+     *
+     * \throws repo::RepoNoAliasException if can't figure an alias
+     */
+    Pathname metadataPath( const RepoInfo &info ) const;
+
+
+    /**
+     * \short Path where the rpm packages are downloaded and kept
+     *
+     * Given a repoinfo, tells where \ref RepoProvidePackage will download
+     * and keep the .rpm files.
+     *
+     * \param info Repository information
+     *
+     * \throws repo::RepoNoAliasException if can't figure an alias
+     */
+    Pathname packagesPath( const RepoInfo &info ) const;
+
+
+   /**
+    * \short Refresh local raw cache
+    *
+    * Will try to download the metadata
+    *
+    * In case of falure the metadata remains
+    * as it was before.
+    *
+    * \throws repo::RepoNoUrlException if no urls are available.
+    * \throws repo::RepoNoAliasException if can't figure an alias
+    * \throws repo::RepoUnknownTypeException if the metadata is unknown
+    * \throws repo::RepoException if the repository is invalid
+    *         (no valid metadata found at any of baseurls)
+    */
+   void refreshMetadata( const RepoInfo &info,
+                         RawMetadataRefreshPolicy policy = RefreshIfNeeded,
+                         const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Clean local metadata
+    *
+    * Empty local metadata.
+    *
+    * \throws repo::RepoNoAliasException if can't figure an alias
+    * \throws Exception on unknown error.
+    */
+   void cleanMetadata( const RepoInfo &info,
+                       const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Clean local package cache
+    *
+    * Empty local directory with downloaded packages
+    *
+    * \throws repo::RepoNoAliasException if can't figure an alias
+    * \throws Exception on unknown error.
+    */
+   void cleanPackages( const RepoInfo &info,
+                       const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Status of metadata cache
+    */
+    RepoStatus cacheStatus( const RepoInfo &info ) const;
+
+   /**
+    * \short Refresh local cache
+    *
+    * Will try to build the cache from local metadata.
+    *
+    * If the cache exists it will be overwriten.
+    *
+    * \note the local metadata must be valid.
+    *
+    * \throws repo::RepoNoAliasException if can't figure
+    *     an alias to look in cache
+    * \throws repo::RepoMetadataException if the metadata
+    *     is not enough to build a cache (empty, incorrect, or
+    *     refresh needed)
+    * \throws repo::RepoUnknownTypeException
+    * \throws parser::ParseException if parser encounters an error.
+    * \throws Exception on unknown error.
+    */
+   void buildCache( const RepoInfo &info,
+                    CacheBuildPolicy policy = BuildIfNeeded,
+                    const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short clean local cache
+    *
+    * Clean the cached version of the metadata
+    *
+    * \note the local metadata must be valid.
+    *
+    * \throws repo::RepoNoAliasException if can't figure an alias to look in cache
+    * \throws cache::CacheRecordNotFoundException if the cache could not be
+    *     cleaned because of repository record not found.
+    * \throws Exception on unknown error.
+    */
+   void cleanCache( const RepoInfo &info,
+                    const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Whether a repository exists in cache
+    *
+    * \param RepoInfo to be checked.
+    */
+    bool isCached( const RepoInfo &info ) const;
+
+
+    /**
+    * \short Load resolvables into the pool
+    *
+    * Creating from cache requires that the repository is
+    * refreshed (metadata downloaded) and cached
+    *
+    * \throws repo::RepoNoAliasException if can't figure an alias to look in cache
+    * \throw RepoNotCachedException When the source is not cached.
+    */
+   void loadFromCache( const RepoInfo &info,
+                       const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * Remove any subdirectories of cache directories which no longer belong
+    * to any of known repositories.
+    *
+    * These can be temporary directories left by interrupted refresh,
+    * or dirs left after changing .repo files outside of libzypp.
+    */
+   void cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Probe repo metadata type.
+    *
+    * The location to probe consists of the base \a url (you may think of it as
+    * a mountpoint) and the \a path to the repository on the mounted media
+    * (ususally \c / ).
+    */
+   repo::RepoType probe( const Url & url, const Pathname & path ) const;
+   /**
+    * \overload Using the default path \c "/".
+    */
+   repo::RepoType probe( const Url & url ) const;
+
+
+   /**
+    * \short Adds a repository to the list of known repositories.
+    *
+    *
+    *
+    * \throws repo::RepoAlreadyExistsException If the repo clash some
+    *         unique attribute like alias
+    * \throws RepoUnknownType
+    *         If RepoManagerOptions::probe is true
+    *         and repository type can't be determined.
+    * \throws RepoException
+    *         If RepoManagerOptions::probe is true and access to the url fails.
+    * \throws Exception On other errors.
+    */
+   void addRepository( const RepoInfo &info,
+                       const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+   /**
+    * \short Adds repositores from a repo file to the list of known repositories.
+    * \param url Url of the repo file
+    *
+    * \throws repo::RepoAlreadyExistsException If the repo clash some
+    *         unique attribute like alias
+    * \throws MediaException If the access to the url fails
+    * \throws ParseException If the file parsing fails
+    * \throws RepoUnknownType If repository type can't be determined
+    * \throws RepoException ON other repository related errors
+    * \throws Exception On other errors.
+    */
+    void addRepositories( const Url &url,
+                         const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+    /**
+     * \short Remove the best matching repository from known repos list
+     *
+     * \throws RepoNotFoundException If no repo match
+     */
+    void removeRepository( const RepoInfo & info,
+                           const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+    /**
+     * \short Modify repository attributes
+     *
+     * \throws RepoAlreadyExistsException if the alias specified in newinfo
+     *         is already used by another repository
+     * \throws RepoNotFoundException If no repo match
+     * \throws ParseException If the file parsing fails
+     * \throws Exception On other errors.
+     */
+    void modifyRepository( const std::string &alias,
+                           const RepoInfo & newinfo,
+                           const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+    /** \overload Take alias from RepoInfo. */
+    void modifyRepository( const RepoInfo & newinfo,
+                           const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() )
+    { modifyRepository( newinfo.alias(), newinfo, progressrcv ); }
+
+    /**
+     * \short Find a matching repository info
+     *
+     * \note if multiple repositories incorrectly share the
+     * same alias, the first one found will be returned.
+     *
+     * \param alias Repository alias
+     * \param progressrcv Progress reporting function
+     * \return RepoInfo of the found repository
+     * \throws RepoNotFoundException If no repo match the alias
+     * \throws ParseException If the file parsing fails
+     * \throws Exception On other errors.
+     */
+    RepoInfo getRepositoryInfo( const std::string &alias,
+                                const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+    /**
+     * \short Find repository info by URL.
+     *
+     * \param url URL to find.
+     * \param urlview url::ViewOption to influence URL matching.
+     * \param progressrcv Progress receiver function.
+     * \return RepoInfo of the found repository.
+     *
+     * \note if multpile repositories incorrectly share the
+     * same URL, the first one found will be returned.
+     *
+     * \note the string representation of the URLs are compared.
+     *       The \a urlview can be used to influence which
+             parts of the URL are to be compared.
+     *
+     * \throws RepoNotFoundException If no repo match
+     * \throws ParseException If the file parsing fails
+     * \throws Exception On other errors.
+     */
+    RepoInfo getRepositoryInfo( const Url & url,
+                                const url::ViewOption & urlview = url::ViewOption::DEFAULTS,
+                                const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() );
+
+
+    /** \name Known services.
+     *
+     * The known services are read from
+     * \ref RepoManagerOptions::knownServicesPath passed on the Ctor.
+     * Which defaults to ZYpp global settings.
+     */
+    //@{
+    /**
+     * Gets true if no service is in RepoManager (so no one in specified location)
+     *
+     * \return true if any ServiceInfo is in RepoManager
+     */
+    bool serviceEmpty() const;
+
+    /**
+     * Gets count of service in RepoManager (in specified location)
+     *
+     * \return count of service
+     */
+    ServiceSizeType serviceSize() const;
+
+    /**
+     * Iterator to first service in internal storage.
+     * \note Iterator is immutable, so you cannot change pointed ServiceInfo
+     * \return Iterator to first service
+     */
+    ServiceConstIterator serviceBegin() const;
+
+    /**
+     * Iterator to place behind last service in internal storage.
+     * \return iterator to end
+     */
+    ServiceConstIterator serviceEnd() const;
+
+    /** List of known services. */
+    std::list<ServiceInfo> knownServices() const
+    { return std::list<ServiceInfo>(serviceBegin(),serviceEnd()); }
+
+    /**
+     * \short Finds ServiceInfo by alias or return \ref ServiceInfo::noService
+     *
+     * \param alias unique identifier of service
+     * \return information about service
+     */
+    ServiceInfo getService( const std::string & alias ) const;
+
+    /** Return whether there is a known service for \c alias. */
+    bool hasService( const std::string & alias ) const;
+    //@}
+
+    /**
+     * \short Probe the type or the service.
+     */
+    repo::ServiceType probeService( const Url &url ) const;
+
+    /**
+     * Adds new service by it's alias and url
+     *
+     * \param alias unique identifier of the service
+     * \param url url to service
+     *
+     * \throws FIXME RepoAlreadyExistException and as reponame is service name
+     */
+    void addService( const std::string & alias, const Url& url );
+
+    /**
+     * Adds new service
+     *
+     * \param service service info
+     *
+     * \throws FIXME RepoAlreadyExistException and as reponame is service name
+     */
+    void addService( const ServiceInfo & service );
+
+    /**
+     * Removes service specified by its name
+     *
+     * \param alias unique indientifier of the service to remove
+     *
+     * \throws RepoException if service is not found or file with ServiceInfo cannot be deleted
+     * \throws Exception if file contain more services and rewrite file failed
+     */
+    void removeService( const std::string & alias );
+    /** \overload Take alias from ServiceInfo */
+    void removeService( const ServiceInfo & service );
+
+
+    /**
+     * Refreshes all enabled services.
+     *
+     * \see refreshService(ServiceInfo)
+     */
+    void refreshServices();
+
+    /**
+     * Refresh specific service.
+     *
+     * \param alias unique indientifier of the service to refresh
+     *
+     * \throws RepoException if service is not found.
+     * \throws MediaException If there's a problem downloading the repo index file.
+     */
+    void refreshService( const std::string & alias );
+    /** \overload Take alias from ServiceInfo */
+    void refreshService( const ServiceInfo & service );
+
+    /**
+     * Modifies service file (rewrites it with new values) and underlying
+     * repositories if needed.
+     *
+     * Modifications of a service can lead to rewrite of all .repo files of
+     * contained repositories. Particularily, disabling a service (changing
+     * ServiceInfo::enabled() from true to false) will disable all contained
+     * repositories. Renaming of a service will modify the "service" key
+     * of all contained repositories.
+     *
+     * \param oldAlias Old alias of the service
+     * \param service ServiceInfo object containing new data
+     *
+     * \throws RepoException if sservice with oldAlias is not known
+     * \throws Exception if have problems with files
+     */
+    void modifyService( const std::string & oldAlias, const ServiceInfo & service );
+    /** \overload Take alias from ServiceInfo. */
+    void modifyService( const ServiceInfo & service )
+    { modifyService( service.alias(), service ); }
+
+  private:
+    /**
+     * Functor thats filter RepoInfo by service which it belongs to.
+     */
+    struct MatchServiceAlias
+    {
+      public:
+        MatchServiceAlias( const std::string & alias_ ) : alias(alias_) {}
+        bool operator()( const RepoInfo & info ) const
+        { return info.service() == alias; }
+      private:
+        std::string alias;
+    };
+
+  public:
+
+    /**
+     * fill to output iterator repositories in service name. This output iterator can perform
+     * any action on with Repo or service Container, because it is sets and it isn't dynamic recreate.
+     *
+     * \note Don't use this function with RepoManager::removeRepository(), it will lead to segfaults
+     *       due to invalidated internal iterators. FIXME can this be solved (using STL) so that this
+     *       warning would not be needed?
+     *
+     * \param alias service alias
+     * \param out output iterator which get all the repositories belonging to
+     *   specified service
+     *
+     * example how set priority for each RepoInfo in this service:
+     * \code
+     * //functor
+     * class ChangePriority
+     * {
+     * private:
+     *   int priority;
+     * public:
+     *   ChangePriority(int prio) : priority(prio) {}
+     *   // missing rewrite priority back via RepoManager::modifyRepo
+     *   void doIt( RepoInfo info ) { info.setPriority(priority); }
+     * }
+     *
+     * //somewhere in code
+     * ChangePriority changer(10);
+     * getRepositoriesInService(name,
+     *   boost::make_function_output_iterator(
+     *     bind(&ChangePriority::doIt, &changer, _1)));
+     * \endcode
+     */
+    template<typename OutputIterator>
+    void getRepositoriesInService( const std::string & alias,
+                                   OutputIterator out ) const
+    {
+      MatchServiceAlias filter(alias);
+
+      std::copy( boost::make_filter_iterator( filter, repoBegin(), repoEnd() ),
+                 boost::make_filter_iterator( filter, repoEnd(), repoEnd() ),
+                 out);
+    }
+
+  protected:
+    RepoStatus rawMetadataStatus( const RepoInfo &info );
+    void setCacheStatus( const RepoInfo &info, const RepoStatus &status );
+
+    /**
+     * Update timestamp of repository index file for the specified repository \a info.
+     * Used in \ref checkIfToRefreshMetadata() for repo.refresh.delay feature.
+     */
+    void touchIndexFile(const RepoInfo & info);
+
+  public:
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoManager Stream output */
+  std::ostream & operator<<( std::ostream & str, const RepoManager & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP2_REPOMANAGER_H
diff --git a/zypp/RepoStatus.cc b/zypp/RepoStatus.cc
new file mode 100644 (file)
index 0000000..7ff4241
--- /dev/null
@@ -0,0 +1,177 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoStatus.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoStatus::Impl
+  //
+  /** RepoStatus implementation. */
+  struct RepoStatus::Impl
+  {
+
+  public:
+
+    string checksum;
+    Date timestamp;
+
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoStatus::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const RepoStatus::Impl & obj )
+  {
+    return str << obj.checksum << " " << (time_t) obj.timestamp;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoStatus
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : RepoStatus::RepoStatus
+  //   METHOD TYPE : Ctor
+  //
+  RepoStatus::RepoStatus()
+    : _pimpl( new Impl() )
+  {}
+
+  RepoStatus::RepoStatus( const Pathname &path )
+    : _pimpl( new Impl() )
+  {
+      PathInfo info(path);
+      if ( info.isExist() )
+      {
+        _pimpl->timestamp = Date(info.mtime());
+        if ( info.isFile() )
+          _pimpl->checksum = filesystem::sha1sum(path);
+        else // non files
+          _pimpl->checksum = str::numstring(info.mtime());
+      }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : RepoStatus::~RepoStatus
+  //   METHOD TYPE : Dtor
+  //
+  RepoStatus::~RepoStatus()
+  {}
+
+  RepoStatus RepoStatus::fromCookieFile( const Pathname &cookiefile )
+  {
+    std::ifstream file(cookiefile.c_str());
+    if (!file) {
+      WAR << "No cookie file " << cookiefile << endl;
+      return RepoStatus();
+    }
+
+    RepoStatus status;
+    std::string buffer;
+    file >> buffer;
+    status.setChecksum(buffer);
+    file >> buffer;
+    status.setTimestamp(Date(str::strtonum<time_t>(buffer)));
+    return status;
+  }
+
+  void RepoStatus::saveToCookieFile( const Pathname &cookiefile ) const
+  {
+    std::ofstream file(cookiefile.c_str());
+    if (!file) {
+      ZYPP_THROW (Exception( "Can't open " + cookiefile.asString() ) );
+    }
+    file << this->checksum() << " " << (int) this->timestamp() << endl << endl;
+    file.close();
+  }
+
+  bool RepoStatus::empty() const
+  {
+    return _pimpl->checksum.empty();
+  }
+
+  RepoStatus & RepoStatus::setChecksum( const string &checksum )
+  {
+    _pimpl->checksum = checksum;
+    return *this;
+  }
+
+  RepoStatus & RepoStatus::setTimestamp( const Date &timestamp )
+  {
+    _pimpl->timestamp = timestamp;
+    return *this;
+  }
+
+  string RepoStatus::checksum() const
+  { return _pimpl->checksum; }
+
+  Date RepoStatus::timestamp() const
+  { return _pimpl->timestamp; }
+
+  RepoStatus operator&&( const RepoStatus & lhs, const RepoStatus & rhs )
+  {
+    if ( lhs.empty() )
+      return rhs;
+    if ( rhs.empty() )
+      return lhs;
+
+    std::string lchk( lhs.checksum() );
+    std::string rchk( rhs.checksum() );
+    // order strings to assert && is kommutativ
+    stringstream ss( lchk < rchk ? lchk+rchk : rchk+lchk );
+
+    RepoStatus result;
+    result.setChecksum( CheckSum::sha1(ss).checksum() );
+    result.setTimestamp( lhs.timestamp() < rhs.timestamp() ? rhs.timestamp() : lhs.timestamp() );
+    return result;
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const RepoStatus & obj )
+  {
+    return str << *obj._pimpl;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/RepoStatus.h b/zypp/RepoStatus.h
new file mode 100644 (file)
index 0000000..5e4caeb
--- /dev/null
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/RepoStatus.h
+ *
+*/
+#ifndef ZYPP2_REPOSTATUS_H
+#define ZYPP2_REPOSTATUS_H
+
+#include <iosfwd>
+#include "zypp/base/PtrTypes.h"
+#include "zypp/CheckSum.h"
+#include "zypp/Date.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : RepoStatus
+  //
+  /**
+   * \short Local facts about a repository
+   * This class represents the status of a
+   * repository on the system.
+   *
+   * Anything that is not provided on the metadata
+   * files, like the timestamp of the downloaded
+   * metadata, and its checksum.
+   */
+  class RepoStatus
+  {
+    friend std::ostream & operator<<( std::ostream & str, const RepoStatus & obj );
+
+  public:
+
+    /**
+     * reads the status from a file which contains the
+     * checksum and timestamp in each line.
+     *
+     * \returns An empty \ref RepoStatus if the file does not
+     * exist or is not readable.
+     */
+    static RepoStatus fromCookieFile( const Pathname &path );
+
+    /**
+     * save the status information to a cookie file
+     * \throws Exception if the file can't be saved
+     */
+    void saveToCookieFile( const Pathname &path ) const;
+
+    /**
+     * Checksum of the repository.
+     * Usually the checksum of the index, but any
+     * checksum that changes when the repository changes
+     * in any way is sufficient.
+     */
+    std::string checksum() const;
+
+    /**
+     * timestamp of the repository. If the repository
+     * changes, it has to be updated as well with the
+     * new timestamp.
+     */
+    Date timestamp() const;
+
+    /**
+     * \short Is the status empty?
+     *
+     * An empty status means that the status
+     * was not calculated.
+     */
+    bool empty() const;
+
+    /**
+     * set the repository checksum \see checksum
+     * \param checksum
+     */
+    RepoStatus & setChecksum( const std::string &checksum );
+
+    /**
+     * set the repository timestamp \see timestamp
+     * \param timestamp
+     */
+    RepoStatus & setTimestamp( const Date &timestamp );
+
+    /** Implementation  */
+    class Impl;
+
+  public:
+    /** Default ctor */
+    RepoStatus();
+
+    /**
+     * \short Status from a single file
+     * As most repository state is represented
+     * by the status of the index file, you can
+     * construct the status from a file.
+     *
+     * \note construct from a non existing
+     * file will result in an empty status
+     *
+     * \note construct from a directory, the
+     * directories mtime will be also used as
+     * checksum.
+     *
+     * \todo Add recursive option for dirs so we finaly get
+     * the same as \ref parser::plaindir::dirStatus and can
+     * unify both.
+     */
+    RepoStatus( const Pathname &file );
+
+    /** Dtor */
+    ~RepoStatus();
+
+  public:
+
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoStatus Stream output */
+  std::ostream & operator<<( std::ostream & str, const RepoStatus & obj );
+
+  /**
+   * combines 2 repostatus with a checksum based on both
+   * checksums and the newest timestamp
+   */
+  RepoStatus operator&&( const RepoStatus &lhs, const RepoStatus &rhs );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP2_REPOSTATUS_H
diff --git a/zypp/Repository.cc b/zypp/Repository.cc
new file mode 100644 (file)
index 0000000..d67ff82
--- /dev/null
@@ -0,0 +1,328 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Repository.cc
+ *
+*/
+#include <climits>
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/AutoDispose.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/Repository.h"
+#include "zypp/sat/Pool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    const Repository Repository::noRepository;
+
+    const std::string & Repository::systemRepoAlias()
+    { return sat::detail::PoolImpl::systemRepoAlias(); }
+
+    /////////////////////////////////////////////////////////////////
+
+    ::_Repo * Repository::get() const
+    { return myPool().getRepo( _id ); }
+
+#define NO_REPOSITORY_RETURN( VAL ) \
+    ::_Repo * _repo( get() ); \
+    if ( ! _repo ) return VAL
+
+#define NO_REPOSITORY_THROW( VAL ) \
+    ::_Repo * _repo( get() ); \
+    if ( ! _repo ) ZYPP_THROW( VAL )
+
+    bool Repository::isSystemRepo() const
+    {
+       NO_REPOSITORY_RETURN( false );
+       return myPool().isSystemRepo( _repo );
+    }
+
+    std::string Repository::alias() const
+    {
+      NO_REPOSITORY_RETURN( std::string() );
+      if ( ! _repo->name )
+        return std::string();
+      return _repo->name;
+    }
+
+    std::string Repository::name() const
+    { return info().name(); }
+
+    int Repository::satInternalPriority() const
+    {
+      NO_REPOSITORY_RETURN( INT_MIN );
+      return _repo->priority;
+    }
+
+    int Repository::satInternalSubPriority() const
+    {
+      NO_REPOSITORY_RETURN( INT_MIN );
+      return _repo->subpriority;
+    }
+
+
+    zypp::Date Repository::generatedTimestamp() const
+    {
+      NO_REPOSITORY_RETURN( 0 );
+      sat::LookupRepoAttr q( sat::SolvAttr::repositoryTimestamp, *this );
+      return( q.empty() ? 0 : q.begin().asUnsigned() );
+    }
+
+    zypp::Date Repository::suggestedExpirationTimestamp() const
+    {
+      NO_REPOSITORY_RETURN( 0 );
+      Date generated = generatedTimestamp();
+      if ( ! generated )
+        return 0; // do not calculate over a missing generated timestamp
+
+      sat::LookupRepoAttr q( sat::SolvAttr::repositoryExpire, *this );
+      if ( q.empty() )
+        return 0;
+
+      return generated + q.begin().asUnsigned();
+    }
+
+    Repository::Keywords Repository::keywords() const
+    {
+      NO_REPOSITORY_RETURN( Keywords() );
+      return Keywords( sat::SolvAttr::repositoryKeywords, *this, sat::LookupAttr::REPO_ATTR );
+    }
+
+    bool Repository::maybeOutdated() const
+    {
+      NO_REPOSITORY_RETURN( false );
+      // system repo is not mirrored
+      if ( isSystemRepo() )
+        return false;
+
+      Date suggested = suggestedExpirationTimestamp();
+
+      // if no data, don't suggest
+      if ( ! suggested )
+        return false;
+
+      return suggestedExpirationTimestamp() < Date::now();
+    }
+
+    bool Repository::providesUpdatesFor( const std::string &key ) const
+    {
+      NO_REPOSITORY_RETURN( false );
+
+      for_( it,
+            updatesProductBegin(),
+            updatesProductEnd() )
+      {
+        // FIXME implement real CPE matching here
+        // someday
+        if ( key == it.cpeId() )
+          return true;
+      }
+
+      return false;
+    }
+
+    bool Repository::isUpdateRepo() const
+    {
+      NO_REPOSITORY_RETURN( false );
+      return ( updatesProductBegin() != updatesProductEnd() );
+    }
+
+    bool Repository::solvablesEmpty() const
+    {
+      NO_REPOSITORY_RETURN( true );
+      return !_repo->nsolvables;
+    }
+
+    Repository::size_type Repository::solvablesSize() const
+    {
+      NO_REPOSITORY_RETURN( 0 );
+      return _repo->nsolvables;
+    }
+
+    Repository::SolvableIterator Repository::solvablesBegin() const
+    {
+      NO_REPOSITORY_RETURN( make_filter_iterator( detail::ByRepository( *this ),
+                            sat::detail::SolvableIterator(),
+                            sat::detail::SolvableIterator() ) );
+      return make_filter_iterator( detail::ByRepository( *this ),
+                                   sat::detail::SolvableIterator(_repo->start),
+                                   sat::detail::SolvableIterator(_repo->end) );
+    }
+
+    Repository::SolvableIterator Repository::solvablesEnd() const
+    {
+      NO_REPOSITORY_RETURN( make_filter_iterator( detail::ByRepository( *this ),
+                            sat::detail::SolvableIterator(),
+                            sat::detail::SolvableIterator() ) );
+      return make_filter_iterator(detail::ByRepository( *this ),
+                                  sat::detail::SolvableIterator(_repo->end),
+                                  sat::detail::SolvableIterator(_repo->end) );
+    }
+
+    Repository::ProductInfoIterator Repository::compatibleWithProductBegin() const
+    {
+      NO_REPOSITORY_RETURN( ProductInfoIterator() );
+      return ProductInfoIterator( sat::SolvAttr::repositoryDistros, *this );
+    }
+
+    Repository::ProductInfoIterator Repository::compatibleWithProductEnd() const
+    {
+      return ProductInfoIterator();
+    }
+
+    Repository::ProductInfoIterator Repository::updatesProductBegin() const
+    {
+      NO_REPOSITORY_RETURN( ProductInfoIterator() );
+      return ProductInfoIterator( sat::SolvAttr::repositoryUpdates, *this );
+    }
+
+    Repository::ProductInfoIterator Repository::updatesProductEnd() const
+    {
+      return ProductInfoIterator();
+    }
+
+    RepoInfo Repository::info() const
+    {
+      NO_REPOSITORY_RETURN( RepoInfo() );
+      return myPool().repoInfo( _repo );
+    }
+
+    void Repository::setInfo( const RepoInfo & info_r )
+    {
+       NO_REPOSITORY_THROW( Exception( "Can't set RepoInfo for norepo." ) );
+       if ( info_r.alias() != alias() )
+       {
+           ZYPP_THROW( Exception( str::form( "RepoInfo alias (%s) does not match repository alias (%s)",
+                                             info_r.alias().c_str(), alias().c_str() ) ) );
+       }
+       myPool().setRepoInfo( _repo, info_r );
+        MIL << *this << endl;
+    }
+
+    void Repository::clearInfo()
+    {
+       NO_REPOSITORY_RETURN();
+       myPool().setRepoInfo( _repo, RepoInfo() );
+    }
+
+    void Repository::eraseFromPool()
+    {
+       NO_REPOSITORY_RETURN();
+        MIL << *this << " removed from pool" << endl;
+       myPool()._deleteRepo( _repo );
+       _id = sat::detail::noRepoId;
+    }
+
+    Repository Repository::nextInPool() const
+    {
+      NO_REPOSITORY_RETURN( noRepository );
+      for_( it, sat::Pool::instance().reposBegin(), sat::Pool::instance().reposEnd() )
+      {
+        if ( *it == *this )
+        {
+          if ( ++it != _for_end )
+            return *it;
+          break;
+        }
+      }
+      return noRepository;
+    }
+
+    void Repository::addSolv( const Pathname & file_r )
+    {
+      NO_REPOSITORY_THROW( Exception( "Can't add solvables to norepo." ) );
+
+      AutoDispose<FILE*> file( ::fopen( file_r.c_str(), "r" ), ::fclose );
+      if ( file == NULL )
+      {
+        file.resetDispose();
+        ZYPP_THROW( Exception( "Can't open solv-file: "+file_r.asString() ) );
+      }
+
+      if ( myPool()._addSolv( _repo, file ) != 0 )
+      {
+        ZYPP_THROW( Exception( "Error reading solv-file: "+file_r.asString() ) );
+      }
+
+      MIL << *this << " after adding " << file_r << endl;
+    }
+
+    void Repository::addHelix( const Pathname & file_r )
+    {
+      NO_REPOSITORY_THROW( Exception( "Can't add solvables to norepo." ) );
+
+      std::string command( file_r.extension() == ".gz" ? "zcat " : "cat " );
+      command += file_r.asString();
+
+      AutoDispose<FILE*> file( ::popen( command.c_str(), "r" ), ::pclose );
+      if ( file == NULL )
+      {
+        file.resetDispose();
+        ZYPP_THROW( Exception( "Can't open helix-file: "+file_r.asString() ) );
+      }
+
+      if ( myPool()._addHelix( _repo, file ) != 0 )
+      {
+        ZYPP_THROW( Exception( "Error reading helix-file: "+file_r.asString() ) );
+      }
+
+      MIL << *this << " after adding " << file_r << endl;
+    }
+
+    sat::detail::SolvableIdType Repository::addSolvables( unsigned count_r )
+    {
+       NO_REPOSITORY_THROW( Exception( "Can't add solvables to norepo.") );
+       return myPool()._addSolvables( _repo, count_r );
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+     */
+    std::ostream & operator<<( std::ostream & str, const Repository & obj )
+    {
+       if ( ! obj )
+           return str << "noRepository";
+
+       return str << "sat::repo(" << obj.alias() << ")"
+                  << "{"
+                   << "prio " << obj.get()->priority << '.' << obj.get()->subpriority
+                  << ", size " << obj.solvablesSize()
+                  << "}";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Repository::ProductInfoIterator
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Repository::ProductInfoIterator::ProductInfoIterator( sat::SolvAttr attr_r, Repository repo_r )
+    { base_reference() = sat::LookupRepoAttr( attr_r, repo_r ).begin(); }
+
+    std::string Repository::ProductInfoIterator::label() const
+    { return base_reference().subFind( sat::SolvAttr::repositoryProductLabel ).asString(); }
+
+    std::string Repository::ProductInfoIterator::cpeId() const
+    { return base_reference().subFind( sat::SolvAttr::repositoryProductCpeid ).asString(); }
+
+    /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Repository.h b/zypp/Repository.h
new file mode 100644 (file)
index 0000000..e9b63a6
--- /dev/null
@@ -0,0 +1,452 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Repository.h
+ *
+*/
+#ifndef ZYPP_SAT_REPOSITORY_H
+#define ZYPP_SAT_REPOSITORY_H
+
+#include <iosfwd>
+#include "zypp/base/SafeBool.h"
+#include "zypp/Pathname.h"
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/sat/LookupAttr.h"     // LookupAttrTools.h included at EOF
+#include "zypp/sat/Solvable.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/Date.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      struct ByRepository;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Repository
+    //
+    /** */
+    class Repository : protected sat::detail::PoolMember,
+                       private base::SafeBool<Repository>
+    {
+    public:
+        typedef filter_iterator<detail::ByRepository, sat::detail::SolvableIterator> SolvableIterator;
+        typedef sat::detail::size_type size_type;
+        typedef sat::detail::RepoIdType IdType;
+
+        typedef sat::ArrayAttr<std::string,std::string> Keywords;
+
+    public:
+        /** Default ctor creates \ref noRepository.*/
+        Repository()
+        : _id( sat::detail::noRepoId ) {}
+
+        /** \ref PoolImpl ctor. */
+        explicit Repository( IdType id_r )
+        : _id( id_r ) {}
+
+    public:
+        /** Represents no \ref Repository. */
+        static const Repository noRepository;
+
+#ifndef SWIG // Swig treats it as syntax error
+        /** Evaluate \ref Repository in a boolean context (\c != \c noRepository). */
+        using base::SafeBool<Repository>::operator bool_type;
+#endif
+        /** Reserved system repository alias \c @System. */
+        static const std::string & systemRepoAlias();
+
+        /** Return whether this is the system repository. */
+        bool isSystemRepo() const;
+
+    public:
+         /**
+          * Short unique string to identify a repo.
+          * ie: openSUSE-10.3
+          *
+          * If you are looking for a label to display
+          * see \ref name().
+          * ie: "openSUSE 10.3 Main repository"
+          *
+          */
+        std::string alias() const;
+
+        /** Label to display for this repo. */
+        std::string name() const;
+
+        /**
+         * Timestamp when this repository was generated
+         *
+         * Usually this value is calculated as the newer
+         * timestamp from the timestamp of all the resources
+         * that conform the repository's metadata.
+         *
+         * For example in a rpm-md repository, it would be
+         * the resource specified in the xml file whith
+         * the newest timestamp attribute (which is the
+         * timestamp of the file in the server ).
+         *
+         * The timestamp is 0 if the repository does not
+         * specify when it was generated.
+         *
+         */
+        Date generatedTimestamp() const;
+
+        /**
+         * Suggested expiration timestamp.
+         *
+         * Repositories can define an amount of time
+         * they expire, with the generated timestamp as
+         * the base point of time.
+         *
+         * Note that is the responsability of the repository
+         * to freshen the generated timestamp to tell the
+         * client that the repo is alive and updating the
+         * metadata.
+         *
+         * The timestamp is 0 if the repository does not specify
+         * an expiration date.
+         *
+         */
+        Date suggestedExpirationTimestamp() const;
+
+        /**
+         * repository keywords (tags)
+         */
+        Keywords keywords() const;
+
+        /**
+         * The suggested expiration date of this repository
+         * already passed
+         *
+         * rpm-md repositories can provide this tag using the
+         * expire extension tag:
+         * \see http://en.opensuse.org/Standards/Rpm_Metadata#SUSE_repository_info_.28suseinfo.xml.29.2C_extensions_to_repomd.xml
+         */
+        bool maybeOutdated() const;
+
+        /**
+         * if the repository claims to update something then
+         * it is an update repository
+         *
+         * This is implemented by looking at the repository updates
+         * tag.
+         * \see http://en.opensuse.org/Standards/Rpm_Metadata#SUSE_repository_info_.28suseinfo.xml.29.2C_extensions_to_repomd.xml
+         */
+        bool isUpdateRepo() const;
+
+        /**
+         * wether the repository claims to update something \ref prod
+         * with key \ref cpeid
+         *
+         * \see zypp::Product::cpeId()
+         *
+         * See http://cpe.mitre.org/ for more information on the
+         * Common Platform Enumearation.
+         */
+        bool providesUpdatesFor( const std::string &cpeid ) const;
+
+        /** Whether \ref Repository contains solvables. */
+        bool solvablesEmpty() const;
+
+        /** Number of solvables in \ref Repository. */
+        size_type solvablesSize() const;
+
+        /** Iterator to the first \ref Solvable. */
+        SolvableIterator solvablesBegin() const;
+
+        /** Iterator behind the last \ref Solvable. */
+        SolvableIterator solvablesEnd() const;
+
+    public:
+
+      /** Query class for Repository */
+      class ProductInfoIterator;
+
+      /**
+       * Get an iterator to the beginning of the repository
+       * compatible distros.
+       * \note This is only a hint. There is no guarantee that
+       * the repository is built for that product.
+       * \see Repository::ProductInfoIterator
+       */
+      ProductInfoIterator compatibleWithProductBegin() const;
+
+      /**
+       * Get an iterator to the end of the repository
+       * compatible distros.
+       * \see Repository::ProductInfoIterator
+       */
+      ProductInfoIterator compatibleWithProductEnd() const;
+
+      /**
+       * Get an iterator to the beginning of the repository
+       * compatible distros.
+       * \see Repository::ProductInfoIterator
+       */
+      ProductInfoIterator updatesProductBegin() const;
+
+      /**
+       * Get an iterator to the end of the repository
+       * compatible distros.
+       * \see Repository::ProductInfoIterator
+       */
+      ProductInfoIterator updatesProductEnd() const;
+
+    public:
+        /** Return any associated \ref RepoInfo. */
+        RepoInfo info() const;
+
+        /** Set \ref RepoInfo for this repository.
+         * \throws Exception if this is \ref noRepository
+         * \throws Exception if the \ref RepoInfo::alias
+         *         does not match the \ref Repository::name.
+        */
+        void setInfo( const RepoInfo & info_r );
+
+       /** Remove any \ref RepoInfo set for this repository. */
+        void clearInfo();
+
+    public:
+        /** Remove this \ref Repository from it's \ref Pool. */
+        void eraseFromPool();
+
+        /** Functor calling \ref eraseFromPool. */
+        struct EraseFromPool;
+
+   public:
+        /** Return next Repository in \ref Pool (or \ref noRepository). */
+        Repository nextInPool() const;
+
+   public:
+        /** \name Repository content manipulating methods.
+         * \todo maybe a separate Repository/Solvable content manip interface
+         * provided by the pool.
+         */
+        //@{
+        /** Load \ref Solvables from a solv-file.
+         * In case of an exception the repository remains in the \ref Pool.
+         * \throws Exception if this is \ref noRepository
+         * \throws Exception if loading the solv-file fails.
+         * \see \ref Pool::addRepoSolv and \ref Repository::EraseFromPool
+         */
+        void addSolv( const Pathname & file_r );
+
+         /** Load \ref Solvables from a helix-file.
+         * Supports loading of gzip compressed files (.gz). In case of an exception
+         * the repository remains in the \ref Pool.
+         * \throws Exception if this is \ref noRepository
+         * \throws Exception if loading the helix-file fails.
+         * \see \ref Pool::addRepoHelix and \ref Repository::EraseFromPool
+         */
+        void addHelix( const Pathname & file_r );
+
+       /** Add \c count_r new empty \ref Solvable to this \ref Repository. */
+        sat::Solvable::IdType addSolvables( unsigned count_r );
+        /** \overload Add only one new \ref Solvable. */
+        sat::Solvable::IdType addSolvable()
+           { return addSolvables( 1 ); }
+        //@}
+
+    public:
+        /** Expert backdoor. */
+        ::_Repo * get() const;
+        /** Expert backdoor. */
+        IdType id() const { return _id; }
+        /** satsolver internal priorities.
+         * Unlike the \ref RepoInfo priority which tries to be YUM conform
+         * (H[1-99]L), this one is the solvers internal priority representation.
+         * It is type \c int and as one might expect it, the higher the value
+         * the higher the priority. Subpriority is currently used to express
+         * media preferences (\see \ref MediaPriority).
+         */
+        //@{
+        int satInternalPriority() const;
+        int satInternalSubPriority() const;
+        //@}
+    private:
+#ifndef SWIG // Swig treats it as syntax error
+        friend base::SafeBool<Repository>::operator bool_type() const;
+#endif
+        bool boolTest() const { return get(); }
+    private:
+        IdType _id;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Repository Stream output */
+    std::ostream & operator<<( std::ostream & str, const Repository & obj );
+
+    /** \relates Repository */
+    inline bool operator==( const Repository & lhs, const Repository & rhs )
+    { return lhs.get() == rhs.get(); }
+
+    /** \relates Repository */
+    inline bool operator!=( const Repository & lhs, const Repository & rhs )
+    { return lhs.get() != rhs.get(); }
+
+    /** \relates Repository */
+    inline bool operator<( const Repository & lhs, const Repository & rhs )
+    { return lhs.get() < rhs.get(); }
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Query class for Repository related products
+     *
+     * The iterator does not provide a dereference
+     * operator so you can do * on it, but you can
+     * access the attributes of each related product
+     * directly from the iterator.
+     *
+     * \code
+     * for_( it, repo.compatibleWithProductBegin(), repo.compatibleWithProductEnd() )
+     * {
+     *   cout << it.cpeid() << endl;
+     * }
+     * \endcode
+     *
+     */
+    class Repository::ProductInfoIterator : public boost::iterator_adaptor<
+        Repository::ProductInfoIterator    // Derived
+        , sat::LookupAttr::iterator        // Base
+        , int                              // Value
+        , boost::forward_traversal_tag     // CategoryOrTraversal
+        , int                              // Reference
+    >
+    {
+      public:
+        ProductInfoIterator()
+        {}
+
+        /**
+         * Product label
+         */
+        std::string label() const;
+
+        /**
+         * The Common Platform Enumeration name
+         * for this product.
+         *
+         * See http://cpe.mitre.org
+         */
+        std::string cpeId() const;
+
+      private:
+        friend class Repository;
+        /** Hide ctor as just a limited set of attributes is valid. */
+        explicit ProductInfoIterator( sat::SolvAttr attr_r, Repository repo_r );
+
+      private:
+        friend class boost::iterator_core_access;
+        int dereference() const { return 0; }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Repository::EraseFromPool
+    //
+    /** Functor removing \ref Repository from it's \ref Pool.
+     *
+     * E.g. used as dispose function in. \ref AutoDispose
+     * to provide a convenient and exception safe temporary
+     * \ref Repository.
+     * \code
+     *  sat::Pool satpool;
+     *  MIL << "1 " << satpool << endl;
+     *  {
+     *    AutoDispose<Repository> tmprepo( (Repository::EraseFromPool()) );
+     *    *tmprepo = satpool.reposInsert( "A" );
+     *    tmprepo->addSolv( "sl10.1-beta7-packages.solv" );
+     *    DBG << "2 " << satpool << endl;
+     *    // Calling 'tmprepo.resetDispose();' here
+     *    // would keep the Repo.
+     *  }
+     *  MIL << "3 " << satpool << endl;
+     * \endcode
+     * \code
+     * 1 sat::pool(){0repos|2slov}
+     * 2 sat::pool(){1repos|2612slov}
+     * 3 sat::pool(){0repos|2slov}
+     * \endcode
+     * Leaving the block without calling <tt>tmprepo.resetDispose();</tt>
+     * before, will automatically remove the \ref Repo from it's \ref Pool.
+     */
+    struct Repository::EraseFromPool
+    {
+       void operator()( Repository repository_r ) const
+           { repository_r.eraseFromPool(); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : RepositoryIterator
+      //
+      /** */
+      class RepositoryIterator : public boost::iterator_adaptor<
+           RepositoryIterator                            // Derived
+                          , ::_Repo **                   // Base
+                           , Repository                   // Value
+                          , boost::forward_traversal_tag // CategoryOrTraversal
+                          , Repository                   // Reference
+                            >
+      {
+        public:
+          RepositoryIterator()
+          : RepositoryIterator::iterator_adaptor_( 0 )
+          {}
+
+          explicit RepositoryIterator( ::_Repo ** p )
+          : RepositoryIterator::iterator_adaptor_( p )
+          {}
+
+        private:
+          friend class boost::iterator_core_access;
+
+          Repository dereference() const
+          { return Repository( *base() ); }
+      };
+      ///////////////////////////////////////////////////////////////////
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : ByRepository
+      //
+      /** Functor filtering \ref Solvable by \ref Repository.*/
+      struct ByRepository
+      {
+        public:
+          ByRepository( const Repository & repository_r ) : _repository( repository_r ) {}
+          ByRepository( sat::detail::RepoIdType id_r ) : _repository( id_r ) {}
+          ByRepository() {}
+
+          bool operator()( const sat::Solvable & slv_r ) const
+          { return slv_r.repository() == _repository; }
+
+        private:
+          Repository _repository;
+      };
+      ///////////////////////////////////////////////////////////////////
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+// Late include as sat::ArrayAttr requires Repository.h
+#include "zypp/sat/LookupAttrTools.h"
+
+#endif // ZYPP_SAT_REPOSITORY_H
diff --git a/zypp/ResFilters.h b/zypp/ResFilters.h
new file mode 100644 (file)
index 0000000..194a419
--- /dev/null
@@ -0,0 +1,359 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResFilters.h
+ *
+*/
+#ifndef ZYPP_RESFILTERS_H
+#define ZYPP_RESFILTERS_H
+
+#include <boost/function.hpp>
+
+#include "zypp/base/Functional.h"
+#include "zypp/Filter.h"
+#include "zypp/Resolvable.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/Repository.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace resfilter
+  { /////////////////////////////////////////////////////////////////
+
+    /** \defgroup RESFILTERS Filter functors operating on ResObjects.
+     * \ingroup g_Functor
+     *
+     * A simple filter is a function or functor matching the signature:
+     * \code
+     *   bool simplefilter( ResObject::Ptr );
+     * \endcode
+     *
+     * \note It's not neccessary that your function or functor actually
+     * returns \c bool. Anything which is convertible into a \c bool
+     * will do;
+     *
+     * Besides basic filter functors which actually evaluate the
+     * \c ResObject (e.g. \ref ByKind, \ref ByName) you may
+     * use \ref LOGICALFILTERS to build more complex filters.
+     *
+     * \code
+     * // some 'action' functor, printing and counting
+     * // ResObjects.
+     * struct PrintAndCount
+     * {
+     *   PrintAndCount( unsigned & counter_r )
+     *   : _counter( counter_r )
+     *   {}
+     *
+     *   bool operator()( ResObject::Ptr p ) const
+     *   {
+     *     DBG << *p << endl;
+     *     ++_counter;
+     *     return true;
+     *   }
+     *
+     *   unsigned _counter;
+     * };
+     *
+     * ResStore store;
+     * unsigned counter = 0;
+     *
+     * // print and count all resolvables
+     * store.forEach( PrintAndCount(counter) );
+     *
+     * // print and count all resolvables named "kernel"
+     * counter = 0;
+     * store.forEach( ByName("kernel"), PrintAndCount(counter) );
+     *
+     * // print and count all Packages named "kernel"
+     * counter = 0;
+     * store.forEach( chain( ByKind(ResKind::package),
+     *                       ByName("kernel") ),
+     *                PrintAndCount(counter) );
+     *
+     * // print and count all Packages not named "kernel"
+     * counter = 0;
+     * store.forEach( chain( ByKind(ResKind::package),
+     *                       not_c(ByName("kernel")) ),
+     *                PrintAndCount(counter) );
+     *
+     * // same as above ;)
+     * counter = 0;
+     * store.forEach( chain( ByKind(ResKind::package),
+     *                       chain( not_c(ByName("kernel")),
+     *                              PrintAndCount(counter) ) ),
+     *                true_c() );
+     * \endcode
+     *
+     * As you can see in the last example there is no difference in using
+     * a filter or an action functor, as both have the same signature.
+     * A difference of course is the way forEach interprets the returned
+     * value.
+     *
+     * Consequently you can netgate and chain actions as well. Thus
+     * <tt>PrintAndCount(counter)</tt> could be
+     * <tt>chain(Print(),Count(counter))</tt>, if these functors are
+     * provided.
+     *
+     * \note These functors are not limited to be used with ResStore::forEach.
+     * You can use them with std::algorithms as well.
+     *
+     * \note In case you already have functions or methods which do what you
+     * want, but thet don't perfectly match the required signature: Make yourself
+     * familiar with <tt>std::ptr_fun, mem_fun, bind1st, bind2nd and compose</tt>.
+     * They are sometimes quite helpfull.
+     *
+     * \c PrintAndCount is an example how a functor can return data collected
+     * during the query. You ca easily write a collector, that takes a
+     * <tt>std:list\<ResObject::Ptr\>\&</tt> and fills it with the matches
+     * found.
+     *
+     * But as a rule of thumb, a functor should be lightweight. If you
+     * want to get data out, pass references to variables in (and assert
+     * these variables live as long as the query lasts). Or use \ref FunctorRef.
+     *
+     * Internally all functors are passed by value. Thus it would not help
+     * you to create an instance of some collecting functor, and pass it
+     * to the query. The query will then fill a copy of your functor, you
+     * won't get the data back. (Well, you probabely could, by using
+     * boosr::ref).
+     *
+     * Why functors and not plain functions?
+     *
+     * You can use plain functions if they don't have to deliver data back to
+     * the application.
+     * The \c C-style approach is having functions that take a <tt>void * data</tt>
+     * as last argument. This \c data pointer is then passed arround and casted
+     * up and down.
+     *
+     * If you look at a functor, you'll see that it contains both, the function
+     * to call (it's <tt>operator()</tt> ) and the data you'd otherwise pass as
+     * <tt>void * data</tt>. That's nice and safe.
+     *
+     * \todo migrate to namespace filter and enhance to support Solvables as well.
+    */
+    //@{
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Some ResObject attributes
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    /** */
+    typedef std::unary_function<ResObject::constPtr, bool> ResObjectFilterFunctor;
+    typedef boost::function<bool ( ResObject::constPtr )> ResFilter;
+
+    /** Select ResObject by kind. \deprecated include filter.h and use filter::ByKind. */
+    ZYPP_DEPRECATED typedef filter::ByKind ByKind;
+    /** */
+    template<class _Res>
+      inline filter::ByKind byKind()
+      { return filter::ByKind( ResTraits<_Res>::kind ); }
+
+    /** Select ResObject by name. */
+    struct ByName : public ResObjectFilterFunctor
+    {
+      ByName( const std::string & name_r )
+      : _name( name_r )
+      {}
+
+      bool operator()( ResObject::constPtr p ) const
+      {
+        return p->name() == _name;
+      }
+
+      std::string _name;
+    };
+
+    /** Select ResObject by repository or repository alias. */
+    struct ByRepository : public ResObjectFilterFunctor
+    {
+      ByRepository( Repository repository_r )
+      : _alias( repository_r.info().alias() )
+      {}
+
+      ByRepository( const std::string & alias_r )
+      : _alias( alias_r )
+      {}
+
+      bool operator()( ResObject::constPtr p ) const
+      {
+        return p->repoInfo().alias() == _alias;
+      }
+
+      std::string _alias;
+    };
+
+    /** Select ResObject by Edition using \a _Compare functor.
+     *
+     * Selects ResObject if <tt>_Compare( ResObject->edition(), _edition )</tt>
+     * is \c true.
+     * \code
+     * // use the convenience funktions to create ByEdition:
+     *
+     * byEdition( someedition ); // selects ResObjects with edition == someedition
+     *
+     * byEdition( someedition, CompareByGT<Edition>() ) //  edition >  someedition
+     * \endcode
+    */
+    template<class _Compare = CompareByEQ<Edition> >
+      struct ByEdition : public ResObjectFilterFunctor
+      {
+        ByEdition( const Edition & edition_r,
+                   _Compare cmp_r )
+        : _edition( edition_r )
+        , _cmp( cmp_r )
+        {}
+
+        bool operator()( ResObject::constPtr p ) const
+        {
+          return _cmp( p->edition(), _edition );
+        }
+
+        Edition  _edition;
+        _Compare _cmp;
+      };
+
+    /** */
+    template<class _Compare>
+      ByEdition<_Compare> byEdition( const Edition & edition_r, _Compare cmp_r )
+      { return ByEdition<_Compare>( edition_r, cmp_r ); }
+
+    /** */
+    template<class _Compare>
+      ByEdition<_Compare> byEdition( const Edition & edition_r )
+      { return byEdition( edition_r, _Compare() ); }
+
+
+    /** Select ResObject by Arch using \a _Compare functor.
+     *
+     * Selects ResObject if <tt>_Compare( ResObject->arch(), _arch )</tt>
+     * is \c true.
+     * \code
+     * // use the convenience funktions to create ByArch:
+     *
+     * byArch( somearch ); // selects ResObjects with arch == somearch
+     *
+     * byArch( somearch, CompareByGT<Arch>() ) //  arch >  somearch
+     * \endcode
+    */
+    template<class _Compare = CompareByEQ<Arch> >
+      struct ByArch : public ResObjectFilterFunctor
+      {
+        ByArch( const Arch & arch_r,
+                   _Compare cmp_r )
+        : _arch( arch_r )
+        , _cmp( cmp_r )
+        {}
+
+        bool operator()( ResObject::constPtr p ) const
+        {
+          return _cmp( p->arch(), _arch );
+        }
+
+        Arch  _arch;
+        _Compare _cmp;
+      };
+
+    /** */
+    template<class _Compare>
+      ByArch<_Compare> byArch( const Arch & arch_r, _Compare cmp_r )
+      { return ByArch<_Compare>( arch_r, cmp_r ); }
+
+    /** */
+    template<class _Compare>
+      ByArch<_Compare> byArch( const Arch & arch_r )
+      { return byArch( arch_r, _Compare() ); }
+
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Some PoolItem attributes
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    /** */
+    typedef std::unary_function<PoolItem, bool> PoolItemFilterFunctor;
+
+    /** Select PoolItem by installed. */
+    struct ByInstalled : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isInstalled();
+      }
+
+    };
+
+    /** Select PoolItem by uninstalled. */
+    struct ByUninstalled : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isUninstalled();
+      }
+    };
+
+    /** Select PoolItem by transact. */
+    struct ByTransact : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().transacts();
+      }
+    };
+
+    /** Select PoolItem by lock. */
+    struct ByLock : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isLocked();
+      }
+    };
+
+    /** Select PoolItem by keep. */
+    struct ByKeep : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isKept();
+      }
+    };
+
+    /** PoolItem which is recommended. */
+    struct ByRecommended : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isRecommended();
+      }
+    };
+
+    /** PoolItem which is suggested. */
+    struct BySuggested : public PoolItemFilterFunctor
+    {
+      bool operator()( const PoolItem & p ) const
+      {
+       return p.status().isSuggested();
+      }
+    };
+
+    //@}
+    /////////////////////////////////////////////////////////////////
+  } // namespace resfilter
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESFILTERS_H
diff --git a/zypp/ResKind.cc b/zypp/ResKind.cc
new file mode 100644 (file)
index 0000000..437a385
--- /dev/null
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResKind.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/String.h"
+
+#include "zypp/ResKind.h"
+#include "zypp/ResTraits.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const ResKind ResKind::nokind;
+  const ResKind ResKind::package   ( "package" );
+  const ResKind ResKind::patch     ( "patch" );
+  const ResKind ResKind::pattern   ( "pattern" );
+  const ResKind ResKind::product   ( "product" );
+  const ResKind ResKind::srcpackage( "srcpackage" );
+
+  template<>
+    const ResKind ResTraits<Package>   ::kind( ResKind::package );
+  template<>
+    const ResKind ResTraits<Patch>     ::kind( ResKind::patch );
+  template<>
+    const ResKind ResTraits<Pattern>   ::kind( ResKind::pattern );
+  template<>
+    const ResKind ResTraits<Product>   ::kind( ResKind::product );
+  template<>
+    const ResKind ResTraits<SrcPackage>::kind( ResKind::srcpackage );
+
+  std::string ResKind::satIdent( const ResKind & refers_r, const std::string & name_r )
+  {
+    if ( ! refers_r || refers_r == package || refers_r == srcpackage )
+      return name_r;
+    return str::form( "%s:%s", refers_r.c_str(), name_r.c_str() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ResKind.h b/zypp/ResKind.h
new file mode 100644 (file)
index 0000000..c687f6a
--- /dev/null
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResKind.h
+ *
+*/
+#ifndef ZYPP_RESKIND_H
+#define ZYPP_RESKIND_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Deprecated.h"
+#include "zypp/base/String.h"
+#include "zypp/IdStringType.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResKind
+  //
+  /** Resolvable kinds.
+   * A \b lowercased string and used as identification.
+   * Comparison against string values is always case
+   * insensitive.
+   */
+  class ResKind : public IdStringType<ResKind>
+  {
+    public:
+      /** \name Some builtin ResKind constants. */
+      //@{
+      /** Value representing \c nokind (<tt>""</tt>)*/
+      static const ResKind nokind;
+
+      static const ResKind package;
+      static const ResKind patch;
+      static const ResKind pattern;
+      static const ResKind product;
+      static const ResKind srcpackage;
+      //@}
+
+    public:
+      /** Default ctor: \ref nokind */
+      ResKind() {}
+
+      /** Ctor taking kind as string. */
+      explicit ResKind( sat::detail::IdType id_r )  : _str( str::toLower(IdString(id_r).c_str()) ) {}
+      explicit ResKind( const IdString & idstr_r )  : _str( str::toLower(idstr_r.c_str()) ) {}
+      explicit ResKind( const std::string & str_r ) : _str( str::toLower(str_r) ) {}
+      explicit ResKind( const char * cstr_r )       : _str( str::toLower(cstr_r) ) {}
+
+    public:
+      /** Return the satsolver identifier for name.
+       * Libsatsolver combines the objects kind and name in a single
+       * identifier \c "pattern:kde_multimedia", \b except for packages
+       * and source packes. They are not prefixed by any kind string.
+      */
+      static std::string satIdent( const ResKind & refers_r, const std::string & name_r );
+      /** \overload */
+      std::string satIdent( const std::string & name_r ) const
+      { return satIdent( *this, name_r ); }
+
+    private:
+      static int _doCompare( const char * lhs,  const char * rhs )
+      {
+        if ( lhs == rhs ) return 0;
+        if ( lhs && rhs ) return ::strcasecmp( lhs, rhs );
+        return( lhs ? 1 : -1 );
+      }
+
+    private:
+      friend class IdStringType<ResKind>;
+      IdString _str;
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESKIND_H
diff --git a/zypp/ResObject.cc b/zypp/ResObject.cc
new file mode 100644 (file)
index 0000000..a6ae73e
--- /dev/null
@@ -0,0 +1,135 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResObject.cc
+ *
+*/
+
+#include "zypp/ResObject.h"
+#include "zypp/sat/SolvAttr.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/Repository.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/IdString.h"
+
+using namespace zypp;
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(ResObject);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResObject::ResObject
+  //   METHOD TYPE : Ctor
+  //
+  ResObject::ResObject( const sat::Solvable & solvable_r )
+  : Resolvable( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResObject::~ResObject
+  //   METHOD TYPE : Dtor
+  //
+  ResObject::~ResObject()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResObject::dumpOn
+  //   METHOD TYPE : std::ostream &
+  //
+  std::ostream & ResObject::dumpOn( std::ostream & str ) const
+  {
+    return Resolvable::dumpOn( str );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::string ResObject::summary( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::summary, lang_r ); }
+
+  std::string ResObject::description( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::description, lang_r ); }
+
+  std::string ResObject::insnotify( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::insnotify, lang_r ); }
+
+  std::string ResObject::delnotify( const Locale & lang_r ) const
+  { return lookupStrAttribute( sat::SolvAttr::delnotify, lang_r ); }
+
+  std::string ResObject::licenseToConfirm( const Locale & lang_r ) const
+  {
+    std::string ret = lookupStrAttribute( sat::SolvAttr::eula, lang_r );
+    if ( ret.empty() && isKind<Product>() )
+      return repoInfo().getLicense( lang_r );
+    return ret;
+  }
+
+  std::string ResObject::distribution() const
+  { return lookupStrAttribute( sat::SolvAttr::distribution ); }
+
+  std::string ResObject::cpeId() const
+  { return lookupStrAttribute( sat::SolvAttr::cpeid ); }
+
+  ByteCount ResObject::installSize() const
+  { return ByteCount( lookupNumAttribute( sat::SolvAttr::installsize ), ByteCount::K ); }
+
+  ByteCount ResObject::downloadSize() const
+  { return ByteCount( lookupNumAttribute( sat::SolvAttr::downloadsize ), ByteCount::K ); }
+
+  unsigned ResObject::mediaNr() const
+  { return lookupNumAttribute( sat::SolvAttr::medianr ); }
+
+  Date ResObject::buildtime() const
+  { return Date( lookupNumAttribute( sat::SolvAttr::buildtime ) ); }
+
+  Date ResObject::installtime() const
+  { return Date( lookupNumAttribute( sat::SolvAttr::installtime ) ); }
+
+#warning DUMMY diskusage
+  const DiskUsage & ResObject::diskusage() const
+  {
+    static DiskUsage _du;
+    return _du;
+  }
+
+   /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#include "zypp/ResObjects.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ResObject::Ptr makeResObject( const sat::Solvable & solvable_r )
+  {
+    if ( ! solvable_r )
+      return 0;
+
+    ResKind kind( solvable_r.kind() );
+#define OUTS(X)  if ( kind == ResTraits<X>::kind ) return make<X>( solvable_r );
+    OUTS( Package );
+    OUTS( Patch );
+    OUTS( Pattern );
+    OUTS( Product );
+    OUTS( SrcPackage );
+#undef OUTS
+    // unknow => return a plain ResObject
+    return new ResObject( solvable_r );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ResObject.h b/zypp/ResObject.h
new file mode 100644 (file)
index 0000000..d508dfe
--- /dev/null
@@ -0,0 +1,270 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResObject.h
+ *
+*/
+#ifndef ZYPP_RESOBJECT_H
+#define ZYPP_RESOBJECT_H
+
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/Resolvable.h"
+#include "zypp/Date.h"
+#include "zypp/Locale.h"
+#include "zypp/Vendor.h"
+#include "zypp/ByteCount.h"
+#include "zypp/DiskUsage.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/Repository.h"
+
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/SolvableSet.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResObject
+  //
+  /**
+   * Interface base for resolvable objects (common data).
+   * That is, all data not needed for solving, but common
+   * across all Resolvable kinds.
+   *
+   * \see \ref makeResObject for how to construct ResObjects.
+  */
+  class ResObject : public Resolvable
+  {
+  public:
+    typedef ResObject                Self;
+    typedef ResTraits<Self>          TraitsType;
+    typedef TraitsType::PtrType      Ptr;
+    typedef TraitsType::constPtrType constPtr;
+
+  public:
+
+    /** Convert \c this into a Ptr of a certain Kind.
+     * This is a convenience to access type specific
+     * attributes.
+     * \return \c NULL if \c this is not of the specified kind.
+     * \code
+     *  PoolItem pi;
+     *  Package::constPtr pkg = pi->asKind<Package>();
+     *
+     *  if ( pi->isKind<Package>() )
+     *     DBG << pi->asKind<Package>()->keywords() << endl;
+     * \endcode
+     */
+    template<class _Res>
+    inline typename ResTraits<_Res>::constPtrType asKind() const;
+
+    template<class _Res>
+    inline typename ResTraits<_Res>::PtrType asKind();
+
+  public:
+    /** \name Locale support.
+     * \see \ref sat::Solvable
+     */
+    //@{
+    /** \see \ref sat::Solvable::supportsLocales */
+    bool supportsLocales() const
+    { return sat::Solvable::supportsLocales(); }
+
+    /** \see \ref sat::Solvable::supportsLocale */
+    bool supportsLocale( const Locale & locale_r ) const
+    { return sat::Solvable::supportsLocale( locale_r ); }
+
+    bool supportsLocale( const LocaleSet & locales_r ) const
+    { return sat::Solvable::supportsLocale( locales_r ); }
+
+    /** \see \ref sat::Solvable::supportsRequestedLocales */
+    bool supportsRequestedLocales() const
+    { return sat::Solvable::supportsRequestedLocales(); }
+
+    /** \see \ref sat::Solvable::getSupportedLocales */
+    LocaleSet getSupportedLocales() const
+    { return sat::Solvable::getSupportedLocales(); }
+    //@}
+
+  public:
+    /**
+     * \short Short text describing the resolvable.
+     * This attribute is usually displayed in columns.
+     */
+    std::string summary( const Locale & lang_r = Locale() ) const;
+
+    /**
+     * \short Long text describing the resolvable.
+     */
+    std::string description( const Locale & lang_r = Locale() ) const;
+
+    /**
+     * \short Installation Notification
+     *
+     * This text can be used to tell the user some notes
+     * When he selects the resovable for installation.
+     */
+    std::string insnotify( const Locale & lang_r = Locale() ) const;
+
+    /**
+     * \short De-Installation Notification
+     *
+     * This text can be used to tell the user some notes
+     * When he selects the resovable for deinstall.
+     */
+    std::string delnotify( const Locale & lang_r = Locale() ) const;
+
+    /**
+     * \short License or agreement to accept
+     *
+     * Agreement, warning or license the user should
+     * accept before installing the resolvable.
+     */
+    std::string licenseToConfirm( const Locale & lang_r = Locale() ) const;
+
+    /**
+     * \short Vendor
+     *
+     * For example "Novell Inc."
+     */
+    Vendor vendor() const
+    { return Resolvable::vendor().asString(); }
+
+    /** The distribution string.
+     * E.g. \c code-11.
+    */
+    std::string distribution() const;
+
+    /**
+     * The Common Platform Enumeration name
+     * for this product.
+     *
+     * See http://cpe.mitre.org
+     */
+    std::string cpeId() const;
+
+    /** Installed size. */
+    ByteCount installSize() const;
+
+    /** Size of the rpm package. */
+    ByteCount downloadSize() const;
+
+    /** \see \ref sat::Solvable::repository */
+    Repository repository() const
+    { return sat::Solvable::repository(); }
+
+     /** \ref RepoInfo associated with the repository
+      *  providing this resolvable.
+      */
+    RepoInfo repoInfo() const
+    { return repository().info(); }
+
+    /**
+     * Media number where the resolvable is located
+     * 0 if no media access is required.
+     */
+    unsigned mediaNr() const;
+
+    /**
+     * \short build time of the resolvable
+     */
+    Date buildtime() const;
+
+    /**
+     * \short Installation time
+     * 0 if the resolvable is not installed.
+     */
+    Date installtime() const;
+
+    /**
+     * \short Disk usage per directory
+     * A common attribute, although mostly packages require
+     * noticeable disk space. An e.g product could try to reserve
+     * a certain ammount of diskspace by providing DiskUsage data.
+     */
+    const DiskUsage & diskusage() const;
+
+  protected:
+    friend ResObject::Ptr makeResObject( const sat::Solvable & solvable_r );
+    /** Ctor */
+    ResObject( const sat::Solvable & solvable_r );
+    /** Dtor */
+    virtual ~ResObject();
+    /** Helper for stream output */
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** Create \ref ResObject from \ref sat::Solvable.
+   *
+   * This function creates the apropriate kind of ResObject
+   * depending on the sat::Solvables kind, and returns a smart
+   * pointer to it.
+   *
+   * If the sat::Solvables kind is not convertible, a NULL
+   * pointer is returned.
+   *
+   * \code
+   * sat::Solvable s;
+   * ResObject::Ptr p( makeResObject( s ) );
+   * ResObject::Ptr q( make<ResObject>( s ) );
+   * Package::Ptr   pkg( make<Package>( s ) );
+   * \endcode
+  */
+  ResObject::Ptr makeResObject( const sat::Solvable & solvable_r );
+
+  /** Directly create a certain kind of ResObject from \ref sat::Solvable.
+   *
+   * If the sat::Solvables kind is not appropriate, a NULL
+   * pointer is returned.
+    * \code
+   * sat::Solvable s;
+   * ResObject::Ptr p( makeResObject( s ) );
+   * ResObject::Ptr q( make<ResObject>( s ) );
+   * Package::Ptr   pkg( make<Package>( s ) );
+   * \endcode
+  */
+  template<class _Res>
+  inline typename ResTraits<_Res>::PtrType make( const sat::Solvable & solvable_r )
+  { return( isKind<_Res>( solvable_r ) ? new _Res( solvable_r ) : 0 ); }
+  /** \overload Specialisation for ResObject autodetecting the kind of resolvable. */
+  template<>
+  inline ResObject::Ptr make<ResObject>( const sat::Solvable & solvable_r )
+  { return makeResObject( solvable_r ); }
+
+  /** Convert ResObject::Ptr into Ptr of a certain Kind.
+   * \return \c NULL iff \a p is \c NULL or points to a Resolvable
+   * not of the specified Kind.
+   * \relates ResObject
+   * \code
+   * asKind<Package>(resPtr);
+   * \endcode
+  */
+  template<class _Res>
+  inline typename ResTraits<_Res>::PtrType asKind( const ResObject::Ptr & p )
+  { return dynamic_pointer_cast<_Res>(p); }
+
+  template<class _Res>
+  inline typename ResTraits<_Res>::constPtrType asKind( const ResObject::constPtr & p )
+  { return dynamic_pointer_cast<const _Res>(p); }
+
+  template<class _Res>
+  inline typename ResTraits<_Res>::constPtrType ResObject::asKind() const
+  { return make<_Res>( *this ); }
+
+  template<class _Res>
+  inline typename ResTraits<_Res>::PtrType ResObject::asKind()
+  { return make<_Res>( *this ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESOBJECT_H
diff --git a/zypp/ResObjects.h b/zypp/ResObjects.h
new file mode 100644 (file)
index 0000000..073008c
--- /dev/null
@@ -0,0 +1,21 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResObjects.h
+ *
+*/
+#ifndef ZYPP_RESOBJECTS_H
+#define ZYPP_RESOBJECTS_H
+
+#include "zypp/Package.h"
+#include "zypp/Patch.h"
+#include "zypp/Pattern.h"
+#include "zypp/Product.h"
+#include "zypp/SrcPackage.h"
+
+#endif // ZYPP_RESOBJECTS_H
diff --git a/zypp/ResPool.cc b/zypp/ResPool.cc
new file mode 100644 (file)
index 0000000..dc84a24
--- /dev/null
@@ -0,0 +1,169 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResPool.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/SerialNumber.h"
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPool.h"
+#include "zypp/pool/PoolImpl.h"
+#include "zypp/pool/PoolStats.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResPool::instance
+  //   METHOD TYPE : ResPool
+  //
+  ResPool ResPool::instance()
+  {
+    static ResPool _val( pool::PoolTraits::Impl_Ptr( new pool::PoolImpl ) );
+    return _val;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResPool::ResPool
+  //   METHOD TYPE : Ctor
+  //
+  ResPool::ResPool( pool::PoolTraits::Impl_Ptr impl_r )
+  : _pimpl( impl_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to impementation:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ResPoolProxy ResPool::proxy() const
+  { return _pimpl->proxy( *this ); }
+
+  Resolver & ResPool::resolver() const
+  { return *getZYpp()->resolver(); }
+
+  const SerialNumber & ResPool::serial() const
+  { return _pimpl->serial(); }
+
+  bool ResPool::empty() const
+  { return _pimpl->empty(); }
+
+  ResPool::size_type ResPool::size() const
+  { return _pimpl->size(); }
+
+
+  PoolItem ResPool::find( const sat::Solvable & slv_r ) const
+  { return _pimpl->find( slv_r ); }
+
+
+  ResPool::size_type ResPool::knownRepositoriesSize() const
+  { return _pimpl->knownRepositoriesSize(); }
+
+  ResPool::repository_iterator ResPool::knownRepositoriesBegin() const
+  { return _pimpl->knownRepositoriesBegin(); }
+
+  ResPool::repository_iterator ResPool::knownRepositoriesEnd() const
+  { return _pimpl->knownRepositoriesEnd(); }
+
+  Repository ResPool::reposFind( const std::string & alias_r ) const
+  { return _pimpl->reposFind( alias_r ); }
+
+  bool ResPool::autoSoftLocksEmpty() const
+  { return _pimpl->autoSoftLocks().empty(); }
+
+  ResPool::size_type ResPool::autoSoftLocksSize() const
+  { return _pimpl->autoSoftLocks().size(); }
+
+  ResPool::autoSoftLocks_iterator ResPool::autoSoftLocksBegin() const
+  { return _pimpl->autoSoftLocks().begin(); }
+
+  ResPool::autoSoftLocks_iterator ResPool::autoSoftLocksEnd() const
+  { return _pimpl->autoSoftLocks().end(); }
+
+  void ResPool::setAutoSoftLocks( const AutoSoftLocks & newLocks_r )
+  { _pimpl->setAutoSoftLocks( newLocks_r ); }
+
+  void ResPool::getActiveSoftLocks( AutoSoftLocks & activeLocks_r )
+  { _pimpl->getActiveSoftLocks( activeLocks_r ); }
+
+
+  bool ResPool::hardLockQueriesEmpty() const
+  { return _pimpl->hardLockQueries().empty(); }
+
+  ResPool::size_type ResPool::hardLockQueriesSize() const
+  { return _pimpl->hardLockQueries().size(); }
+
+  ResPool::hardLockQueries_iterator ResPool::hardLockQueriesBegin() const
+  { return _pimpl->hardLockQueries().begin(); }
+
+  ResPool::hardLockQueries_iterator ResPool::hardLockQueriesEnd() const
+  { return _pimpl->hardLockQueries().end(); }
+
+  void ResPool::setHardLockQueries( const HardLockQueries & newLocks_r )
+  { _pimpl->setHardLockQueries( newLocks_r ); }
+
+  void ResPool::getHardLockQueries( HardLockQueries & activeLocks_r )
+  { _pimpl->getHardLockQueries( activeLocks_r ); }
+
+
+  const pool::PoolTraits::ItemContainerT & ResPool::store() const
+  { return _pimpl->store(); }
+
+  const pool::PoolTraits::Id2ItemT & ResPool::id2item() const
+  { return _pimpl->id2item(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to sat::Pool:
+  //
+  ///////////////////////////////////////////////////////////////////
+  void ResPool::setRequestedLocales( const LocaleSet & locales_r )
+  { sat::Pool::instance().setRequestedLocales( locales_r ); }
+
+  bool ResPool::addRequestedLocale( const Locale & locale_r )
+  { return sat::Pool::instance().addRequestedLocale( locale_r ); }
+
+  bool ResPool::eraseRequestedLocale( const Locale & locale_r )
+  { return sat::Pool::instance().eraseRequestedLocale( locale_r ); }
+
+  const LocaleSet & ResPool::getRequestedLocales() const
+  { return sat::Pool::instance().getRequestedLocales(); }
+
+  bool ResPool::isRequestedLocale( const Locale & locale_r ) const
+  { return sat::Pool::instance().isRequestedLocale( locale_r ); }
+
+  const LocaleSet & ResPool::getAvailableLocales() const
+  { return sat::Pool::instance().getAvailableLocales(); }
+
+  bool ResPool::isAvailableLocale( const Locale & locale_r ) const
+  { return sat::Pool::instance().isAvailableLocale( locale_r ); }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const ResPool & obj )
+  {
+    return dumpPoolStats( str << "ResPool " << sat::Pool::instance() << endl << "  ",
+                          obj.begin(), obj.end() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ResPool.h b/zypp/ResPool.h
new file mode 100644 (file)
index 0000000..9ef1e1c
--- /dev/null
@@ -0,0 +1,407 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResPool.h
+ *
+*/
+#ifndef ZYPP_RESPOOL_H
+#define ZYPP_RESPOOL_H
+
+#include <iosfwd>
+
+#include "zypp/base/Deprecated.h"
+#include "zypp/base/Iterator.h"
+
+#include "zypp/pool/PoolTraits.h"
+#include "zypp/PoolItem.h"
+#include "zypp/Filter.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class SerialNumber;
+  class ResPoolProxy;
+  class Resolver;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResPool
+  //
+  /** Global ResObject pool.
+   *
+   * Explicitly shared singleton.
+   *
+   * \note Filter iterators provided by ResPool are intended to
+   * operate on internal index tables for faster access. If the
+   * the index is not yet implemented, they are realized as
+   * an ordinary filter iterator. Do not provide filter iterators
+   * here, if there is no index table for it.
+   *
+   * \include n_ResPool_nomorenameiter
+  */
+  class ResPool
+  {
+    friend std::ostream & operator<<( std::ostream & str, const ResPool & obj );
+
+    public:
+      /** \ref PoolItem */
+      typedef PoolItem                                  value_type;
+      typedef pool::PoolTraits::size_type               size_type;
+      typedef pool::PoolTraits::const_iterator          const_iterator;
+      typedef pool::PoolTraits::repository_iterator      repository_iterator;
+
+    public:
+      /** Singleton ctor. */
+      static ResPool instance();
+
+      /** preliminary */
+      ResPoolProxy proxy() const;
+
+      /** The Resolver */
+      Resolver & resolver() const;
+
+    public:
+      /** The pools serial number. Changing whenever the
+       * whenever the content changes. (Resolvables or
+       * Dependencies).
+       */
+      const SerialNumber & serial() const;
+
+    public:
+      /**  */
+      bool empty() const;
+      /**  */
+      size_type size() const;
+
+      /** \name Iterate over all PoolItems (all kinds). */
+      //@{
+      /** */
+      const_iterator begin() const
+      { return make_filter_begin( pool::ByPoolItem(), store() ); }
+      /** */
+      const_iterator end() const
+      { return make_filter_end( pool::ByPoolItem(), store() ); }
+      //@}
+
+    public:
+      /** Return the corresponding \ref PoolItem.
+       * Pool and sat pool should be in sync. Returns an empty
+       * \ref PoolItem if there is no corresponding \ref PoolItem.
+       * \see \ref PoolItem::satSolvable.
+       */
+      PoolItem find( const sat::Solvable & slv_r ) const;
+      /** \overload */
+      PoolItem find( const ResObject::constPtr & resolvable_r ) const
+      { return( resolvable_r ? find( resolvable_r->satSolvable() ) : PoolItem() ); }
+
+    public:
+      /** \name Iterate over all PoolItems matching a \c _Filter. */
+      //@{
+      template<class _Filter>
+      filter_iterator<_Filter,const_iterator> filterBegin( const _Filter & filter_r ) const
+      { return make_filter_begin( filter_r, *this ); }
+
+      template<class _Filter>
+      filter_iterator<_Filter,const_iterator> filterEnd( const _Filter & filter_r ) const
+      { return make_filter_end( filter_r, *this ); }
+      //@}
+
+      /** \name Iterate over all PoolItems by status.
+       *
+       * Simply pass the \ref ResStatus predicate you want to use as filter:
+       * \code
+       *   // iterate over all orphaned items:
+       *   for_( it, pool.byStatusBegin(&ResStatus::isOrphaned), pool.byStatusEnd(&ResStatus::isOrphaned) )
+       *   {...}
+       * \endcode
+       *
+       * Or use \ref filter::ByStatus in more complex queries:
+       * \code
+       *   // iterate over all (orphaned and recommended) items:
+       *   functor::Chain<filter::ByStatus,filter::ByStatus> myfilter( filter::ByStatus(&ResStatus::isOrphaned),
+       *                                                               filter::ByStatus(&ResStatus::isRecommended) );
+       *   for_( it, pool.filterBegin(myfilter), pool.filterEnd(myfilter) )
+       *   { ... }
+       * \endcode
+       */
+      //@{
+      filter_iterator<filter::ByStatus,const_iterator> byStatusBegin( const filter::ByStatus & filter_r ) const
+      { return make_filter_begin( filter_r, *this ); }
+
+      filter_iterator<filter::ByStatus,const_iterator> byStatusEnd( const filter::ByStatus & filter_r ) const
+      { return make_filter_end( filter_r, *this ); }
+      //@}
+
+    public:
+      /** \name Iterate over all PoolItems of a certain name and kind. */
+      //@{
+      typedef pool::ByIdent                       ByIdent;
+      typedef pool::PoolTraits::byIdent_iterator  byIdent_iterator;
+
+      byIdent_iterator byIdentBegin( const ByIdent & ident_r ) const
+      {
+       return make_transform_iterator( id2item().equal_range( ident_r.get() ).first,
+                                        pool::PoolTraits::Id2ItemValueSelector() );
+      }
+
+      byIdent_iterator byIdentBegin( ResKind kind_r, IdString name_r ) const
+      { return byIdentBegin( ByIdent(kind_r,name_r) ); }
+
+      byIdent_iterator byIdentBegin( ResKind kind_r, const C_Str & name_r ) const
+      { return byIdentBegin( ByIdent(kind_r,name_r) ); }
+
+      template<class _Res>
+      byIdent_iterator byIdentBegin( IdString name_r ) const
+      { return byIdentBegin( ByIdent(ResTraits<_Res>::kind,name_r) ); }
+
+      template<class _Res>
+      byIdent_iterator byIdentBegin( const C_Str & name_r ) const
+      { return byIdentBegin( ByIdent(ResTraits<_Res>::kind,name_r) ); }
+
+      /** Derive name and kind from \ref PoolItem. */
+      byIdent_iterator byIdentBegin( const PoolItem & pi_r ) const
+      { return byIdentBegin( ByIdent(pi_r.satSolvable()) ); }
+      /** Derive name and kind from \ref sat::Solvable. */
+      byIdent_iterator byIdentBegin( sat::Solvable slv_r ) const
+      { return byIdentBegin( ByIdent(slv_r) ); }
+      /** Takes a \ref sat::Solvable::ident string. */
+      byIdent_iterator byIdentBegin( IdString ident_r ) const
+      { return byIdentBegin( ByIdent(ident_r) ); }
+
+
+      byIdent_iterator byIdentEnd( const ByIdent & ident_r ) const
+      {
+       return make_transform_iterator( id2item().equal_range( ident_r.get() ).second,
+                                        pool::PoolTraits::Id2ItemValueSelector() );
+      }
+
+      byIdent_iterator byIdentEnd( ResKind kind_r, IdString name_r ) const
+      { return byIdentEnd( ByIdent(kind_r,name_r) ); }
+
+      byIdent_iterator byIdentEnd( ResKind kind_r, const C_Str & name_r ) const
+      { return byIdentEnd( ByIdent(kind_r,name_r) ); }
+
+      template<class _Res>
+      byIdent_iterator byIdentEnd( IdString name_r ) const
+      { return byIdentEnd( ByIdent(ResTraits<_Res>::kind,name_r) ); }
+
+      template<class _Res>
+      byIdent_iterator byIdentEnd( const C_Str & name_r ) const
+      { return byIdentEnd( ByIdent(ResTraits<_Res>::kind,name_r) ); }
+
+      /** Derive name and kind from \ref PoolItem. */
+      byIdent_iterator byIdentEnd( const PoolItem & pi_r ) const
+      { return byIdentEnd( ByIdent(pi_r.satSolvable()) ); }
+      /** Derive name and kind from \ref sat::Solvable. */
+      byIdent_iterator byIdentEnd( sat::Solvable slv_r ) const
+      { return byIdentEnd( ByIdent(slv_r) ); }
+      /** Takes a \ref sat::Solvable::ident string. */
+      byIdent_iterator byIdentEnd( IdString ident_r ) const
+      { return byIdentEnd( ByIdent(ident_r) ); }
+     //@}
+
+    public:
+      /** \name Iterate over all ResObjects of a certain kind. */
+      //@{
+      typedef filter::ByKind ByKind;
+      typedef filter_iterator<ByKind,const_iterator> byKind_iterator;
+
+      byKind_iterator byKindBegin( const ResKind & kind_r ) const
+      { return make_filter_begin( ByKind(kind_r), *this ); }
+
+      template<class _Res>
+          byKind_iterator byKindBegin() const
+      { return make_filter_begin( resfilter::byKind<_Res>(), *this ); }
+
+      byKind_iterator byKindEnd( const ResKind & kind_r ) const
+      { return make_filter_end( ByKind(kind_r), *this ); }
+
+      template<class _Res>
+          byKind_iterator byKindEnd() const
+      { return make_filter_end( resfilter::byKind<_Res>(), *this ); }
+      //@}
+
+    public:
+      /** \name Iterate over all ResObjects with a certain name (all kinds). */
+      //@{
+      typedef zypp::resfilter::ByName ByName;
+      typedef filter_iterator<ByName,const_iterator> byName_iterator;
+
+      byName_iterator byNameBegin( const std::string & name_r ) const
+      { return make_filter_begin( ByName(name_r), *this ); }
+
+      byName_iterator byNameEnd( const std::string & name_r ) const
+      { return make_filter_end( ByName(name_r), *this ); }
+      //@}
+
+    public:
+      /** \name Special iterators. */
+      //@{
+
+      //@}
+   public:
+      /** \name Iterate over all Repositories that contribute ResObjects.
+       */
+      //@{
+      size_type knownRepositoriesSize() const;
+
+      repository_iterator knownRepositoriesBegin() const;
+
+      repository_iterator knownRepositoriesEnd() const;
+
+      /** Find a \ref Repository named \c alias_r.
+       * Returns \ref Repository::norepository if there is no such \ref Repository.
+       */
+      Repository reposFind( const std::string & alias_r ) const;
+      //@}
+
+    public:
+      /** \name Handle locale support.
+       *
+       * A \ref filter::ByLocaleSupport is provided to iterate over
+       * all items supporting a specific locale.
+       *
+       * \see \ref sat::LocaleSupport for a more convenient interface.
+       *
+       * \code
+       * ResPool pool( ResPool::instance() );
+       *
+       * filter::ByLocaleSupport f( Locale("de") );
+       * for_( it, pool.filterBegin(f), pool.filterEnd(f) )
+       * {
+       *   MIL << *it << endl; // supporting "de"
+       * }
+       *
+       * f = filter::ByLocaleSupport( pool.getRequestedLocales() );
+       * for_( it, pool.filterBegin(f), pool.filterEnd(f) )
+       * {
+       *   MIL << *it << endl; // supporting any requested locale
+       * }
+       * \endcode
+       */
+      //@{
+      /** Set the requested locales.
+       * Languages to be supported by the system, e.g. language specific
+       * packages to be installed.
+       */
+      void setRequestedLocales( const LocaleSet & locales_r );
+
+      /** Add one \ref Locale to the set of requested locales.
+       * Return \c true if \c locale_r was newly added to the set.
+      */
+      bool addRequestedLocale( const Locale & locale_r );
+
+      /** Erase one \ref Locale from the set of requested locales.
+      * Return \c false if \c locale_r was not found in the set.
+       */
+      bool eraseRequestedLocale( const Locale & locale_r );
+
+      /** Return the requested locales.
+       * \see \ref setRequestedLocales
+      */
+      const LocaleSet & getRequestedLocales() const;
+
+      /** Wheter this \ref Locale is in the set of requested locales. */
+      bool isRequestedLocale( const Locale & locale_r ) const;
+
+      /** Get the set of available locales.
+       * This is computed from the package data so it actually
+       * represents all locales packages claim to support.
+       */
+      const LocaleSet & getAvailableLocales() const;
+
+      /** Wheter this \ref Locale is in the set of available locales. */
+      bool isAvailableLocale( const Locale & locale_r ) const;
+      //@}
+
+    public:
+      /** \name Handle automatic soft-locks.
+       *
+       * Solvables with and ident listed here are per default created with
+       * a setSoftLock applied. I.e. the \ref Resolver should not automatically
+       * select them, if they are just recommended.
+       *
+       * This list is considered when adding new repos to the pool. It is
+       * \b not the list of currently softLocked items.
+       *
+       * Mainly used to re-apply soft-locks remembered during the last commit.
+       */
+      //@{
+      typedef pool::PoolTraits::AutoSoftLocks          AutoSoftLocks;
+      typedef pool::PoolTraits::autoSoftLocks_iterator autoSoftLocks_iterator;
+
+      bool autoSoftLocksEmpty() const;
+      size_type autoSoftLocksSize() const;
+      autoSoftLocks_iterator autoSoftLocksBegin() const;
+      autoSoftLocks_iterator autoSoftLocksEnd() const;
+
+      /** Set a new soft-lock list.
+       * The soft-locks of existing PoolItems are adjusted according
+       * to the list. (usually called on target load)
+      */
+      void setAutoSoftLocks( const AutoSoftLocks & newLocks_r );
+
+      /** Suggest a new soft-lock list based on the current selection.
+       * Based on the the current soft-lock list. Items tagged to be
+       * installed are removed, and those tagged to be deleted are added.
+       * (usually remembered on commit).
+      */
+      void getActiveSoftLocks( AutoSoftLocks & activeLocks_r );
+      //@}
+
+    public:
+      /** \name Handle hard locks (e.g set from /etc/zypp/locks).
+       *
+       * As this kind of lock is query based, it's quite expensive.
+       *
+       * These queries are re-evaluated when adding new repos to the pool.
+       */
+      //@{
+      typedef pool::PoolTraits::HardLockQueries           HardLockQueries;
+      typedef pool::PoolTraits::hardLockQueries_iterator  hardLockQueries_iterator;
+
+      bool hardLockQueriesEmpty() const;
+      size_type hardLockQueriesSize() const;
+      hardLockQueries_iterator hardLockQueriesBegin() const;
+      hardLockQueries_iterator hardLockQueriesEnd() const;
+
+      /** Set a new set of queries.
+       * The hard-locks of existing PoolItems are adjusted according
+       * to the queries. (usually called on target load)
+       */
+      void setHardLockQueries( const HardLockQueries & newLocks_r );
+
+      /** Suggest a new set of queries based on the current selection.
+       * (usually remembered on commit).
+       */
+      void getHardLockQueries( HardLockQueries & activeLocks_r );
+      //@}
+
+    private:
+      const pool::PoolTraits::ItemContainerT & store() const;
+      const pool::PoolTraits::Id2ItemT & id2item() const;
+
+    private:
+      /** Ctor */
+      ResPool( pool::PoolTraits::Impl_Ptr impl_r );
+      /** Access to implementation. */
+      RW_pointer<pool::PoolTraits::Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ResPool Stream output */
+  std::ostream & operator<<( std::ostream & str, const ResPool & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#include "zypp/ResPoolProxy.h"
+
+#endif // ZYPP_RESPOOL_H
diff --git a/zypp/ResPoolProxy.cc b/zypp/ResPoolProxy.cc
new file mode 100644 (file)
index 0000000..c942fb2
--- /dev/null
@@ -0,0 +1,337 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResPoolProxy.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/base/Functional.h"
+
+#include "zypp/ResPoolProxy.h"
+#include "zypp/pool/PoolImpl.h"
+#include "zypp/ui/SelectableImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Tem. friend of PoolItem */
+  struct PoolItemSaver
+  {
+    void saveState( ResPool pool_r )
+    {
+      std::for_each( pool_r.begin(), pool_r.end(),
+                     std::mem_fun_ref(&PoolItem::saveState) );
+    }
+
+    void saveState( ResPool pool_r, const ResKind & kind_r )
+    {
+      std::for_each( pool_r.byKindBegin(kind_r), pool_r.byKindEnd(kind_r),
+                     std::mem_fun_ref(&PoolItem::saveState) );
+    }
+
+    void restoreState( ResPool pool_r )
+    {
+      std::for_each( pool_r.begin(), pool_r.end(),
+                     std::mem_fun_ref(&PoolItem::restoreState) );
+    }
+
+    void restoreState( ResPool pool_r, const ResKind & kind_r )
+    {
+      std::for_each( pool_r.byKindBegin(kind_r), pool_r.byKindEnd(kind_r),
+                     std::mem_fun_ref(&PoolItem::restoreState) );
+    }
+
+    bool diffState( ResPool pool_r ) const
+    {
+      // return whether some PoolItem::sameState reported \c false.
+      return( invokeOnEach( pool_r.begin(), pool_r.end(),
+                            std::mem_fun_ref(&PoolItem::sameState) ) < 0 );
+    }
+
+    bool diffState( ResPool pool_r, const ResKind & kind_r ) const
+    {
+      // return whether some PoolItem::sameState reported \c false.
+      return( invokeOnEach( pool_r.byKindBegin(kind_r), pool_r.byKindEnd(kind_r),
+                            std::mem_fun_ref(&PoolItem::sameState) ) < 0 );
+    }
+  };
+
+  namespace
+  {
+    ui::Selectable::Ptr makeSelectablePtr( pool::PoolImpl::Id2ItemT::const_iterator begin_r,
+                                           pool::PoolImpl::Id2ItemT::const_iterator end_r )
+    {
+      pool::PoolTraits::byIdent_iterator begin( begin_r, pool::PoolTraits::Id2ItemValueSelector() );
+      pool::PoolTraits::byIdent_iterator end( end_r, pool::PoolTraits::Id2ItemValueSelector() );
+      sat::Solvable solv( begin->satSolvable() );
+
+      return new ui::Selectable( ui::Selectable::Impl_Ptr( new ui::Selectable::Impl( solv.kind(), solv.name(), begin, end ) ) );
+    }
+  } // namespace
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResPoolProxy::Impl
+  //
+  /** ResPoolProxy implementation.
+   * \todo Seedup as it is still using old index
+  */
+  struct ResPoolProxy::Impl
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
+    friend std::ostream & dumpOn( std::ostream & str, const Impl & obj );
+
+    typedef std::tr1::unordered_map<sat::detail::IdType,ui::Selectable::Ptr> SelectableIndex;
+    typedef ResPoolProxy::const_iterator const_iterator;
+
+  public:
+    Impl()
+    :_pool( ResPool::instance() )
+    {}
+
+    Impl( ResPool pool_r, const pool::PoolImpl & poolImpl_r )
+    : _pool( pool_r )
+    {
+      const pool::PoolImpl::Id2ItemT & id2item( poolImpl_r.id2item() );
+      if ( ! id2item.empty() )
+      {
+        // set startpoint
+        pool::PoolImpl::Id2ItemT::const_iterator cbegin = id2item.begin();
+
+        for_( it, id2item.begin(), id2item.end() )
+        {
+          if ( it->first != cbegin->first )
+          {
+            // starting a new Selectable, create the previous one
+            ui::Selectable::Ptr p( makeSelectablePtr( cbegin, it ) );
+            _selPool.insert( SelectablePool::value_type( p->kind(), p ) );
+            _selIndex[cbegin->first] = p;
+            // remember new startpoint
+            cbegin = it;
+          }
+        }
+        // create the final one
+        ui::Selectable::Ptr p( makeSelectablePtr( cbegin, id2item.end() ) );
+        _selPool.insert( SelectablePool::value_type( p->kind(), p ) );
+        _selIndex[cbegin->first] = p;
+      }
+    }
+
+  public:
+    ui::Selectable::Ptr lookup( const pool::ByIdent & ident_r ) const
+    {
+      SelectableIndex::const_iterator it( _selIndex.find( ident_r.get() ) );
+      if ( it != _selIndex.end() )
+        return it->second;
+      return ui::Selectable::Ptr();
+    }
+
+  public:
+    bool empty() const
+    { return _selPool.empty(); }
+
+    size_type size() const
+    { return _selPool.size(); }
+
+    const_iterator begin() const
+    { return make_map_value_begin( _selPool ); }
+
+    const_iterator end() const
+    { return make_map_value_end( _selPool ); }
+
+  public:
+    bool empty( const ResKind & kind_r ) const
+    { return( _selPool.count( kind_r ) == 0 );  }
+
+    size_type size( const ResKind & kind_r ) const
+    { return _selPool.count( kind_r ); }
+
+    const_iterator byKindBegin( const ResKind & kind_r ) const
+    { return make_map_value_lower_bound( _selPool, kind_r ); }
+
+    const_iterator byKindEnd( const ResKind & kind_r ) const
+    { return make_map_value_upper_bound( _selPool, kind_r ); }
+
+  public:
+    size_type knownRepositoriesSize() const
+    { return _pool.knownRepositoriesSize(); }
+
+    repository_iterator knownRepositoriesBegin() const
+    { return _pool.knownRepositoriesBegin(); }
+
+    repository_iterator knownRepositoriesEnd() const
+    { return _pool.knownRepositoriesEnd(); }
+
+  public:
+
+    void saveState() const
+    { PoolItemSaver().saveState( _pool ); }
+
+    void saveState( const ResKind & kind_r ) const
+    { PoolItemSaver().saveState( _pool, kind_r ); }
+
+    void restoreState() const
+    { PoolItemSaver().restoreState( _pool ); }
+
+    void restoreState( const ResKind & kind_r ) const
+    { PoolItemSaver().restoreState( _pool, kind_r ); }
+
+    bool diffState() const
+    { return PoolItemSaver().diffState( _pool ); }
+
+    bool diffState( const ResKind & kind_r ) const
+    { return PoolItemSaver().diffState( _pool, kind_r ); }
+
+  private:
+    ResPool _pool;
+    mutable SelectablePool _selPool;
+    mutable SelectableIndex _selIndex;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ResPoolProxy::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const ResPoolProxy::Impl & obj )
+  {
+    return str << "ResPoolProxy (" << obj._pool.serial() << ") [" << obj._pool.size()
+               << "solv/" << obj.size()<< "sel]";
+  }
+
+  namespace detail
+  {
+    struct DumpFilter
+    {
+      bool operator()( const ui::Selectable::Ptr & selp ) const
+      { return selp->toModify(); }
+    };
+  }
+
+  /** \relates ResPoolProxy::Impl Verbose stream output */
+  inline std::ostream & dumpOn( std::ostream & str, const ResPoolProxy::Impl & obj )
+  {
+    detail::DumpFilter f;
+    return dumpRange( str << obj << " toModify: ",
+                     make_filter_begin( f, obj ),
+                     make_filter_end( f, obj ) );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResPoolProxy
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResPoolProxy::ResPoolProxy
+  //   METHOD TYPE : Ctor
+  //
+  ResPoolProxy::ResPoolProxy()
+  : _pimpl( Impl::nullimpl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResPoolProxy::ResPoolProxy
+  //   METHOD TYPE : Ctor
+  //
+  ResPoolProxy::ResPoolProxy( ResPool pool_r, const pool::PoolImpl & poolImpl_r )
+  : _pimpl( new Impl( pool_r, poolImpl_r ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResPoolProxy::~ResPoolProxy
+  //   METHOD TYPE : Dtor
+  //
+  ResPoolProxy::~ResPoolProxy()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // forward to implementation
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ui::Selectable::Ptr ResPoolProxy::lookup( const pool::ByIdent & ident_r ) const
+  { return _pimpl->lookup( ident_r ); }
+
+  bool ResPoolProxy::empty() const
+  { return _pimpl->empty(); }
+
+  ResPoolProxy::size_type ResPoolProxy::size() const
+  { return _pimpl->size(); }
+
+  ResPoolProxy::const_iterator ResPoolProxy::begin() const
+  { return _pimpl->begin(); }
+
+  ResPoolProxy::const_iterator ResPoolProxy::end() const
+  { return _pimpl->end(); }
+
+  bool ResPoolProxy::empty( const ResKind & kind_r ) const
+  { return _pimpl->empty( kind_r ); }
+
+  ResPoolProxy::size_type ResPoolProxy::size( const ResKind & kind_r ) const
+  { return _pimpl->size( kind_r ); }
+
+  ResPoolProxy::const_iterator ResPoolProxy::byKindBegin( const ResKind & kind_r ) const
+  { return _pimpl->byKindBegin( kind_r ); }
+
+  ResPoolProxy::const_iterator ResPoolProxy::byKindEnd( const ResKind & kind_r ) const
+  { return _pimpl->byKindEnd( kind_r ); }
+
+  ResPoolProxy::size_type ResPoolProxy::knownRepositoriesSize() const
+  { return _pimpl->knownRepositoriesSize(); }
+
+  ResPoolProxy::repository_iterator ResPoolProxy::knownRepositoriesBegin() const
+  { return _pimpl->knownRepositoriesBegin(); }
+
+  ResPoolProxy::repository_iterator ResPoolProxy::knownRepositoriesEnd() const
+  { return _pimpl->knownRepositoriesEnd(); }
+
+  void ResPoolProxy::saveState() const
+  { _pimpl->saveState(); }
+
+  void ResPoolProxy::saveState( const ResKind & kind_r ) const
+  { _pimpl->saveState( kind_r ); }
+
+  void ResPoolProxy::restoreState() const
+  { _pimpl->restoreState(); }
+
+  void ResPoolProxy::restoreState( const ResKind & kind_r ) const
+  { _pimpl->restoreState( kind_r ); }
+
+  bool ResPoolProxy::diffState() const
+  { return _pimpl->diffState(); }
+
+  bool ResPoolProxy::diffState( const ResKind & kind_r ) const
+  { return _pimpl->diffState( kind_r ); }
+
+  std::ostream & operator<<( std::ostream & str, const ResPoolProxy & obj )
+  { return str << *obj._pimpl; }
+
+  std::ostream & dumpOn( std::ostream & str, const ResPoolProxy & obj )
+  { return dumpOn( str, *obj._pimpl ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ResPoolProxy.h b/zypp/ResPoolProxy.h
new file mode 100644 (file)
index 0000000..6234bc5
--- /dev/null
@@ -0,0 +1,232 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResPoolProxy.h
+ *
+*/
+#ifndef ZYPP_RESPOOLPROXY_H
+#define ZYPP_RESPOOLPROXY_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/ResPool.h"
+#include "zypp/ui/Selectable.h"
+#include "zypp/ui/SelFilters.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResPoolProxy
+  //
+  /** ResPool::instance().proxy();
+   * \todo integrate it into ResPool
+  */
+  class ResPoolProxy
+  {
+    friend std::ostream & operator<<( std::ostream & str, const ResPoolProxy & obj );
+    friend std::ostream & dumpOn( std::ostream & str, const ResPoolProxy & obj );
+    typedef std::multimap<ResKind,ui::Selectable::Ptr> SelectablePool;
+
+  public:
+    /** Implementation  */
+    class Impl;
+
+    typedef MapKVIteratorTraits<SelectablePool>::Value_const_iterator const_iterator;
+    typedef SelectablePool::size_type size_type;
+
+    typedef ResPool::repository_iterator    repository_iterator;
+
+  public:
+
+    /** Default ctor: no pool
+     * Nonempty proxies are provided by \ref ResPool.
+     * \see \ref ResPool::proxy
+     * \code
+     * ResPoolProxy p( ResPool::instance().proxy() );
+     * \endcode
+     */
+    ResPoolProxy();
+
+    /** Dtor */
+    ~ResPoolProxy();
+
+  public:
+    /** \name Lookup individual Selectables. */
+    //@{
+    ui::Selectable::Ptr lookup( const pool::ByIdent & ident_r ) const;
+
+    ui::Selectable::Ptr lookup( IdString ident_r ) const
+    { return lookup( pool::ByIdent( ident_r ) ); }
+
+    ui::Selectable::Ptr lookup( ResKind kind_r, const std::string & name_r ) const
+    { return lookup( pool::ByIdent( kind_r, name_r ) ); }
+
+    ui::Selectable::Ptr lookup( const sat::Solvable & solv_r ) const
+    { return lookup( pool::ByIdent( solv_r ) ); }
+
+    ui::Selectable::Ptr lookup( const ResObject::constPtr & resolvable_r ) const
+    { return resolvable_r ? lookup( resolvable_r->satSolvable() ) : ui::Selectable::Ptr(); }
+
+    ui::Selectable::Ptr lookup( const PoolItem & pi_r ) const
+    { return lookup( pi_r.satSolvable() ); }
+    //@}
+
+  public:
+    /** \name Iterate through all Selectables of a all kind. */
+    //@{
+    bool empty() const;
+    size_type size() const;
+    const_iterator begin() const;
+    const_iterator end() const;
+    //@}
+
+    /** \name Iterate through all Selectables of a certain kind. */
+    //@{
+    /** True if there are items of a certain kind. */
+    bool empty( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      bool empty() const
+      { return empty( ResTraits<_Res>::kind ); }
+
+    /** Number of Items of a certain kind.  */
+    size_type size( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      size_type size() const
+      { return size( ResTraits<_Res>::kind ); }
+
+    const_iterator byKindBegin( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      const_iterator byKindBegin() const
+      { return byKindBegin( ResTraits<_Res>::kind ); }
+
+
+    const_iterator byKindEnd( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      const_iterator byKindEnd() const
+      { return byKindEnd( ResTraits<_Res>::kind ); }
+    //@}
+
+ public:
+   /** \name Iterate through all Repositories that contribute ResObjects.
+   */
+   //@{
+   size_type knownRepositoriesSize() const;
+
+   repository_iterator knownRepositoriesBegin() const;
+
+   repository_iterator knownRepositoriesEnd() const;
+   //@}
+
+  public:
+    /** Test whether there is at least one ui::Selectable with
+     * an installed object.
+    */
+    bool hasInstalledObj( const ResKind & kind_r ) const
+    {
+      return(    make_begin<ui::selfilter::ByHasInstalledObj>( kind_r )
+              != make_end<ui::selfilter::ByHasInstalledObj>( kind_r ) );
+    }
+
+    template<class _Res>
+      bool hasInstalledObj() const
+      { return hasInstalledObj( ResTraits<_Res>::kind ); }
+
+  public:
+    /** \name Save and restore state per kind of resolvable.
+     * Simple version, no savety net. So don't restore or diff,
+     * if you didn't save before.
+     *
+     * Diff returns true, if current stat differs from the saved
+     * state.
+    */
+    //@{
+    void saveState() const;
+
+    void saveState( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      void saveState() const
+      { return saveState( ResTraits<_Res>::kind ); }
+
+    void restoreState() const;
+
+    void restoreState( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      void restoreState() const
+      { return restoreState( ResTraits<_Res>::kind ); }
+
+    bool diffState() const;
+
+    bool diffState( const ResKind & kind_r ) const;
+
+    template<class _Res>
+      bool diffState() const
+      { return diffState( ResTraits<_Res>::kind ); }
+    //@}
+
+  private:
+    template<class _Filter>
+      filter_iterator<_Filter,const_iterator>
+      make_begin( _Filter filter_r, const ResKind & kind_r ) const
+      {
+        return make_filter_iterator( filter_r,
+                                     byKindBegin(kind_r),
+                                     byKindEnd(kind_r) );
+      }
+    template<class _Filter>
+      filter_iterator<_Filter,const_iterator>
+      make_begin( const ResKind & kind_r ) const
+      {
+        return make_begin( _Filter(), kind_r );
+      }
+
+
+    template<class _Filter>
+      filter_iterator<_Filter,const_iterator>
+      make_end( _Filter filter_r, const ResKind & kind_r ) const
+      {
+        return make_filter_iterator( filter_r,
+                                     byKindEnd(kind_r),
+                                     byKindEnd(kind_r) );
+      }
+    template<class _Filter>
+      filter_iterator<_Filter,const_iterator>
+      make_end( const ResKind & kind_r ) const
+      {
+        return make_end( _Filter(), kind_r );
+      }
+
+  private:
+    friend class pool::PoolImpl;
+    /** Ctor */
+    ResPoolProxy( ResPool pool_r, const pool::PoolImpl & poolImpl_r );
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ResPoolProxy Stream output */
+  std::ostream & operator<<( std::ostream & str, const ResPoolProxy & obj );
+
+  /** \relates ResPoolProxy Verbose stream output */
+  std::ostream & dumpOn( std::ostream & str, const ResPoolProxy & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESPOOLPROXY_H
diff --git a/zypp/ResStatus.cc b/zypp/ResStatus.cc
new file mode 100644 (file)
index 0000000..23bc8cd
--- /dev/null
@@ -0,0 +1,126 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResStatus.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/ResStatus.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  const ResStatus ResStatus::toBeInstalled              (UNINSTALLED, UNDETERMINED, TRANSACT);
+  const ResStatus ResStatus::toBeUninstalled            (INSTALLED,   UNDETERMINED, TRANSACT);
+  const ResStatus ResStatus::toBeUninstalledDueToUpgrade (INSTALLED,   UNDETERMINED, TRANSACT, EXPLICIT_INSTALL, DUE_TO_UPGRADE);
+  const ResStatus ResStatus::toBeUninstalledDueToObsolete(INSTALLED,   UNDETERMINED, TRANSACT, EXPLICIT_INSTALL, DUE_TO_OBSOLETE);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResStatus::ResStatus
+  //   METHOD TYPE : Ctor
+  //
+  ResStatus::ResStatus()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResStatus::ResStatus
+  //   METHOD TYPE : Ctor
+  //
+  ResStatus::ResStatus( bool isInstalled_r )
+  : _bitfield( isInstalled_r ? INSTALLED : UNINSTALLED )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ResStatus::~ResStatus
+  //   METHOD TYPE : Dtor
+  //
+  ResStatus::~ResStatus()
+  {}
+
+
+  ResStatus::ResStatus (enum StateValue s, enum ValidateValue v, enum TransactValue t, enum InstallDetailValue i, enum RemoveDetailValue r)
+    : _bitfield (s)
+  {
+    fieldValueAssign<ValidateField>(v);
+    fieldValueAssign<TransactField>(t);
+    if (t == TRANSACT) {
+       if (s == INSTALLED) fieldValueAssign<TransactDetailField>(r);
+       else fieldValueAssign<TransactDetailField>(i);
+    }
+  }
+
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const ResStatus & obj )
+  {
+    str << (obj.isInstalled() ? "I" : "U");
+
+    str << (obj.isBroken() ? "B" :
+       ( obj.isSatisfied() ? "S" :
+       ( obj.isNonRelevant() ? "N" : "_") ) );
+
+    str << (obj.transacts () ? "T"
+                             : (obj.isLocked() ? "L" : "_") );
+
+    if (obj.isBySolver()) str << "s";
+    else if (obj.isByApplLow()) str << "l";
+    else if (obj.isByApplHigh()) str << "h";
+    else if (obj.isByUser()) str << "u";
+
+    str << (obj.isToBeUninstalledDueToObsolete() ? "O" :
+       ( obj.isToBeUninstalledDueToUpgrade() ? "U" :
+       ( obj.isToBeInstalledSoft() ? "S" : "_" ) ) );
+
+    str << (obj.isOrphaned() ? "o" : "" );
+    str << (obj.isRecommended() ? "r" : "" );
+    str << (obj.isSuggested() ? "s" : "" );
+
+    return str;
+  }
+
+
+#define OUTS(X) case ResStatus::X: return str << #X; break
+
+  std::ostream & operator<<( std::ostream & str, ResStatus::TransactValue obj )
+  {
+    switch ( obj )
+    {
+      OUTS( KEEP_STATE );
+      OUTS( LOCKED );
+      OUTS( TRANSACT );
+    }
+    return str;
+  }
+
+  std::ostream & operator<<( std::ostream & str, ResStatus::TransactByValue obj )
+  {
+    switch ( obj )
+    {
+      OUTS( SOLVER );
+      OUTS( APPL_LOW );
+      OUTS( APPL_HIGH );
+      OUTS( USER );
+    }
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ResStatus.h b/zypp/ResStatus.h
new file mode 100644 (file)
index 0000000..c962640
--- /dev/null
@@ -0,0 +1,730 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ResStatus.h
+ *
+*/
+#ifndef ZYPP_RESSTATUS_H
+#define ZYPP_RESSTATUS_H
+
+#include <inttypes.h>
+#include <iosfwd>
+#include "zypp/Bit.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace resstatus
+  {
+    class UserLockQueryManip;
+    class StatusBackup;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ResStatus
+  //
+  /** Status bitfield.
+   *
+   * \li \c StateField Whether the resolvable is or uninstalled (available).
+   * \li \c ValidateField Validate status computed by the solver as
+   *        nonrelevant: it is unimportant for the user
+   *       satisfied: it important nothing has to be done
+   *       broken: it is incomplete. So e.g. an update is needed
+   * \li \c TransactField Wheter to transact this resolvable
+   *        (delete if installed install if uninstalled).
+   *        In case the resolvable is locked, only USER may modify the
+   *        transact bit.
+   * \li \c TransactByField Who triggered the transaction. Transaction
+   *        bit may be reset by higer levels only.
+   * \li \c TransactDetailField Reason why the Resolvable transacts.
+   *        Splitted into \c InstallDetailValue and \c RemoveDetailValue
+   *        dependent on the kind of transaction.
+   * \li \c WeakField The solvable will be recommended/suggested by
+   *        a to be installed/deleted solvable.
+   *
+  */
+  class ResStatus
+  {
+    friend std::ostream & operator<<( std::ostream & str, const ResStatus & obj );
+    friend bool operator==( const ResStatus & lhs, const ResStatus & rhs );
+
+  public:
+    /** \name BitField range definitions.
+     *
+     * \note Enlarge FieldType if more bit's needed. It's not yet
+     * checked by the compiler.
+     */
+    //@{
+    typedef uint16_t FieldType;
+    typedef bit::BitField<FieldType> BitFieldType;
+    // Bit Ranges within FieldType defined by 1st bit and size:
+    typedef bit::Range<FieldType,0,                          1> StateField;
+    typedef bit::Range<FieldType,StateField::end,            2> ValidateField;
+    typedef bit::Range<FieldType,ValidateField::end,         2> TransactField;
+    typedef bit::Range<FieldType,TransactField::end,         2> TransactByField;
+    typedef bit::Range<FieldType,TransactByField::end,       2> TransactDetailField;
+    typedef bit::Range<FieldType,TransactDetailField::end,   1> LicenceConfirmedField;
+    typedef bit::Range<FieldType,LicenceConfirmedField::end, 3> WeakField;
+    typedef bit::Range<FieldType,WeakField::end,             1> UserLockQueryField; // internal
+    // enlarge FieldType if more bit's needed. It's not yet
+    // checked by the compiler.
+    //@}
+  public:
+
+    /** \name Status values.
+     *
+     * Each enum corresponds to a BitField range.
+     * \note Take care that enumerator values actually fit into
+     * the corresponding field. It's not yet checked by the compiler.
+     */
+    //@{
+    enum StateValue
+      {
+        UNINSTALLED = bit::RangeValue<StateField,0>::value,
+        INSTALLED   = bit::RangeValue<StateField,1>::value
+      };
+    enum ValidateValue
+      {
+       UNDETERMINED = bit::RangeValue<ValidateField,0>::value,
+        BROKEN       = bit::RangeValue<ValidateField,1>::value,
+        SATISFIED    = bit::RangeValue<ValidateField,2>::value,
+        NONRELEVANT  = bit::RangeValue<ValidateField,3>::value
+      };
+    enum TransactValue
+      {
+        KEEP_STATE = bit::RangeValue<TransactField,0>::value,
+        LOCKED     = bit::RangeValue<TransactField,1>::value, // locked, must not transact
+        TRANSACT   = bit::RangeValue<TransactField,2>::value  // transact according to state
+      };
+    enum TransactByValue
+      {
+        SOLVER    = bit::RangeValue<TransactByField,0>::value,
+        APPL_LOW  = bit::RangeValue<TransactByField,1>::value,
+        APPL_HIGH = bit::RangeValue<TransactByField,2>::value,
+        USER      = bit::RangeValue<TransactByField,3>::value
+      };
+
+    enum DetailValue
+      {
+        /** Detail for no transact, i.e. reset any Install/RemoveDetailValue. */
+        NO_DETAIL = bit::RangeValue<TransactDetailField,0>::value,
+      };
+    enum InstallDetailValue
+      {
+        EXPLICIT_INSTALL = bit::RangeValue<TransactDetailField,0>::value,
+        SOFT_INSTALL     = bit::RangeValue<TransactDetailField,1>::value
+      };
+    enum RemoveDetailValue
+      {
+        EXPLICIT_REMOVE = bit::RangeValue<TransactDetailField,0>::value,
+       SOFT_REMOVE     = bit::RangeValue<TransactDetailField,1>::value,
+        DUE_TO_OBSOLETE = bit::RangeValue<TransactDetailField,2>::value,
+        DUE_TO_UPGRADE  = bit::RangeValue<TransactDetailField,3>::value
+      };
+
+    enum LicenceConfirmedValue
+      {
+        LICENCE_UNCONFIRMED = bit::RangeValue<LicenceConfirmedField,0>::value,
+        LICENCE_CONFIRMED   = bit::RangeValue<LicenceConfirmedField,1>::value
+      };
+
+    enum WeakValue     // Unlike the other fields those are BITS that may be or'ed!
+      {
+        NO_WEAK                = bit::RangeValue<WeakField,0>::value,
+        SUGGESTED              = bit::RangeValue<WeakField,1<<0>::value,
+       RECOMMENDED             = bit::RangeValue<WeakField,1<<1>::value,
+       ORPHANED                = bit::RangeValue<WeakField,1<<2>::value
+      };
+
+    enum UserLockQuery // internal
+      {
+        USERLOCK_NOMATCH       = bit::RangeValue<UserLockQueryField,0>::value,
+        USERLOCK_MATCH         = bit::RangeValue<UserLockQueryField,1>::value
+      };
+    //@}
+
+  public:
+
+    /** Default ctor. */
+    ResStatus();
+
+    /** Ctor setting the initial . */
+    ResStatus( bool isInstalled_r );
+
+    /** Dtor. */
+    ~ResStatus();
+
+    /** Debug helper returning the bitfield.
+     * It's save to expose the bitfield, as it can't be used to
+     * recreate a ResStatus. So it is not possible to bypass
+     * transition rules.
+    */
+    BitFieldType bitfield() const
+    { return _bitfield; }
+
+  public:
+
+    bool isLicenceConfirmed() const
+    { return fieldValueIs<LicenceConfirmedField>( LICENCE_CONFIRMED ); }
+
+    void setLicenceConfirmed( bool toVal_r = true )
+    { fieldValueAssign<LicenceConfirmedField>( toVal_r ? LICENCE_CONFIRMED : LICENCE_UNCONFIRMED ); }
+
+  public:
+    bool isRecommended() const
+    { return _bitfield.test( RECOMMENDED ); }
+
+    bool isSuggested() const
+    { return _bitfield.test( SUGGESTED ); }
+
+    bool isOrphaned() const
+    { return _bitfield.test( ORPHANED ); }
+
+    void resetWeak()
+    { return fieldValueAssign<WeakField>( NO_WEAK ); }
+
+    void setRecommended( bool toVal_r = true )
+    { _bitfield.set( RECOMMENDED, toVal_r ); }
+
+    void setSuggested( bool toVal_r = true )
+    { _bitfield.set( SUGGESTED, toVal_r ); }
+
+    void setOrphaned( bool toVal_r = true )
+    { _bitfield.set( ORPHANED, toVal_r ); }
+
+  public:
+    ValidateValue validate() const
+    { return (ValidateValue)_bitfield.value<ValidateField>(); }
+
+    bool isUndetermined() const
+    { return fieldValueIs<ValidateField>( UNDETERMINED ); }
+
+    bool isSatisfied() const
+    { return fieldValueIs<ValidateField>( SATISFIED ); }
+
+    bool isBroken() const
+    { return fieldValueIs<ValidateField>( BROKEN ); }
+
+    bool isNonRelevant() const
+    { return fieldValueIs<ValidateField>( NONRELEVANT ); }
+
+  public:
+    // These two are IMMUTABLE!
+
+    bool isInstalled() const
+    { return fieldValueIs<StateField>( INSTALLED ); }
+
+    bool isUninstalled() const
+    { return fieldValueIs<StateField>( UNINSTALLED ); }
+
+  public:
+
+    bool staysInstalled() const
+    { return isInstalled() && !transacts(); }
+
+    bool wasInstalled() const { return staysInstalled(); }     //for old status
+
+    bool isToBeInstalled() const
+    { return isUninstalled() && transacts(); }
+
+    bool staysUninstalled() const
+    { return isUninstalled() && !transacts(); }
+
+    bool wasUninstalled() const { return staysUninstalled(); } // for old status
+
+    bool isToBeUninstalled() const
+    { return isInstalled() && transacts(); }
+
+    bool isLocked() const
+    { return fieldValueIs<TransactField>( LOCKED ); }
+
+    bool isUserLocked() const
+    { return isLocked() && isByUser(); }
+
+    bool isSoftLocked( TransactByValue causer_r = USER ) const
+    { return isKept() && fieldValueIs<TransactByField>( causer_r ); }
+
+    bool isKept() const
+    { return fieldValueIs<TransactField>( KEEP_STATE ); }
+
+    bool transacts() const
+    { return fieldValueIs<TransactField>( TRANSACT ); }
+
+    TransactValue getTransactValue() const
+    { return (TransactValue)_bitfield.value<TransactField>(); }
+
+    /** True if would be on system after commit. */
+    bool onSystem() const
+    { return( isInstalled() != transacts() ); }
+
+    /** True if would be off system after commit. */
+    bool offSystem() const
+    { return ! onSystem(); }
+
+    bool isBySolver() const
+    { return fieldValueIs<TransactByField>( SOLVER ); }
+
+    bool isByApplLow() const
+    { return fieldValueIs<TransactByField>( APPL_LOW ); }
+
+    bool isByApplHigh() const
+    { return fieldValueIs<TransactByField>( APPL_HIGH ); }
+
+    bool isByUser() const
+    { return fieldValueIs<TransactByField>( USER ); }
+
+    TransactByValue getTransactByValue() const
+    { return (TransactByValue)_bitfield.value<TransactByField>(); }
+
+    bool setTransactByValue(TransactByValue causer)
+    {
+       if ( isLessThan<TransactByField>( causer ) ) {
+           fieldValueAssign<TransactByField>( causer );
+           return true;
+       } else {
+           return false;
+       }
+    }
+
+    bool isToBeUninstalledDueToObsolete () const
+    { return isToBeUninstalled() && fieldValueIs<TransactDetailField>( DUE_TO_OBSOLETE ); }
+
+    bool isToBeUninstalledDueToUpgrade() const
+    { return isToBeUninstalled() && fieldValueIs<TransactDetailField>( DUE_TO_UPGRADE ); }
+
+    bool isToBeInstalledSoft () const
+    { return isToBeInstalled() && fieldValueIs<TransactDetailField>( SOFT_INSTALL ); }
+
+    bool isToBeInstalledNotSoft () const
+    { return isToBeInstalled() && !fieldValueIs<TransactDetailField>( SOFT_INSTALL ); }
+
+    bool isToBeUninstalledSoft () const
+    { return isToBeUninstalled() && fieldValueIs<TransactDetailField>( SOFT_REMOVE ); }
+
+  private:
+
+    /** \name Internal hard lock maintainance */
+    //@{
+    friend class resstatus::UserLockQueryManip;
+
+    bool isUserLockQueryMatch() const
+    { return fieldValueIs<UserLockQueryField>( USERLOCK_MATCH ); }
+
+    void setUserLockQueryMatch( bool match_r )
+    { fieldValueAssign<UserLockQueryField>( match_r ? USERLOCK_MATCH : USERLOCK_NOMATCH ); }
+    //@}
+
+  public:
+
+    //------------------------------------------------------------------------
+    // get/set functions, returnig \c true if requested status change
+    // was successfull (i.e. leading to the desired transaction).
+    // If a lower level (e.g.SOLVER) wants to transact, but it's
+    // already set by a higher level, \c true should be returned.
+    // Removing a higher levels transaction bit should fail.
+    //
+    // The may functions checks only, if the action would return true
+    // if it is called.
+
+    /** Set TransactValue.
+     * Convenience to set TransactValue from enum.
+     */
+    bool setTransactValue( TransactValue newVal_r, TransactByValue causer_r )
+    {
+      switch ( newVal_r )
+        {
+        case KEEP_STATE:
+          return setTransact( false, causer_r );
+          break;
+        case LOCKED:
+          return setLock( true, causer_r );
+          break;
+        case TRANSACT:
+          return setTransact( true, causer_r );
+          break;
+        }
+      return false;
+    }
+
+    bool maySetTransactValue( TransactValue newVal_r, TransactByValue causer_r )
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setTransactValue( newVal_r, causer_r );
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    /** Apply a lock (prevent transaction).
+     * Currently by USER or APPL_HIGH only, but who knows...
+     * Set LOCKED from KEEP_STATE to be shure all transaction
+     * details were reset properly.
+    */
+    bool setLock( bool toLock_r, TransactByValue causer_r )
+    {
+      if ( toLock_r == isLocked() )
+        {
+          // we're already in the desired state, but in case of
+          // LOCKED, remember a superior causer.
+          if ( isLocked() && isLessThan<TransactByField>( causer_r ) )
+            fieldValueAssign<TransactByField>( causer_r );
+           return true;
+        }
+      // Here: Lock status is to be changed:
+      if ( causer_r != USER && causer_r != APPL_HIGH )
+        return false;
+      if ( toLock_r ) {
+        // We're in unlocked state, which includes TRANSACT.
+        // Causer must be allowed to reset this. But from
+        // KEEP_STATE every causer is allowed to set the lock.
+        if ( ! setTransact( false, causer_r ) )
+          return false;
+        fieldValueAssign<TransactField>( LOCKED );
+        fieldValueAssign<TransactByField>( causer_r );
+      } else {
+        // To leave Locked state it needs a superior causer.
+        if ( isGreaterThan<TransactByField>( causer_r ) )
+          return false;
+        fieldValueAssign<TransactField>( KEEP_STATE );
+        fieldValueAssign<TransactByField>( SOLVER ); // reset to lowest causer
+                                                    // in order to distinguish from keep_state_by_user
+      }
+      return true;
+    }
+
+    bool maySetLock( bool to_r, TransactByValue causer_r )
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setLock( to_r, causer_r );
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    /** Toggle between TRANSACT and KEEP_STATE.
+     * LOCKED state means KEEP_STATE. But in contrary to KEEP_STATE,
+     * LOCKED state is immutable for \a causer_r less than TransactByValue.
+     * KEEP_STATE may be canged by any \a causer_r.
+    */
+    bool setTransact( bool toTansact_r, TransactByValue causer_r )
+    {
+      if ( toTansact_r == transacts() )
+        {
+          // we're already in the desired state, but in case of
+          // TRANSACT, remember a superior causer.
+          if ( transacts() && isLessThan<TransactByField>( causer_r ) )
+              fieldValueAssign<TransactByField>( causer_r );
+
+         fieldValueAssign<TransactDetailField>( NO_DETAIL ); // Details has to be set again
+          return true;
+        }
+      // Here: transact status is to be changed:
+      if (    ! fieldValueIs<TransactField>( KEEP_STATE )
+             && isGreaterThan<TransactByField>( causer_r ) ) {
+        return false;
+      }
+
+      if ( toTansact_r )
+      {
+          fieldValueAssign<TransactField>( TRANSACT );
+      }
+      else
+      {
+          fieldValueAssign<TransactField>( KEEP_STATE );
+      }
+      fieldValueAssign<TransactDetailField>( NO_DETAIL ); // Details has to be set again
+      fieldValueAssign<TransactByField>( causer_r );
+      return true;
+    }
+
+    bool maySetTransact( bool val_r, TransactByValue causer )
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setTransact (val_r, causer);
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    /** */
+    bool setSoftLock( TransactByValue causer_r )
+    {
+      if ( ! setTransact( false, causer_r ) )
+        return false;
+      if ( fieldValueIs<TransactField>( KEEP_STATE )
+           && isLessThan<TransactByField>( causer_r ) )
+        fieldValueAssign<TransactByField>( causer_r );
+      return true;
+    }
+
+    /** Not the same as setTransact( false ).
+     */
+    bool resetTransact( TransactByValue causer_r )
+    {
+      if ( ! setTransact( false, causer_r ) )
+        return false;
+      if ( fieldValueIs<TransactField>( KEEP_STATE ) )
+        fieldValueAssign<TransactByField>( SOLVER );
+      return true;
+    }
+
+    /** Soft toggle between TRANSACT and KEEP_STATE.
+     * Similar to setTransact, but leaving KEEP_STATE also requires
+     * a superior \a causerLimit_r. So this is a kind of soft lock.
+     * \code
+     * // SOLVER wants to set TRANSACT, iff KEEP_STATE is
+     * // not superior to APPL_LOW.
+     * setSoftTransact( true, SOLVER, APPL_LOW );
+     * \endcode
+    */
+    bool setSoftTransact( bool toTansact_r, TransactByValue causer_r,
+                          TransactByValue causerLimit_r )
+    {
+      if ( fieldValueIs<TransactField>( KEEP_STATE )
+           && toTansact_r != transacts()
+           && isGreaterThan<TransactByField>( causerLimit_r ) )
+        {
+          // any transact status change requires a superior causer.
+          return false;
+        }
+      return setTransact( toTansact_r, causer_r );
+    }
+
+    bool setSoftTransact( bool toTansact_r, TransactByValue causer_r )
+    { return setSoftTransact( toTansact_r, causer_r, causer_r ); }
+
+    bool maySetSoftTransact( bool val_r, TransactByValue causer,
+                             TransactByValue causerLimit_r )
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setSoftTransact( val_r, causer, causerLimit_r );
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    bool maySetSoftTransact( bool val_r, TransactByValue causer )
+    { return maySetSoftTransact( val_r, causer, causer ); }
+
+    bool setToBeInstalled (TransactByValue causer)
+    {
+      if (isInstalled()) return false;
+      return setTransact (true, causer);
+    }
+
+    bool maySetToBeInstalled (TransactByValue causer)
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setToBeInstalled (causer);
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    bool setToBeUninstalled (TransactByValue causer)
+    {
+      if (!isInstalled()) return false;
+      return setTransact (true, causer);
+    }
+
+    bool maySetToBeUninstalled (TransactByValue causer)
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setToBeUninstalled (causer);
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    //------------------------------------------------------------------------
+    // *** These are only for the Resolver ***
+
+    bool setToBeUninstalledDueToObsolete ( )
+    {
+      if (!setToBeUninstalled (SOLVER)) return false;
+      fieldValueAssign<TransactDetailField>(DUE_TO_OBSOLETE);
+      return true;
+    }
+
+    bool setToBeUninstalledDueToUpgrade ( TransactByValue causer )
+    {
+      if (!setToBeUninstalled (causer)) return false;
+      fieldValueAssign<TransactDetailField>(DUE_TO_UPGRADE);
+      return true;
+    }
+
+    bool setToBeInstalledSoft ( )
+    {
+      if (isInstalled()
+         || !setSoftTransact (true, SOLVER))
+         return false;
+
+      fieldValueAssign<TransactDetailField>(SOFT_INSTALL);
+      return true;
+    }
+
+    bool setToBeUninstalledSoft ( )
+    {
+      if (!isInstalled()
+         || !setSoftTransact (true, SOLVER))
+         return false;
+
+      fieldValueAssign<TransactDetailField>(SOFT_REMOVE);
+      return true;
+    }
+
+    bool maySetToBeUninstalledSoft ()
+    {
+       bit::BitField<FieldType> savBitfield = _bitfield;
+       bool ret = setToBeUninstalledSoft ();
+       _bitfield = savBitfield;
+       return ret;
+    }
+
+    bool isSoftInstall () {
+        return fieldValueIs<TransactDetailField> (SOFT_INSTALL);
+    }
+
+    bool isSoftUninstall () {
+        return fieldValueIs<TransactDetailField> (SOFT_REMOVE);
+    }
+
+    bool setSoftInstall (bool flag) {
+        fieldValueAssign<TransactDetailField>(flag?SOFT_INSTALL:0);
+       return true;
+    }
+
+    bool setSoftUninstall (bool flag) {
+        fieldValueAssign<TransactDetailField>(flag?SOFT_REMOVE:0);
+       return true;
+    }
+
+    bool setUndetermined ()
+    {
+      fieldValueAssign<ValidateField>(UNDETERMINED);
+      return true;
+    }
+
+    bool setSatisfied ()
+    {
+      fieldValueAssign<ValidateField>(SATISFIED);
+      return true;
+    }
+
+    bool setBroken ()
+    {
+      fieldValueAssign<ValidateField>(BROKEN);
+      return true;
+    }
+
+    bool setNonRelevant ()
+    {
+      fieldValueAssign<ValidateField>(NONRELEVANT);
+      return true;
+    }
+
+    bool setStatus( ResStatus newStatus_r )
+    {
+      // State field is immutable!
+      if ( _bitfield.value<StateField>() != newStatus_r._bitfield.value<StateField>() )
+        return false;
+      // Transaction state change allowed?
+      if ( ! setTransactValue( newStatus_r.getTransactValue(), newStatus_r.getTransactByValue() ) )
+        return false;
+
+      // Ok, we take it all..
+      _bitfield = newStatus_r._bitfield;
+      return true;
+    }
+
+    /** \name Builtin ResStatus constants. */
+    //@{
+    static const ResStatus toBeInstalled;
+    static const ResStatus toBeUninstalled;
+    static const ResStatus toBeUninstalledDueToUpgrade;
+    static const ResStatus toBeUninstalledDueToObsolete;
+    //@}
+
+  private:
+    /** Ctor for intialization of builtin constants. */
+    ResStatus( StateValue s,
+               ValidateValue v      = UNDETERMINED,
+               TransactValue t      = KEEP_STATE,
+               InstallDetailValue i = EXPLICIT_INSTALL,
+               RemoveDetailValue r  = EXPLICIT_REMOVE );
+
+    /** Return whether the corresponding Field has value \a val_r.
+    */
+    template<class _Field>
+      bool fieldValueIs( FieldType val_r ) const
+    { return _bitfield.isEqual<_Field>( val_r ); }
+
+    /** Set the corresponding Field to value \a val_r.
+    */
+    template<class _Field>
+      void fieldValueAssign( FieldType val_r )
+    { _bitfield.assign<_Field>( val_r ); }
+
+    /** compare two values.
+    */
+    template<class _Field>
+      bool isGreaterThan( FieldType val_r )
+    { return _bitfield.value<_Field>() > val_r; }
+
+    template<class _Field>
+      bool isLessThan( FieldType val_r )
+    { return _bitfield.value<_Field>() < val_r; }
+
+  private:
+    friend class resstatus::StatusBackup;
+    BitFieldType _bitfield;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ResStatus Stream output */
+  std::ostream & operator<<( std::ostream & str, const ResStatus & obj );
+
+  /** \relates ResStatus Stream output */
+  std::ostream & operator<<( std::ostream & str, ResStatus::TransactValue obj );
+
+  /** \relates ResStatus Stream output */
+  std::ostream & operator<<( std::ostream & str, ResStatus::TransactByValue obj );
+
+  /** \relates ResStatus */
+  inline bool operator==( const ResStatus & lhs, const ResStatus & rhs )
+  { return lhs._bitfield == rhs._bitfield; }
+
+  /** \relates ResStatus */
+  inline bool operator!=( const ResStatus & lhs, const ResStatus & rhs )
+  { return ! (lhs == rhs); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  namespace resstatus
+  {
+    class StatusBackup
+    {
+      public:
+        StatusBackup()
+        : _status( 0 )
+        {}
+
+        StatusBackup( ResStatus & status_r )
+        : _status( &status_r )
+        , _bitfield( _status->_bitfield )
+        {}
+
+        void replay()
+        { if ( _status ) _status->_bitfield = _bitfield; }
+
+      private:
+        ResStatus *             _status;
+        ResStatus::BitFieldType _bitfield;
+    };
+  }
+
+ /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESSTATUS_H
diff --git a/zypp/ResTraits.h b/zypp/ResTraits.h
new file mode 100644 (file)
index 0000000..1f8088d
--- /dev/null
@@ -0,0 +1,145 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ResTraits.h
+ *
+*/
+#ifndef ZYPP_RESTRAITS_H
+#define ZYPP_RESTRAITS_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ResKind.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace traits
+  { /////////////////////////////////////////////////////////////////
+
+    /** Those are denoted to be installed, if the
+     *  solver verifies them as being satisfied. */
+    inline bool isPseudoInstalled( ResKind kind_r )
+    { return( kind_r == ResKind::patch || kind_r == ResKind::pattern ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace traits
+  ///////////////////////////////////////////////////////////////////
+
+   /** \defgroup ZYPP_RESOLVABLE_SMART_POINTER_TYPES
+   * Resolvable smart pointer types.
+   *
+   * Forward declaration of all Resolvable smart pointer
+   * types provided in \c ResTraits.h (recommended in header files):
+   * \code
+   * #include<zypp/ResTraits.h>
+   *
+   * Resolvable_Ptr                      // Resolvable *
+   * ResTraits<Resolvable>::PtrType      // same as above
+   *
+   * Resolvable_constPtr                 // const Resolvable *
+   * ResTraits<Resolvable>::constPtrType // same as above
+   * \endcode
+   *
+   * Synonym, but requires \c Resolvable.h being included:
+   * \code
+   * #include<zypp/Resolvable.h>
+   *
+   * Resolvable::Ptr        // same as Resolvable_Ptr but requires Resolvable.h
+   * Resolvable::constPtr   // same as Resolvable_constPtr but requires Resolvable.h
+   * \endcode
+   *
+   * \note When adding a \c NewResolvable type here, dont forgett to
+   * put <tt>IMPL_PTR_TYPE(NewResolvable);</tt> into the \c NewResolvable.cc.
+   */
+  //@{
+  DEFINE_PTR_TYPE( Resolvable );
+  DEFINE_PTR_TYPE( ResObject );
+
+  DEFINE_PTR_TYPE( Package );
+  DEFINE_PTR_TYPE( SrcPackage );
+  DEFINE_PTR_TYPE( Pattern );
+  DEFINE_PTR_TYPE( Product );
+  DEFINE_PTR_TYPE( Patch );
+  //@}
+
+  /** Frequently associated. */
+  class PoolItem;
+
+  /** ResTraits. Defines common types and the ResKind value. */
+  template<typename _Res>
+    struct ResTraits
+    {
+      typedef ResKind                   KindType;
+      typedef intrusive_ptr<_Res>       PtrType;
+      typedef intrusive_ptr<const _Res> constPtrType;
+
+      static const ResKind              kind;
+
+      /** Those are denoted to be installed, if the
+       *  solver verifies them as being satisfied. */
+      static bool isPseudoInstalled()   { return traits::isPseudoInstalled( kind ); }
+    };
+
+  /** ResTraits specialisation for Resolvable.
+   * Resolvable is common base and has no Kind value.
+   */
+  template<>
+    struct ResTraits<Resolvable>
+    {
+      typedef ResKind                         KindType;
+      typedef intrusive_ptr<Resolvable>       PtrType;
+      typedef intrusive_ptr<const Resolvable> constPtrType;
+    };
+
+  /** ResTraits specialisation for ResObject.
+   * ResObject is common base and has no Kind value.
+   */
+  template<>
+    struct ResTraits<ResObject>
+    {
+      typedef ResKind                        KindType;
+      typedef intrusive_ptr<ResObject>       PtrType;
+      typedef intrusive_ptr<const ResObject> constPtrType;
+    };
+
+  /** Convenient access to well known ResKinds.
+   * \code
+   * ResKind packagekind = ResKind::package;
+   * ResKind packagekind = resKind<Package>();
+   * \endcode
+  */
+  template<typename _Res>
+    inline ResKind resKind() { return ResTraits<_Res>::kind; }
+
+  /** Convenient test for ResKinds.
+   * \code
+   * ResKind value;
+   * if ( ResKind::package == value )
+   * if ( resKind<Package>() == value )
+   * if ( isKind<Package>( value ) )
+   * \endcode
+   */
+  template<typename _Res>
+    inline bool isKind( const ResKind & val_r )
+    { return( resKind<_Res>() == val_r ); }
+  /** \overload */
+  template<typename _Res>
+    inline bool isKind( const std::string & val_r )
+    { return( resKind<_Res>() == val_r ); }
+  /** \overload */
+  template<typename _Res>
+    inline bool isKind( const char * val_r )
+    { return( resKind<_Res>() == val_r ); }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESTRAITS_H
diff --git a/zypp/Resolvable.cc b/zypp/Resolvable.cc
new file mode 100644 (file)
index 0000000..aa8ccad
--- /dev/null
@@ -0,0 +1,57 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Resolvable.cc
+ *
+*/
+#include "zypp/Resolvable.h"
+#include "zypp/ResObject.h"
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(Resolvable);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolvable::Resolvable
+  //   METHOD TYPE : Ctor
+  //
+  Resolvable::Resolvable( const sat::Solvable & solvable_r )
+  : sat::Solvable( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolvable::~Resolvable
+  //   METHOD TYPE : Dtor
+  //
+  Resolvable::~Resolvable()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolvable::poolItem
+  //   METHOD TYPE : PoolItem
+  //
+  PoolItem Resolvable::poolItem() const
+  { return PoolItem( *this ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolvable::dumpOn
+  //   METHOD TYPE : std::ostream &
+  //
+  std::ostream & Resolvable::dumpOn( std::ostream & str ) const
+  { return str << satSolvable(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Resolvable.h b/zypp/Resolvable.h
new file mode 100644 (file)
index 0000000..e9a74b0
--- /dev/null
@@ -0,0 +1,258 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Resolvable.h
+ *
+*/
+#ifndef ZYPP_RESOLVABLE_H
+#define ZYPP_RESOLVABLE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/sat/Solvable.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class PoolItem;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Resolvable
+  //
+  /** Interface base for resolvable objects (identification and dependencies).
+   * \todo Merge with ResObject
+  */
+  class Resolvable : protected sat::Solvable,
+                     public base::ReferenceCounted, private base::NonCopyable
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Resolvable & obj );
+
+  public:
+    typedef Resolvable               Self;
+    typedef ResTraits<Self>          TraitsType;
+    typedef TraitsType::KindType     Kind;
+    typedef TraitsType::PtrType      Ptr;
+    typedef TraitsType::constPtrType constPtr;
+
+  public:
+#ifndef SWIG // Swig treats it as syntax error
+    /** Whether this represents a valid- or no-solvable. */
+    using sat::Solvable::operator bool_type;
+#endif
+    /** Whether this represents an installed solvable. */
+    bool isSystem() const
+    { return sat::Solvable::isSystem(); }
+
+    /** \copydoc sat::Solvable::onSystemByUser() */
+    bool onSystemByUser() const
+    { return sat::Solvable::onSystemByUser(); }
+
+    IdString ident() const
+    { return sat::Solvable::ident(); }
+
+    ResKind kind() const
+    { return sat::Solvable::kind(); }
+
+    bool isKind( const ResKind & kind_r ) const
+    { return sat::Solvable::isKind( kind_r ); }
+
+    template<class _Res>
+    bool isKind() const
+    { return sat::Solvable::isKind<_Res>(); }
+
+    std::string name() const
+    { return sat::Solvable::name(); }
+
+    Edition edition() const
+    { return sat::Solvable::edition(); }
+
+    Arch arch() const
+    { return sat::Solvable::arch(); }
+
+    /** Whether different versions of this package can be installed at the same time.
+     * Per default \c false. \see also \ref ZConfig::multiversion.
+     */
+    bool multiversionInstall() const
+    { return sat::Solvable::multiversionInstall(); }
+
+    /** \name Dependencies. */
+    //@{
+    /** Select by Dep. */
+    Capabilities dep( Dep which_r ) const
+    { return operator[]( which_r ); }
+
+    Capabilities operator[]( Dep which_r ) const
+    { return sat::Solvable::operator[]( which_r ); }
+
+    Capabilities provides()    const
+    { return sat::Solvable::provides(); }
+
+    Capabilities requires()    const
+    { return sat::Solvable::requires(); }
+
+    Capabilities conflicts()   const
+    { return sat::Solvable::conflicts(); }
+
+    Capabilities obsoletes()   const
+    { return sat::Solvable::obsoletes(); }
+
+    Capabilities recommends()  const
+    { return sat::Solvable::recommends(); }
+
+    Capabilities suggests()    const
+    { return sat::Solvable::suggests(); }
+
+    Capabilities enhances()    const
+    { return sat::Solvable::enhances(); }
+
+    Capabilities supplements() const
+    { return sat::Solvable::supplements(); }
+
+    Capabilities prerequires() const
+    { return sat::Solvable::prerequires(); }
+
+    CapabilitySet providesNamespace( const std::string & namespace_r ) const
+    { return sat::Solvable::providesNamespace( namespace_r ); }
+
+    CapabilitySet valuesOfNamespace( const std::string & namespace_r ) const
+    { return sat::Solvable::valuesOfNamespace( namespace_r ); }
+    //@}
+
+  public:
+    /** Access the corresponding \ref sat:::Solvable. */
+    const sat::Solvable & satSolvable() const { return *this; }
+
+    /** Access the corresponding \ref PoolItem. */
+    PoolItem poolItem() const;
+
+  protected:
+    /** Ctor */
+    Resolvable( const sat::Solvable & solvable_r );
+    /** Dtor */
+    virtual ~Resolvable();
+    /** Helper for stream output */
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+ };
+ ///////////////////////////////////////////////////////////////////
+
+ /** \relates Resolvable Stream output */
+ inline std::ostream & operator<<( std::ostream & str, const Resolvable & obj )
+ { return obj.dumpOn( str ); }
+
+ /** \relates Resolvable More verbose stream output including dependencies */
+ inline std::ostream & dumpOn( std::ostream & str, const Resolvable & obj )
+ { return dumpOn( str, obj.satSolvable() ); }
+
+  /** Test whether a Resolvable::Ptr is of a certain Kind.
+   * \return \c Ture iff \a p is not \c NULL and points to a Resolvable
+   * of the specified Kind.
+   * \relates Resolvable
+   * \code
+   * isKind<Package>(resPtr);
+   * \endcode
+  */
+  template<class _Res>
+    inline bool isKind( const Resolvable::constPtr & p )
+    { return p && p->kind() == ResTraits<_Res>::kind; }
+
+  // Specialization for Resolvable: Always true.
+  template<>
+    inline bool isKind<Resolvable>( const Resolvable::constPtr & p )
+    { return p; }
+
+  // Specialization for ResObject: Always true.
+  template<>
+    inline bool isKind<ResObject>( const Resolvable::constPtr & p )
+    { return p; }
+
+
+  /** Convert Resolvable::Ptr into Ptr of a certain Kind.
+   * \return \c NULL iff \a p is \c NULL or points to a Resolvable
+   * not of the specified Kind.
+   * \relates Resolvable
+   * \code
+   * asKind<Package>(resPtr);
+   * \endcode
+  */
+  template<class _Res>
+    inline typename ResTraits<_Res>::PtrType asKind( const Resolvable::Ptr & p )
+    { return dynamic_pointer_cast<_Res>(p); }
+
+  template<class _Res>
+    inline typename ResTraits<_Res>::constPtrType asKind( const Resolvable::constPtr & p )
+    { return dynamic_pointer_cast<const _Res>(p); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Resolvable Compare Resolvable::constPtr according to
+   *  \a kind and \a name.
+  */
+  inline int compareByN( const Resolvable::constPtr & lhs,
+                         const Resolvable::constPtr & rhs )
+  {
+    if ( lhs == rhs )
+      return 0;
+    if ( ! (lhs && rhs) )
+      return lhs ? 1 : -1;
+    int res = 0;
+    if ( (res = lhs->kind().compare( rhs->kind() )) )
+      return res;
+    return lhs->name().compare( rhs->name() );
+  }
+
+  /** \relates Resolvable Compare Resolvable::constPtr according to
+   *  \a kind, \a name and \a edition(compare!).
+  */
+  inline int compareByNVR( const Resolvable::constPtr & lhs,
+                           const Resolvable::constPtr & rhs )
+  {
+    if ( lhs == rhs )
+      return 0;
+    if ( ! (lhs && rhs) )
+      return lhs ? 1 : -1;
+    int res = 0;
+    if ( (res = lhs->kind().compare( rhs->kind() )) )
+      return res;
+    if ( (res = lhs->name().compare( rhs->name() )) )
+      return res;
+    return lhs->edition().compare( rhs->edition() );
+  }
+
+  /** \relates Resolvable Compare Resolvable::constPtr according to
+   *  \a kind, \a name, \a edition(compare!) and \a arch.
+  */
+  inline int compareByNVRA( const Resolvable::constPtr & lhs,
+                            const Resolvable::constPtr & rhs )
+  {
+    if ( lhs == rhs )
+      return 0;
+    if ( ! (lhs && rhs) )
+      return lhs ? 1 : -1;
+    int res = 0;
+    if ( (res = lhs->kind().compare( rhs->kind() )) )
+      return res;
+    if ( (res = lhs->name().compare( rhs->name() )) )
+      return res;
+    if ( (res = lhs->edition().compare( rhs->edition() )) )
+      return res;
+    return lhs->arch().compare( rhs->arch() );
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESOLVABLE_H
diff --git a/zypp/Resolver.cc b/zypp/Resolver.cc
new file mode 100644 (file)
index 0000000..9922788
--- /dev/null
@@ -0,0 +1,150 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Resolver.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/Resolver.h"
+#include "zypp/ZConfig.h"
+#include "zypp/TriBool.h"
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/Testcase.h"
+#include "zypp/sat/Transaction.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  using namespace solver;
+
+  IMPL_PTR_TYPE(Resolver);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolver::Resolver
+  //   METHOD TYPE : Ctor
+  //
+  Resolver::Resolver( const ResPool & pool )
+  : _pimpl( new Impl(pool) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Resolver::~Resolver
+  //   METHOD TYPE : Dtor
+  //
+  Resolver::~Resolver()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   Resolver interface forwarded to implementation
+  //
+  ///////////////////////////////////////////////////////////////////
+  bool Resolver::verifySystem ()
+  { return _pimpl->verifySystem(); }
+
+  bool Resolver::resolvePool ()
+  { return _pimpl->resolvePool(); }
+
+  bool Resolver::resolveQueue( solver::detail::SolverQueueItemList & queue )
+  { return _pimpl->resolveQueue(queue); }
+
+  void Resolver::undo()
+  { _pimpl->undo(); }
+
+  ResolverProblemList Resolver::problems ()
+  { return _pimpl->problems (); }
+
+  void Resolver::applySolutions( const ProblemSolutionList & solutions )
+  { _pimpl->applySolutions (solutions); }
+
+  sat::Transaction Resolver::getTransaction()
+  { return _pimpl->getTransaction(); }
+
+  bool Resolver::doUpgrade()
+  { return _pimpl->doUpgrade(); }
+
+  void Resolver::doUpdate()
+  { _pimpl->doUpdate(); }
+
+  void Resolver::setForceResolve( bool yesno_r )       { _pimpl->setForceResolve( yesno_r ); }
+  bool Resolver::forceResolve() const                  { return _pimpl->forceResolve(); }
+
+  void Resolver::setIgnoreAlreadyRecommended( bool yesno_r) { _pimpl->setIgnoreAlreadyRecommended( yesno_r ); }
+  bool Resolver::ignoreAlreadyRecommended() const      { return _pimpl->ignoreAlreadyRecommended(); }
+
+  void Resolver::setOnlyRequires( bool yesno_r )       { _pimpl->setOnlyRequires( yesno_r ); }
+  void Resolver::resetOnlyRequires()                   { _pimpl->setOnlyRequires( indeterminate ); }
+  bool Resolver::onlyRequires() const                  { return _pimpl->onlyRequires(); }
+
+  bool Resolver::upgradeMode() const                   { return _pimpl->isUpgradeMode(); }
+
+  void Resolver::setAllowVendorChange( bool yesno_r )  { _pimpl->setAllowVendorChange( yesno_r ); }
+  void Resolver::setDefaultAllowVendorChange()         { _pimpl->setAllowVendorChange( indeterminate ); }
+  bool Resolver::allowVendorChange() const             { return _pimpl->allowVendorChange(); }
+
+  void Resolver::setSystemVerification( bool yesno_r ) { _pimpl->setVerifyingMode( yesno_r ); }
+  void Resolver::setDefaultSystemVerification()                { _pimpl->setVerifyingMode( indeterminate ); }
+  bool Resolver::systemVerification() const            { return _pimpl->isVerifyingMode(); }
+
+  void Resolver::setSolveSrcPackages( bool yesno_r )   { _pimpl->setSolveSrcPackages( yesno_r ); }
+  void Resolver::setDefaultSolveSrcPackages()          { _pimpl->setSolveSrcPackages( indeterminate ); }
+  bool Resolver::solveSrcPackages() const              { return _pimpl->solveSrcPackages(); }
+
+  void Resolver::setCleandepsOnRemove( bool yesno_r )  { _pimpl->setCleandepsOnRemove( yesno_r ); }
+  void Resolver::setDefaultCleandepsOnRemove()         { _pimpl->setCleandepsOnRemove( indeterminate ); }
+  bool Resolver::cleandepsOnRemove() const             { return _pimpl->cleandepsOnRemove(); }
+
+  void Resolver::addUpgradeRepo( Repository repo_r )   { _pimpl->addUpgradeRepo( repo_r ); }
+  bool Resolver::upgradingRepo( Repository repo_r ) const { return _pimpl->upgradingRepo( repo_r ); }
+  void Resolver::removeUpgradeRepo( Repository repo_r )        { _pimpl->removeUpgradeRepo( repo_r ); }
+  void Resolver::removeUpgradeRepos()                  { _pimpl->removeUpgradeRepos(); }
+
+  void Resolver::addRequire( const Capability & capability )   { _pimpl->addExtraRequire( capability ); }
+  void Resolver::addConflict( const Capability & capability )  { _pimpl->addExtraConflict( capability ); }
+  void Resolver::removeRequire( const Capability & capability )        { _pimpl->removeExtraRequire( capability ); }
+  void Resolver::removeConflict( const Capability & capability ){ _pimpl->removeExtraConflict( capability ); }
+
+  CapabilitySet Resolver::getRequire() const   { return _pimpl->extraRequires(); }
+  CapabilitySet Resolver::getConflict() const  { return _pimpl->extraConflicts(); }
+
+  std::list<PoolItem> Resolver::problematicUpdateItems() const
+  { return _pimpl->problematicUpdateItems(); }
+
+  bool Resolver::createSolverTestcase( const std::string & dumpPath, bool runSolver )
+  {
+    solver::detail::Testcase testcase (dumpPath);
+    return testcase.createTestcase(*_pimpl, true, runSolver);
+  }
+
+  solver::detail::ItemCapKindList Resolver::isInstalledBy( const PoolItem & item )
+  { return _pimpl->isInstalledBy (item); }
+
+  solver::detail::ItemCapKindList Resolver::installs( const PoolItem & item )
+  { return _pimpl->installs (item); }
+
+  solver::detail::ItemCapKindList Resolver::satifiedByInstalled( const PoolItem & item )
+  { return _pimpl->satifiedByInstalled (item); }
+
+  solver::detail::ItemCapKindList Resolver::installedSatisfied( const PoolItem & item )
+  { return _pimpl->installedSatisfied (item); }
+
+  void Resolver::reset()
+  { _pimpl->reset( false ); /* Do not keep extra requires/conflicts */ }
+
+  std::ostream & operator<<( std::ostream & str, const Resolver & obj )
+  { return str << *obj._pimpl; }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Resolver.h b/zypp/Resolver.h
new file mode 100644 (file)
index 0000000..f76a331
--- /dev/null
@@ -0,0 +1,390 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Resolver.h
+ *
+*/
+#ifndef ZYPP_RESOLVER_H
+#define ZYPP_RESOLVER_H
+
+#include <iosfwd>
+#include <functional>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/ResPool.h"
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+#include "zypp/ProblemTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace sat
+  {
+    class Transaction;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Resolver
+  //
+  /**
+   * Dependency resolver interface.
+   *
+   * To resolve dependencies after making changes to the \ref ResPool (using
+   * \ref addRequire(), \ref addConflict(), \ref applySolutions(), or by making
+   * the changes directly on the \ref PoolItem status objects,
+   * call the \ref resolvePool() method.
+   */
+  class Resolver : public base::ReferenceCounted, private base::NonCopyable
+  {
+  public:
+
+    /** Ctor */
+    Resolver( const ResPool & pool );
+    /** Dtor */
+    virtual ~Resolver();
+
+    /**
+     * Resolve package dependencies:
+     *
+     * Enter \ref systemVerification mode to monitor and repair dependencies
+     * of already installed packages, and solve immediately.
+     *
+     * Call \ref setSystemVerification to turn of this mode.
+     **/
+    bool verifySystem();
+
+
+    /**
+     * Resolve package dependencies:
+     *
+     * Try to execute all pending transactions (there may be more than
+     * one!).
+     * The solver collects all transactions (install/delete resolvables)
+     * from the pool, generates task, solving it and writes the
+     * results back to pool
+     *
+     * Returns "true" on success (i.e., if there were no problems that
+     * need user interaction) and "false" if there were problems.  In
+     * the latter case, use problems() and later applySolutions()
+     * below.
+     **/
+    bool resolvePool();
+
+
+    /**
+     * Resolve package dependencies:
+     *
+     * The solver works off the given queue and writes back the solution
+     * to pool.
+     *
+     * Returns "true" on success (i.e., if there were no problems that
+     * need user interaction) and "false" if there were problems.  In
+     * the latter case, use problems() and later applySolutions()
+     * below.
+     * The solution could be that the solver remove/add some entries
+     * in the task queue. So make a new call of resolveQueue after you
+     * have applied any solution AND check the parameter "queue" if
+     * there has been any changes by the solver and adapt these changes
+     * to e.g. the selectables.
+     *
+     **/
+    bool resolveQueue( solver::detail::SolverQueueItemList & queue );
+
+    /*
+     * Undo solver changes done in resolvePool()
+     * Throwing away all ignored dependencies.
+     */
+    void undo();
+
+    /*
+     * Resets solver information and verify option.
+     */
+    void reset();
+
+
+    /**
+     * Do an distribution upgrade
+     *
+     * Perform a distribution upgrade. This performs an update of
+     * all packages with a special resolver algorithm which takes
+     * care of package splits, pattern  and  product  updates,
+     * etc.
+     *
+     * \see \ref addUpgradeRepo
+     **/
+    bool doUpgrade();
+
+    /**
+     * Update to newest package
+     *
+     * Install the newest version of your installed packages as
+     * far as possible. This means a newer package will NOT be
+     * installed if it generates dependency problems.
+     * So the user will not get an error message.
+     *
+     **/
+    void doUpdate( );
+
+    /**
+     * Unmaintained packages which does not fit to
+     * the updated system (broken dependencies) will be
+     * deleted.
+     * Return the list of deleted items.
+     * Note : This list is valid after the call doUpgrade() only.
+     **/
+    std::list<PoolItem> problematicUpdateItems() const;
+
+    /**
+     * Return the dependency problems found by the last call to
+     * resolveDependencies(). If there were no problems, the returned
+     * list will be empty.
+     **/
+    ResolverProblemList problems();
+
+
+    /**
+     * Apply problem solutions. No more than one solution per problem
+     * can be applied.
+     **/
+    void applySolutions( const ProblemSolutionList & solutions );
+
+    /**
+     * Return the \ref Transaction computed by the last solver run.
+     */
+    sat::Transaction getTransaction();
+
+    /**
+     * Remove resolvables which are conflicts with others or
+     * have unfulfilled requirements.
+     * This behaviour is favourited by ZMD.
+     **/
+    void setForceResolve( bool force );
+    bool forceResolve() const;
+
+    /**
+     * Ignore recommended packages that were already recommended by
+     * the installed packages
+     **/
+    void setIgnoreAlreadyRecommended( bool yesno_r );
+    bool ignoreAlreadyRecommended() const;
+
+    /**
+     * Setting whether required packages are installed ONLY
+     * So recommended packages, language packages and packages which depend
+     * on hardware (modalias) will not be regarded.
+     **/
+    void setOnlyRequires( bool yesno_r );
+    void resetOnlyRequires(); // set back to default (described in zypp.conf)
+    bool onlyRequires() const;
+
+    /**
+     * Whether the \ref Resolver is in upgrade mode.
+     */
+    bool upgradeMode() const;
+
+    /**
+     * Setting whether the solver should allow or disallow vendor changes.
+     *
+     * If OFF (the default) the solver will replace packages with packages
+     * of the same (or equivalent) vendor ony.
+     *
+     * \see \ref VendorAttr for definition of vendor equivalence.
+     **/
+    void setAllowVendorChange( bool yesno_r );
+    void setDefaultAllowVendorChange(); // set back to default (in zypp.conf)
+    bool allowVendorChange() const;
+
+    /**
+     * System verification mode also monitors and repairs dependencies
+     * of already installed packages.
+     * \see \ref verifySystem
+     */
+    void setSystemVerification( bool yesno_r );
+    void setDefaultSystemVerification();
+    bool systemVerification() const;
+
+    /**
+     * Set whether to solve source packages build dependencies per default.
+     * Usually turned off and if, enabled per source package.
+     * \NOTE This affects only source packges selected in the \ref ResPool. No solver rule
+     * will be generated for them. Source packages requested via e.g. \ref addRequire will
+     * always be solved.
+     * \NOTE Be carefull. The older the source package is, the stranger may be the
+     * result of solving it's build dependencies.
+     */
+    void setSolveSrcPackages( bool yesno_r );
+    void setDefaultSolveSrcPackages();
+    bool solveSrcPackages() const;
+
+    /**
+     * Cleanup when deleting packages. Whether the solver should per default
+     * try to remove packages exclusively required by the ones he's asked to delete.
+     */
+    void setCleandepsOnRemove( bool yesno_r );
+    void setDefaultCleandepsOnRemove(); // set back to default (in zypp.conf)
+    bool cleandepsOnRemove() const;
+
+    /** \name Upgrade to content of a specific repository.
+     * \note This is an ordinary solver request. You should simply
+     * \ref resolvePool to execute, and not \ref doUpgrade.
+     */
+    //@{
+    /**
+     * Adding request to perform a dist upgrade restricted to this repository.
+     *
+     * This is what e.g. <tt>zypper dup --repo myrepo</tt> should perform.
+     * \see \ref doUpgrade
+     */
+    void addUpgradeRepo( Repository repo_r );
+
+    /**
+     * Whether there is an \c UpgradeRepo request pending for this repo.
+     */
+    bool upgradingRepo( Repository repo_r ) const;
+
+    /**
+     * Remove an upgrade request for this repo.
+     */
+    void removeUpgradeRepo( Repository repo_r );
+
+    /**
+     * Remove all upgrade repo requests.
+     */
+    void removeUpgradeRepos();
+    //@}
+
+    /**
+     * Adding additional requirement
+     *
+     */
+    void addRequire( const Capability & capability );
+
+    /**
+     * Adding additional conflict
+     *
+     */
+    void addConflict( const Capability & capability );
+
+    /**
+     * Remove the additional requirement set by \ref addRequire(Capability).
+     *
+     */
+    void removeRequire( const Capability & capability );
+
+    /**
+     * Remove the additional conflict set by \ref addConflict(Capability).
+     *
+     */
+    void removeConflict( const Capability & capability );
+
+    /**
+     * Get all the additional requirements set by \ref addRequire(Capability).
+     *
+     */
+    CapabilitySet getRequire() const;
+
+    /**
+     * Get all the additional conflicts set by \ref addConflict(Capability).
+     *
+     */
+    CapabilitySet getConflict() const;
+
+    /**
+     * Generates a solver Testcase of the current state
+     *
+     * \parame dumpPath destination directory of the created directory
+     * \return true if it was successful
+     */
+    bool createSolverTestcase( const std::string & dumpPath = "/var/log/YaST2/solverTestcase", bool runSolver = true );
+
+    /**
+     * Gives information about WHO has pused an installation of an given item.
+     *
+     * \param item    Evaluate additional information for this resolvable.
+     * \return A list of structures which contains:
+     *         item                Item which has triggered the installation of the given param item.
+     *          initialInstallation This item has triggered the installation
+     *                             Not already fullfilled requierement only.
+     *         cap                 Capability which has triggerd this installation
+     *         capKind             Kind of that capability (e.g.  Dep::REQUIRES,Dep::RECOMMENDS,... )
+     *
+     * Note: In order to have a result start a solver run before. Not matter if it is valid or invalid.
+     *
+     */
+    solver::detail::ItemCapKindList isInstalledBy( const PoolItem & item );
+
+    /**
+     * Gives information about WHICH additional items will be installed due the installation of an item.
+     *
+     * \param item     Evaluate additional information for this resolvable.
+     * \return A list of structures which contains:
+     *         item                Item which has triggered the installation of the given param item.
+     *          initialInstallation This item has triggered the installation
+     *                             Not already fullfilled requierement only.
+     *         cap                 Capability which has triggerd this installation
+     *         capKind             Kind of that capability (e.g.  Dep::REQUIRES,Dep::RECOMMENDS,... )
+     *
+     * Note: In order to have a result start a solver run before. Not matter if it is valid or invalid.
+     *
+     */
+    solver::detail::ItemCapKindList installs( const PoolItem & item );
+
+    /**
+     * Gives information about WHICH installed items are requested by the installation of an item.
+     *
+     * \param item     Evaluate additional information for this resolvable.
+     * \return A list of structures which contains:
+     *         item                Item which has triggered the installation of the given param item.
+     *          initialInstallation This item has triggered the installation
+     *                             Not already fullfilled requierement only.
+     *         cap                 Capability which has triggerd this installation
+     *         capKind             Kind of that capability (e.g.  Dep::REQUIRES,Dep::RECOMMENDS,... )
+     *
+     * Note: In order to have a result start a solver run before. Not matter if it is valid or invalid.
+     *
+     */
+    solver::detail::ItemCapKindList satifiedByInstalled( const PoolItem & item );
+
+
+    /**
+     * Gives information about WHICH items require an already installed item.
+     *
+     * \param item     Evaluate additional information for this resolvable.
+     * \return A list of structures which contains:
+     *         item                Item which has triggered the installation of the given param item.
+     *          initialInstallation This item has triggered the installation
+     *                             Not already fullfilled requierement only.
+     *         cap                 Capability which has triggerd this installation
+     *         capKind             Kind of that capability (e.g.  Dep::REQUIRES,Dep::RECOMMENDS,... )
+     *
+     * Note: In order to have a result start a solver run before. Not matter if it is valid or invalid.
+     *
+     */
+    solver::detail::ItemCapKindList installedSatisfied( const PoolItem & item );
+
+
+  private:
+    friend std::ostream & operator<<( std::ostream & str, const Resolver & obj );
+
+    typedef solver::detail::Resolver Impl;
+    zypp::RW_pointer<Impl,rw_pointer::Intrusive<Impl> > _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Resolver Stream output */
+  std::ostream & operator<<( std::ostream & str, const Resolver & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_RESOLVER_H
diff --git a/zypp/ResolverProblem.cc b/zypp/ResolverProblem.cc
new file mode 100644 (file)
index 0000000..41969de
--- /dev/null
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* ResolverProblem.cc
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zypp/ResolverProblem.h"
+#include "zypp/ProblemSolution.h"
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(ResolverProblem);
+
+//---------------------------------------------------------------------------
+
+ostream&
+operator<<( ostream& os, const ResolverProblem & problem)
+{
+    os << "Problem:" << endl;
+    os << problem._description << endl;
+    os << problem._details << endl;
+    os << problem._solutions;
+    return os;
+}
+
+
+ostream&
+operator<<( ostream& os, const ResolverProblemList & problemlist)
+{
+    for (ResolverProblemList::const_iterator iter = problemlist.begin(); iter != problemlist.end(); ++iter) {
+       if (iter != problemlist.begin())
+           os << ", ";
+       os << (*iter);
+    }
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+/**
+ * Constructor.
+ **/
+ResolverProblem::ResolverProblem( const string & description, const string & details )
+    : _description (description)
+    , _details (details)
+{
+}
+
+/**
+ * Destructor.
+ **/
+ResolverProblem::~ResolverProblem()
+{
+}
+
+/**
+ * Return the possible solutions to this problem.
+ * All problems should have at least 2-3 (mutually exclusive) solutions:
+ *
+ *       -  Undo: Do not perform the offending transaction
+ *      (do not install the package that had unsatisfied requirements,
+ *       do not remove  the package that would break other packages' requirements)
+ *
+ *       - Remove referrers: Remove all packages that would break because
+ *     they depend on the package that is requested to be removed
+ *
+ *       - Ignore: Inject artificial "provides" for a missing requirement
+ *     (pretend that requirement is satisfied)
+ **/
+
+ProblemSolutionList
+ResolverProblem::solutions() const
+{
+    return _solutions;
+}
+
+/**
+ * Add a solution to this problem. This class takes over ownership of
+ * the problem and will delete it when neccessary.
+ **/
+
+void
+ResolverProblem::addSolution( ProblemSolution_Ptr solution,
+                             bool inFront )
+{
+    if (inFront) {
+       _solutions.push_front (solution);
+    } else {
+       _solutions.push_back (solution);
+    }
+}
+
+void
+ResolverProblem::clear()
+{
+    _solutions.clear();
+}
+
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/ResolverProblem.h b/zypp/ResolverProblem.h
new file mode 100644 (file)
index 0000000..92f52ad
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Author: Stefan Hundhammer <sh@suse.de>
+ *
+ **/
+
+#ifndef ZYPP_RESOLVERPROBLEM_H
+#define ZYPP_RESOLVERPROBLEM_H
+
+#include <list>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ProblemSolution.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+
+    class ResolverProblem : public base::ReferenceCounted
+    {
+    private:
+
+       /**
+        * Clear all data.
+        * In particular, delete all members of _solutions.
+        **/
+       void clear();
+
+
+       //
+       // Data members
+       //
+
+       Resolver_constPtr       _resolver;
+       std::string             _description;
+       std::string             _details;
+       ProblemSolutionList     _solutions;
+
+    public:
+
+       /**
+        * Constructor.
+        **/
+       ResolverProblem( const std::string & description, const std::string & details );
+
+       /**
+        * Destructor.
+        **/
+       ~ResolverProblem();
+
+       // ---------------------------------- I/O
+
+       friend std::ostream& operator<<(std::ostream&, const ResolverProblem & problem);
+
+       // ---------------------------------- accessors
+
+       /**
+        * Return a one-line description of the problem.
+        **/
+       std::string description() const { return _description; }
+
+       /**
+        * Return a (possibly muti-line) detailed description of the problem
+        * or an empty string if there are no useful details.
+        **/
+       std::string details() const { return _details; }
+
+       /**
+        * Set description of the problem.
+        **/
+       void setDescription(const std::string & description)
+           { _description=description; }
+
+       /**
+        * Set detail description of the problem.
+        **/
+       void setDetails(const std::string & detail)
+           { _details=detail; }
+
+       /**
+        * Return the possible solutions to this problem.
+        * All problems should have at least 2-3 (mutually exclusive) solutions:
+        *
+        *        -  Undo: Do not perform the offending transaction
+        *       (do not install the package that had unsatisfied requirements,
+        *        do not remove  the package that would break other packages' requirements)
+        *
+        *        - Remove referrers: Remove all packages that would break because
+        *      they depend on the package that is requested to be removed
+        *
+        *        - Ignore: Inject artificial "provides" for a missing requirement
+        *      (pretend that requirement is satisfied)
+        **/
+       ProblemSolutionList solutions() const;
+
+       /**
+        * Return the parent dependency resolver.
+        **/
+       Resolver_constPtr resolver() const { return _resolver; }
+
+       // ---------------------------------- methods
+
+       /**
+        * Add a solution to this problem. This class takes over ownership of
+        * the problem and will delete it when neccessary.
+        **/
+       void addSolution( ProblemSolution_Ptr solution, bool inFront = false );
+
+    };
+    ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_RESOLVERPROBLEM_H
+
diff --git a/zypp/ServiceInfo.cc b/zypp/ServiceInfo.cc
new file mode 100644 (file)
index 0000000..175caf2
--- /dev/null
@@ -0,0 +1,212 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/ServiceInfo.cc
+ *
+ */
+#include <ostream>
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/parser/xml/XmlEscape.h"
+
+#include "zypp/RepoInfo.h"
+#include "zypp/repo/RepoInfoBaseImpl.h"
+
+#include "zypp/ServiceInfo.h"
+
+using namespace std;
+using zypp::xml::escape;
+
+///////////////////////////////////////////////////////////////////////////////
+namespace zypp
+{//////////////////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : ServiceInfo::Impl
+  //
+  struct ServiceInfo::Impl : public repo::RepoInfoBase::Impl
+  {
+    typedef ServiceInfo::ReposToEnable  ReposToEnable;
+    typedef ServiceInfo::ReposToDisable ReposToDisable;
+
+  public:
+    Url url;
+    repo::ServiceType type;
+    ReposToEnable  reposToEnable;
+    ReposToDisable reposToDisable;
+
+  public:
+    Impl()
+      : repo::RepoInfoBase::Impl()
+      , type(repo::ServiceType::NONE_e)
+    {}
+
+    Impl(const Url & url_)
+      : repo::RepoInfoBase::Impl()
+      , url(url_)
+      , type(repo::ServiceType::NONE_e)
+    {}
+
+    ~Impl()
+    {}
+
+    void setProbedType( const repo::ServiceType & t ) const
+    {
+      if ( type == repo::ServiceType::NONE
+           && t != repo::ServiceType::NONE )
+      {
+        // lazy init!
+        const_cast<Impl*>(this)->type = t;
+      }
+    }
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : ServiceInfo::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const ServiceInfo ServiceInfo::noService;
+
+  ServiceInfo::ServiceInfo() : _pimpl( new Impl() ) {}
+
+  ServiceInfo::ServiceInfo(const string & alias)
+    : repo::RepoInfoBase(alias), _pimpl( new Impl() )
+  {}
+
+  ServiceInfo::ServiceInfo(const string & alias, const Url & url)
+    : repo::RepoInfoBase(alias), _pimpl( new Impl(url) )
+  {}
+
+  ServiceInfo::~ServiceInfo()
+  {}
+
+  Url ServiceInfo::url() const { return _pimpl->url; }
+  void ServiceInfo::setUrl( const Url& url ) { _pimpl->url = url; }
+
+  repo::ServiceType ServiceInfo::type() const
+  { return _pimpl->type; }
+  void ServiceInfo::setType( const repo::ServiceType & type )
+  { _pimpl->type = type; }
+
+  void ServiceInfo::setProbedType( const repo::ServiceType &t ) const
+  { _pimpl->setProbedType( t ); }
+
+  bool ServiceInfo::reposToEnableEmpty() const
+  { return _pimpl->reposToEnable.empty(); }
+
+  ServiceInfo::ReposToEnable::size_type ServiceInfo::reposToEnableSize() const
+  { return _pimpl->reposToEnable.size(); }
+
+  ServiceInfo::ReposToEnable::const_iterator ServiceInfo::reposToEnableBegin() const
+  { return _pimpl->reposToEnable.begin(); }
+
+  ServiceInfo::ReposToEnable::const_iterator ServiceInfo::reposToEnableEnd() const
+  { return _pimpl->reposToEnable.end(); }
+
+  bool ServiceInfo::repoToEnableFind( const std::string & alias_r ) const
+  { return( _pimpl->reposToEnable.find( alias_r ) != _pimpl->reposToEnable.end() ); }
+
+  void ServiceInfo::addRepoToEnable( const std::string & alias_r )
+  {
+    _pimpl->reposToEnable.insert( alias_r );
+    _pimpl->reposToDisable.erase( alias_r );
+  }
+
+  void ServiceInfo::delRepoToEnable( const std::string & alias_r )
+  { _pimpl->reposToEnable.erase( alias_r ); }
+
+  void ServiceInfo::clearReposToEnable()
+  { _pimpl->reposToEnable.clear(); }
+
+
+  bool ServiceInfo::reposToDisableEmpty() const
+  { return _pimpl->reposToDisable.empty(); }
+
+  ServiceInfo::ReposToDisable::size_type ServiceInfo::reposToDisableSize() const
+  { return _pimpl->reposToDisable.size(); }
+
+  ServiceInfo::ReposToDisable::const_iterator ServiceInfo::reposToDisableBegin() const
+  { return _pimpl->reposToDisable.begin(); }
+
+  ServiceInfo::ReposToDisable::const_iterator ServiceInfo::reposToDisableEnd() const
+  { return _pimpl->reposToDisable.end(); }
+
+  bool ServiceInfo::repoToDisableFind( const std::string & alias_r ) const
+  { return( _pimpl->reposToDisable.find( alias_r ) != _pimpl->reposToDisable.end() ); }
+
+  void ServiceInfo::addRepoToDisable( const std::string & alias_r )
+  {
+    _pimpl->reposToDisable.insert( alias_r );
+    _pimpl->reposToEnable.erase( alias_r );
+  }
+
+  void ServiceInfo::delRepoToDisable( const std::string & alias_r )
+  { _pimpl->reposToDisable.erase( alias_r ); }
+
+  void ServiceInfo::clearReposToDisable()
+  { _pimpl->reposToDisable.clear(); }
+
+
+  std::ostream & ServiceInfo::dumpAsIniOn( std::ostream & str ) const
+  {
+    RepoInfoBase::dumpAsIniOn(str)
+      << "url = " << url() << endl
+      << "type = " << type() << endl;
+
+    if ( ! reposToEnableEmpty() )
+      str << "repostoenable = " << str::joinEscaped( reposToEnableBegin(), reposToEnableEnd() ) << endl;
+    if ( ! reposToDisableEmpty() )
+      str << "repostodisable = " << str::joinEscaped( reposToDisableBegin(), reposToDisableEnd() ) << endl;
+    return str;
+  }
+
+  std::ostream & ServiceInfo::dumpAsXMLOn(std::ostream & str) const
+  { return dumpAsXMLOn(str, ""); }
+
+  ostream & ServiceInfo::dumpAsXMLOn( ostream & str, const string & content) const
+  {
+    str
+      << "<service"
+      << " alias=\"" << escape(alias()) << "\""
+      << " name=\"" << escape(name()) << "\""
+      << " enabled=\"" << enabled() << "\""
+      << " autorefresh=\"" << autorefresh() << "\""
+      << " url=\"" << escape(url().asString()) << "\""
+      << " type=\"" << type().asString() << "\"";
+
+    if (content.empty())
+      str << "/>" << endl;
+    else
+      str << ">" << endl << content << "</service>" << endl;
+
+    return str;
+  }
+
+
+  std::ostream & operator<<( std::ostream& str, const ServiceInfo &obj )
+  {
+    return obj.dumpAsIniOn(str);
+  }
+
+
+///////////////////////////////////////////////////////////////////////////////
+} //namespace zypp
+///////////////////////////////////////////////////////////////////////////////
diff --git a/zypp/ServiceInfo.h b/zypp/ServiceInfo.h
new file mode 100644 (file)
index 0000000..c6706f5
--- /dev/null
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ServiceInfo.h
+ *
+ */
+#ifndef ZYPP_SERVICE_H
+#define ZYPP_SERVICE_H
+
+#include <set>
+#include <string>
+
+#include "zypp/Url.h"
+
+#include "zypp/repo/ServiceType.h"
+#include "zypp/repo/RepoInfoBase.h"
+
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ServiceInfo
+  //
+  /** */
+  class ServiceInfo : public repo::RepoInfoBase
+  {
+  public:
+    /** Default ctor creates \ref noService.*/
+    ServiceInfo();
+
+    /**
+     *  Creates ServiceInfo with specified alias.
+     *
+     * \param alias unique short name of service
+     */
+    ServiceInfo( const std::string & alias );
+
+    /**
+     * ServiceInfo with alias and its URL
+     *
+     * \param alias unique shortname of service
+     * \param url url to service
+     */
+    ServiceInfo( const std::string & alias, const Url& url );
+
+    virtual ~ServiceInfo();
+
+  public:
+    /** Represents an empty service. */
+    static const ServiceInfo noService;
+
+  public:
+
+    /**
+     * Gets url to service
+     *
+     * \return url to service
+     */
+    Url url() const;
+
+    /**
+     * Sets url for this service
+     *
+     * \param url url to this service
+     */
+    void setUrl( const Url& url );
+
+    /**
+     *
+     */
+    repo::ServiceType type() const;
+
+    /**
+     * Set service type.
+     *
+     * \param type the new type
+     */
+    void setType( const repo::ServiceType & type );
+
+    void setProbedType( const repo::ServiceType & t ) const;
+
+
+    /** \name Set of repos (repository aliases) to enable on next refresh.
+     *
+     * Per default new repositories are created in disabled state. But repositories
+     * mentioned here will be created in enabled state on the next refresh.
+     * Afterwards they get removed from the list.
+     */
+    //@{
+    /** Container of repos. */
+    typedef std::set<std::string> ReposToEnable;
+    bool                          reposToEnableEmpty() const;
+    ReposToEnable::size_type      reposToEnableSize() const;
+    ReposToEnable::const_iterator reposToEnableBegin() const;
+    ReposToEnable::const_iterator reposToEnableEnd() const;
+
+    /** Wheter \c alias_r is mentioned in ReposToEnable. */
+    bool repoToEnableFind( const std::string & alias_r ) const;
+
+    /** Add \c alias_r to the set of ReposToEnable. */
+    void addRepoToEnable( const std::string & alias_r );
+    /** Remove \c alias_r from the set of ReposToEnable. */
+    void delRepoToEnable( const std::string & alias_r );
+    /** Clear the set of ReposToEnable. */
+    void clearReposToEnable();
+    //@}
+
+    /** \name Set of repos (repository aliases) to disable on next refresh.
+     *
+     * Repositories mentioned here will be disabled on the next refresh, in case they
+     * still exist. Afterwards they get removed from the list.
+     */
+    //@{
+    /** Container of repos. */
+    typedef std::set<std::string>  ReposToDisable;
+    bool                           reposToDisableEmpty() const;
+    ReposToDisable::size_type      reposToDisableSize() const;
+    ReposToDisable::const_iterator reposToDisableBegin() const;
+    ReposToDisable::const_iterator reposToDisableEnd() const;
+
+    /** Wheter \c alias_r is mentioned in ReposToDisable. */
+    bool repoToDisableFind( const std::string & alias_r ) const;
+
+    /** Add \c alias_r to the set of ReposToDisable. */
+    void addRepoToDisable( const std::string & alias_r );
+    /** Remove \c alias_r from the set of ReposToDisable. */
+    void delRepoToDisable( const std::string & alias_r );
+    /** Clear the set of ReposToDisable. */
+    void clearReposToDisable();
+    //@}
+
+  public:
+    /**
+     * Writes ServiceInfo to stream in ".service" format
+     *
+     * \param str stream where serialized version service is written
+     */
+    virtual std::ostream & dumpAsIniOn( std::ostream & str ) const;
+
+    /**
+     * Write an XML representation of this ServiceInfo object.
+     */
+    virtual std::ostream & dumpAsXMLOn(std::ostream & str) const;
+
+    /**
+     * Write an XML representation of this ServiceInfo object.
+     *
+     * \param str
+     * \param content if not empty, produces <service ...>content</service>
+     *                otherwise <service .../>
+     */
+    virtual std::ostream & dumpAsXMLOn(
+        std::ostream & str, const std::string & content) const;
+
+    class Impl;
+
+  private:
+      RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ServiceInfo */
+  typedef shared_ptr<ServiceInfo> ServiceInfo_Ptr;
+  /** \relates ServiceInfo */
+  typedef shared_ptr<const ServiceInfo> ServiceInfo_constPtr;
+  /** \relates ServiceInfo */
+  typedef std::list<ServiceInfo> ServiceInfoList;
+
+  /** \relates ServiceInfo Stream output */
+  std::ostream & operator<<( std::ostream & str, const ServiceInfo & obj );
+
+
+    /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_REPOSITORY_H
diff --git a/zypp/Signature.cc b/zypp/Signature.cc
new file mode 100644 (file)
index 0000000..f5d1a11
--- /dev/null
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <string>
+#include <iostream>
+#include "zypp/Signature.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  Signature::Signature()
+  {
+      
+  }
+    
+  std::ostream & Signature::dumpOn( std::ostream & str ) const
+  {
+    return str;
+  }
+  
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Signature.h b/zypp/Signature.h
new file mode 100644 (file)
index 0000000..a3d00fb
--- /dev/null
@@ -0,0 +1,37 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+
+#ifndef ZYPP_Signature_H
+#define ZYPP_Signature_H
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  
+  class Signature
+  {
+    public:
+    Signature();
+    ~Signature();
+    
+    /** Overload to realize stream output. */
+    std::ostream & dumpOn( std::ostream & str ) const;
+    
+    private:
+  };  
+  
+  /** \relates Signature Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Signature & obj )
+  { return obj.dumpOn( str ); }  
+  
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_Signature_H
diff --git a/zypp/SrcPackage.cc b/zypp/SrcPackage.cc
new file mode 100644 (file)
index 0000000..dacb48f
--- /dev/null
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/SrcPackage.cc
+ *
+*/
+#include "zypp/SrcPackage.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(SrcPackage);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SrcPackage::SrcPackage
+  //   METHOD TYPE : Ctor
+  //
+  SrcPackage::SrcPackage( const sat::Solvable & solvable_r )
+  : ResObject( solvable_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SrcPackage::~SrcPackage
+  //   METHOD TYPE : Dtor
+  //
+  SrcPackage::~SrcPackage()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   SrcPackage interface forwarded to implementation
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  std::string SrcPackage::sourcePkgType() const
+  { return lookupStrAttribute( sat::SolvAttr::arch ); }
+
+  OnMediaLocation SrcPackage::location() const
+  { return lookupLocation(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/SrcPackage.h b/zypp/SrcPackage.h
new file mode 100644 (file)
index 0000000..01d8861
--- /dev/null
@@ -0,0 +1,57 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/SrcPackage.h
+ *
+*/
+#ifndef ZYPP_SRCPACKAGE_H
+#define ZYPP_SRCPACKAGE_H
+
+#include "zypp/ResObject.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  DEFINE_PTR_TYPE(SrcPackage);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SrcPackage
+  //
+  /** SrcPackage interface.
+  */
+  class SrcPackage : public ResObject
+  {
+
+  public:
+    typedef SrcPackage               Self;
+    typedef ResTraits<Self>          TraitsType;
+    typedef TraitsType::PtrType      Ptr;
+    typedef TraitsType::constPtrType constPtr;
+
+  public:
+    /** The type of the source rpm ("src" or "nosrc"). */
+    std::string sourcePkgType() const;
+
+    /** location of resolvable in repo */
+    OnMediaLocation location() const;
+
+  protected:
+    friend Ptr make<Self>( const sat::Solvable & solvable_r );
+    /** Ctor */
+    SrcPackage( const sat::Solvable & solvable_r );
+    /** Dtor */
+    virtual ~SrcPackage();
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SRCPACKAGE_H
diff --git a/zypp/SysContent.cc b/zypp/SysContent.cc
new file mode 100644 (file)
index 0000000..ee0f920
--- /dev/null
@@ -0,0 +1,457 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/SysContent.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/SysContent.h"
+#include "zypp/parser/xml/Reader.h"
+#include "zypp/parser/xml/ParseDef.h"
+#include "zypp/parser/xml/ParseDefConsume.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace syscontent
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace // Writer helpers
+    { /////////////////////////////////////////////////////////////////
+
+      /** writeXml helper.
+       * \return <tt>tag="value"</tt> if value not empty, else
+       * an empty string.
+      */
+      inline std::string attrIf( const std::string & tag_r,
+                                 const std::string & value_r )
+      {
+        std::string ret;
+        if ( ! value_r.empty() )
+          {
+            ret += " ";
+            ret += tag_r;
+            ret += "=\"";
+            ret += value_r;
+            ret += "\"";
+          }
+        return ret;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Writer::Impl
+    //
+    /** \see Writer */
+    class Writer::Impl
+    {
+    public:
+      std::ostream & writeXml( std::ostream & str ) const;
+
+    public:
+      std::string _name;
+      Edition     _edition;
+      std::string _description;
+      StorageT    _onsys;
+
+    public:
+      /** Offer default Impl. */
+      static shared_ptr<Impl> nullimpl()
+      {
+        static shared_ptr<Impl> _nullimpl( new Impl );
+        return _nullimpl;
+      }
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const
+      { return new Impl( *this ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Writer::Impl::writeXml
+    // METHOD TYPE : std::ostream &
+    //
+    std::ostream & Writer::Impl::writeXml( std::ostream & str ) const
+    {
+      // intro
+      str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+      str << "<syscontent>\n";
+      // ident data
+      str << "  <ident>\n";
+      str << "    <name>" << _name << "</name>\n";
+      str << "    <version"
+          << attrIf( "epoch", str::numstring(_edition.epoch()) )
+          << attrIf( "ver",   _edition.version() )
+          << attrIf( "rel",   _edition.release() )
+          << "/>\n";
+      str << "    <description>" << _description << "</description>\n";
+      str << "    <created>" << Date::now().asSeconds() << "</created>\n";
+      str << "  </ident>\n";
+      // ResObjects
+      str << "  <onsys>\n";
+      for ( iterator it = _onsys.begin(); it != _onsys.end(); ++it )
+        {
+          str << "    <entry"
+              << attrIf( "kind",  (*it)->kind().asString() )
+              << attrIf( "name",  (*it)->name() )
+              << attrIf( "epoch", str::numstring((*it)->edition().epoch()) )
+              << attrIf( "ver",   (*it)->edition().version() )
+              << attrIf( "rel",   (*it)->edition().release() )
+              << attrIf( "arch",  (*it)->arch().asString() )
+              << "/>\n";
+        }
+      str << "  </onsys>\n";
+      // extro
+      str << "</syscontent>" << endl;
+      return str;
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Writer
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Writer::Writer()
+    : _pimpl( Impl::nullimpl() )
+    {}
+
+    const std::string & Writer::name() const
+    { return _pimpl->_name; }
+
+    Writer & Writer::name( const std::string & val_r )
+    { _pimpl->_name = val_r; return *this; }
+
+    const Edition & Writer::edition() const
+    { return _pimpl->_edition; }
+
+    Writer & Writer::edition( const Edition & val_r )
+    { _pimpl->_edition = val_r; return *this; }
+
+    const std::string & Writer::description() const
+    { return _pimpl->_description; }
+
+    Writer & Writer::description( const std::string & val_r )
+    { _pimpl->_description = val_r; return *this; }
+
+    void Writer::addInstalled( const PoolItem & obj_r )
+    {
+      if ( obj_r.status().isInstalled() )
+        {
+          _pimpl->_onsys.insert( obj_r.resolvable() );
+        }
+    }
+
+    void Writer::addIf( const PoolItem & obj_r )
+    {
+      if ( obj_r.status().isInstalled() != obj_r.status().transacts()
+           && ! ( obj_r.status().transacts() && obj_r.status().isBySolver() ) )
+        {
+          _pimpl->_onsys.insert( obj_r.resolvable() );
+        }
+    }
+
+    void Writer::add( const ResObject::constPtr & obj_r )
+    { _pimpl->_onsys.insert( obj_r ); }
+
+    bool Writer::empty() const
+    { return _pimpl->_onsys.empty(); }
+
+    Writer::size_type Writer::size() const
+    { return _pimpl->_onsys.size(); }
+
+    Writer::const_iterator Writer::begin() const
+    { return _pimpl->_onsys.begin(); }
+
+    Writer::const_iterator Writer::end() const
+    { return _pimpl->_onsys.end(); }
+
+    std::ostream & Writer::writeXml( std::ostream & str ) const
+    { return _pimpl->writeXml( str ); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader::Entry::Impl
+    //
+    class Reader::Entry::Impl
+    {
+    public:
+      std::string _kind;
+      std::string _name;
+      Edition     _edition;
+      Arch        _arch;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader::Entry
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Reader::Entry::Entry()
+    : _pimpl( new Impl )
+    {}
+
+    Reader::Entry::Entry( const shared_ptr<Impl> & pimpl_r )
+    : _pimpl( pimpl_r )
+    {}
+
+    const std::string & Reader::Entry::kind() const
+    { return _pimpl->_kind; }
+
+    const std::string & Reader::Entry::name() const
+    { return _pimpl->_name; }
+
+    const Edition & Reader::Entry::edition() const
+    { return _pimpl->_edition; }
+
+    const Arch & Reader::Entry::arch() const
+    { return _pimpl->_arch; }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader::Impl
+    //
+    /** \see Reader */
+    class Reader::Impl
+    {
+    public:
+      Impl()
+      {}
+
+      Impl( std::istream & input_r );
+
+    public:
+      std::string _name;
+      Edition     _edition;
+      std::string _description;
+      Date        _created;
+
+      std::list<Entry> _content;
+
+    public:
+      /** Offer default Impl. */
+      static shared_ptr<Impl> nullimpl()
+      {
+        static shared_ptr<Impl> _nullimpl( new Impl );
+        return _nullimpl;
+      }
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const
+      { return new Impl( *this ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace // Reader helpers
+    { /////////////////////////////////////////////////////////////////
+
+      using namespace xml;
+
+      /** Sycontent xml node structure. */
+      struct SycontentNode : public ParseDef
+      {
+        SycontentNode( Mode mode_r )
+        : ParseDef( "syscontent", mode_r )
+        {
+          (*this)("ident",       OPTIONAL)
+                 ("onsys",       OPTIONAL)
+                 ;
+
+          (*this)["ident"]
+                 ("name",        OPTIONAL)
+                 ("version",     OPTIONAL)
+                 ("description", OPTIONAL)
+                 ("created",     OPTIONAL)
+                 ;
+
+          (*this)["onsys"]
+                 ("entry",       MULTIPLE_OPTIONAL)
+                 ;
+        }
+      };
+
+      /** Parse Edition from ver/rel/eopch attributes. */
+      struct ConsumeEdition : public ParseDefConsume
+      {
+        ConsumeEdition( Edition & value_r )
+        : _value( & value_r )
+        {}
+
+        virtual void start( const Node & node_r )
+        {
+          *_value = Edition( node_r.getAttribute("ver").asString(),
+                             node_r.getAttribute("rel").asString(),
+                             node_r.getAttribute("epoch").asString() );
+        }
+
+        Edition *_value;
+      };
+
+      /** Parse std::string from node value. */
+      struct ConsumeString : public ParseDefConsume
+      {
+        ConsumeString( std::string & value_r )
+        : _value( & value_r )
+        {}
+
+        virtual void text( const Node & node_r )
+        {
+          *_value = node_r.value().asString();
+        }
+
+        std::string *_value;
+      };
+
+      /** Parse Date from node value. */
+      struct ConsumeDate : public ParseDefConsume
+      {
+        ConsumeDate( Date & value_r )
+        : _value( & value_r )
+        {}
+
+        virtual void text( const Node & node_r )
+        {
+          *_value = node_r.value().asString();
+        }
+
+        Date *_value;
+      };
+
+      /** Parse entry list. */
+      struct ConsumeEntries : public ParseDefConsume
+      {
+        ConsumeEntries( std::list<Reader::Entry> & value_r )
+        : _value( & value_r )
+        {}
+
+        virtual void start( const Node & node_r )
+        {
+          shared_ptr<Reader::Entry::Impl> centry( new Reader::Entry::Impl );
+
+          centry->_kind = node_r.getAttribute("kind").asString();
+          centry->_name = node_r.getAttribute("name").asString();
+          centry->_edition = Edition( node_r.getAttribute("ver").asString(),
+                                      node_r.getAttribute("rel").asString(),
+                                      node_r.getAttribute("epoch").asString() );
+          centry->_arch = Arch( node_r.getAttribute("arch").asString() );
+
+          _value->push_back( Reader::Entry( centry ) );
+        }
+
+        std::list<Reader::Entry> *_value;
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::Impl::Impl
+    // METHOD TYPE : Constructor
+    //
+    Reader::Impl::Impl( std::istream & input_r )
+    {
+      xml::Reader reader( input_r );
+      SycontentNode rootNode( xml::ParseDef::MANDTAORY );
+
+      rootNode["ident"]["name"].setConsumer
+      ( new ConsumeString( _name ) );
+
+      rootNode["ident"]["version"].setConsumer
+      ( new ConsumeEdition( _edition ) );
+
+      rootNode["ident"]["description"].setConsumer
+      ( new ConsumeString( _description ) );
+
+      rootNode["ident"]["created"].setConsumer
+      ( new ConsumeDate( _created ) );
+
+      rootNode["onsys"]["entry"].setConsumer
+      ( new ConsumeEntries( _content ) );
+
+      // parse
+      rootNode.take( reader );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Reader::Reader()
+    : _pimpl( Impl::nullimpl() )
+    {}
+
+    Reader::Reader( std::istream & input_r )
+    : _pimpl( new Impl( input_r ) )
+    {}
+
+    const std::string & Reader::name() const
+    { return _pimpl->_name; }
+
+    const Edition & Reader::edition() const
+    { return _pimpl->_edition; }
+
+    const std::string & Reader::description() const
+    { return _pimpl->_description; }
+
+    const Date & Reader::ctime() const
+    { return _pimpl->_created; }
+
+    bool Reader::empty() const
+    { return _pimpl->_content.empty(); }
+
+    Reader::size_type Reader::size() const
+    { return _pimpl->_content.size(); }
+
+    Reader::const_iterator Reader::begin() const
+    { return _pimpl->_content.begin(); }
+
+    Reader::const_iterator Reader::end() const
+    { return _pimpl->_content.end(); }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : inline std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Reader & obj )
+    {
+      return str << "syscontent(" << obj.name() << "-" << obj.edition()
+                 << ", " << obj.size() << " entries"
+                 << ",  created " << obj.ctime()
+                 << ")";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace syscontent
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/SysContent.h b/zypp/SysContent.h
new file mode 100644 (file)
index 0000000..d95616d
--- /dev/null
@@ -0,0 +1,256 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/SysContent.h
+ *
+*/
+#ifndef ZYPP_SYSCONTENT_H
+#define ZYPP_SYSCONTENT_H
+
+#include <iosfwd>
+#include <string>
+#include <set>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/Edition.h"
+#include "zypp/Date.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace syscontent
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Writer
+    //
+    /** Collect and serialize a set of \ref ResObject.
+     * \code
+     * <?xml version="1.0" encoding="UTF-8"?>
+     * <syscontent>
+     *   <ident>
+     *     <name>mycollection</name>
+     *     <version epoch="0" ver="1.0" rel="1"/>
+     *     <description>All the cool stuff...</description>
+     *     <created>1165270942</created>
+     *   </ident>
+     *   <onsys>
+     *     <entry kind="package" name="pax" epoch="0" ver="3.4" rel="12" arch="x86_64"/>
+     *     <entry kind="product" name="SUSE_SLES" epoch="0" ver="10" arch="x86_64"/>
+     *     <entry ...
+     *   </onsys>
+     * </syscontent>
+     * \endcode
+     * \see Reader
+    */
+    class Writer
+    {
+      typedef std::set<ResObject::constPtr> StorageT;
+    public:
+      typedef StorageT::value_type     value_type;
+      typedef StorageT::size_type      size_type;
+      typedef StorageT::iterator       iterator;
+      typedef StorageT::const_iterator const_iterator;
+
+    public:
+      /** Default Ctor. */
+      Writer();
+
+    public:
+      /** \name Identification.
+       * User provided optional data to identify the collection.
+      */
+      //@{
+      /** Get name. */
+      const std::string & name() const;
+
+      /** Set name. */
+      Writer & name( const std::string & val_r );
+
+      /** Get edition. */
+      const Edition & edition() const;
+
+      /** Set edition. */
+      Writer & edition( const Edition & val_r );
+
+      /** Get description. */
+      const std::string & description() const;
+
+      /** Set description.*/
+      Writer & description( const std::string & val_r );
+      //@}
+
+    public:
+      /** \name Collecting data.
+       * \code
+       * syscontent::Writer contentW;
+       * contentW.name( "mycollection" )
+       *         .edition( Edition( "1.0" ) )
+       *         .description( "All the cool stuff..." );
+       *
+       * ResPool pool( getZYpp()->pool() );
+       * for_each( pool.begin(), pool.end(),
+       *           bind( &syscontent::Writer::addIf, ref(contentW), _1 ) );
+       *
+       * std::ofstream my_file( "some_file" );
+       * my_file << contentW;
+       * my_file.close();
+       * \endcode
+      */
+      //@{
+      /** Collect currently installed \ref PoolItem. */
+      void addInstalled( const PoolItem & obj_r );
+
+      /** Collect \ref PoolItem if it stays on the system.
+       * I.e. it stays installed or is tagged to be installed.
+       * Solver selected items are omitted.
+      */
+      void addIf( const PoolItem & obj_r );
+
+      /** Unconditionally add this \ref ResObject (or \ref PoolItem). */
+      void add( const ResObject::constPtr & obj_r );
+      //@}
+
+    public:
+      /** \name Collected data. */
+      //@{
+      /** Whether no data collected so far. */
+      bool empty() const;
+
+      /** Number of items collected. */
+      size_type size() const;
+
+      /** Iterator to the begin of collected data. */
+      const_iterator begin() const;
+
+      /** Iterator to the end of collected data. */
+      const_iterator end() const;
+      //@}
+
+    public:
+      /** Write collected data as XML.
+       * Read them back using \ref Reader.
+      */
+      std::ostream & writeXml( std::ostream & str ) const;
+
+    private:
+      class Impl;
+      RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Writer Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Writer & obj )
+    { return obj.writeXml( str ); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader
+    //
+    /** Retrieve \ref ResObject data serialized by \ref Writer.
+     * \see Writer
+    */
+    class Reader
+    {
+    public:
+      /** Restored \ref ResObject data. */
+      class Entry;
+
+    private:
+      typedef std::list<Entry> StorageT;
+
+    public:
+      typedef StorageT::value_type     value_type;
+      typedef StorageT::size_type      size_type;
+      typedef StorageT::iterator       iterator;
+      typedef StorageT::const_iterator const_iterator;
+
+    public:
+      /** Default Ctor. */
+      Reader();
+
+      /** Ctor parsing data from \a input_r.
+       * \throws Exception on read or parse error.
+      */
+      Reader( std::istream & input_r );
+
+    public:
+      /** \name Identification.
+       * User provided optional data to identify the collection.
+      */
+      //@{
+      /** Get name. */
+      const std::string & name() const;
+
+      /** Get edition. */
+      const Edition & edition() const;
+
+      /** Get description. */
+      const std::string & description() const;
+
+      /** Get creation date. */
+      const Date & ctime() const;
+
+    public:
+      /** \name Collected data. */
+      //@{
+      /** Whether no data collected so far. */
+      bool empty() const;
+
+      /** Number of items collected. */
+      size_type size() const;
+
+      /** Iterator to the begin of collected data. */
+      const_iterator begin() const;
+
+      /** Iterator to the end of collected data. */
+      const_iterator end() const;
+      //@}
+
+    private:
+      class Impl;
+      RWCOW_pointer<Impl> _pimpl;
+    };
+
+    /** \relates Reader Stream output */
+    std::ostream & operator<<( std::ostream & str, const Reader & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader::Entry
+    //
+    /** Restored \ref ResObject data. */
+    class Reader::Entry
+    {
+    public:
+      Entry();
+      const std::string & kind() const;
+      const std::string & name() const;
+      const Edition & edition() const;
+      const Arch & arch() const;
+    public:
+      class Impl;
+      Entry( const shared_ptr<Impl> & pimpl_r );
+    private:
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace syscontent
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SYSCONTENT_H
diff --git a/zypp/Target.cc b/zypp/Target.cc
new file mode 100644 (file)
index 0000000..d4f965a
--- /dev/null
@@ -0,0 +1,138 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Target.cc
+ *
+*/
+#include <cassert>
+
+#include <iostream>
+
+#include "zypp/Target.h"
+#include "zypp/target/TargetImpl.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  IMPL_PTR_TYPE(Target);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Target::Target
+  //   METHOD TYPE : Ctor
+  //
+  Target::Target( const Pathname & root, bool doRebuild_r )
+  : _pimpl( new Impl(root,doRebuild_r) )
+  {
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : Target::Target
+  //   METHOD TYPE : Ctor
+  //
+  Target::Target( const Impl_Ptr & impl_r )
+  : _pimpl( impl_r )
+  {
+    assert( impl_r );
+  }
+
+  Target_Ptr Target::_nullimpl;
+
+  /** Null implementation */
+  Target_Ptr Target::nullimpl()
+  {
+    if (! _nullimpl)
+    {
+      _nullimpl = new Target(target::TargetImpl::nullimpl());
+    }
+    return _nullimpl;
+  }
+
+  std::ostream & operator<<( std::ostream & str, const Target::DistributionLabel & obj )
+  {
+    str << "summary=" << obj.summary << endl;
+    str << "shortName=" << obj.shortName << endl;
+    return str;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   Forward to TargetImpl:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  void Target::buildCache()
+  { _pimpl->buildCache(); }
+
+  void Target::cleanCache()
+  { _pimpl->clearCache(); }
+
+  void Target::load()
+  { _pimpl->load(); }
+
+  void Target::unload()
+  { _pimpl->unload(); }
+
+  target::rpm::RpmDb & Target::rpmDb()
+  { return _pimpl->rpm(); }
+
+  Pathname Target::root() const
+  { return _pimpl->root(); }
+
+  bool Target::providesFile (const std::string & name_str, const std::string & path_str) const
+  { return _pimpl->providesFile (name_str, path_str); }
+
+  std::string Target::whoOwnsFile (const std::string & path_str) const
+  { return _pimpl->whoOwnsFile (path_str); }
+
+  std::ostream & Target::dumpOn( std::ostream & str ) const
+  { return _pimpl->dumpOn( str ); }
+
+  Date Target::timestamp() const
+  { return _pimpl->timestamp(); }
+
+  Product::constPtr Target::baseProduct() const
+  { return _pimpl->baseProduct(); }
+
+  std::string Target::targetDistribution() const
+  { return _pimpl->targetDistribution(); }
+  std::string Target::targetDistribution( const Pathname & root_r )
+  { return target::TargetImpl::targetDistribution( root_r ); }
+
+  std::string Target::targetDistributionRelease() const
+  { return _pimpl->targetDistributionRelease(); }
+  std::string Target::targetDistributionRelease( const Pathname & root_r )
+  { return target::TargetImpl::targetDistributionRelease( root_r ); }
+
+  Target::DistributionLabel Target::distributionLabel() const
+  { return _pimpl->distributionLabel(); }
+  Target::DistributionLabel Target::distributionLabel( const Pathname & root_r )
+  { return target::TargetImpl::distributionLabel( root_r ); }
+
+  std::string Target::distributionVersion() const
+  { return _pimpl->distributionVersion(); }
+  std::string Target::distributionVersion( const Pathname & root_r )
+  { return target::TargetImpl::distributionVersion( root_r ); }
+
+  std::string Target::distributionFlavor() const
+  { return _pimpl->distributionFlavor(); }
+  std::string Target::distributionFlavor( const Pathname & root_r )
+  { return target::TargetImpl::distributionFlavor( root_r ); }
+
+  std::string Target::anonymousUniqueId() const
+  { return _pimpl->anonymousUniqueId(); }
+  std::string Target::anonymousUniqueId( const Pathname & root_r )
+  { return target::TargetImpl::anonymousUniqueId( root_r ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/Target.h b/zypp/Target.h
new file mode 100644 (file)
index 0000000..0bffb98
--- /dev/null
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Target.h
+ *
+*/
+#ifndef ZYPP_TARGET_H
+#define ZYPP_TARGET_H
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/Product.h"
+#include "zypp/Pathname.h"
+#include "zypp/ResPool.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace target
+  {
+    class TargetImpl;
+    namespace rpm {
+      class RpmDb;
+    }
+  }
+  namespace zypp_detail
+  {
+    class ZYppImpl;
+  }
+
+  DEFINE_PTR_TYPE(Target);
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Target
+  //
+  /**
+  */
+  class Target : public base::ReferenceCounted, public base::NonCopyable
+  {
+  public:
+    typedef target::TargetImpl  Impl;
+    typedef intrusive_ptr<Impl> Impl_Ptr;
+    typedef std::list<PoolItem> PoolItemList;
+
+  public:
+
+    /**
+     * builds or refreshes the target cache
+     */
+    void buildCache();
+
+    /**
+     * cleans the target cache (.solv files)
+     */
+    void cleanCache();
+
+   /**
+     * load resolvables into the pool
+     */
+    void load();
+
+    /**
+     * unload target resolvables from the
+     * pool
+     */
+    void unload();
+
+    /** Null implementation */
+    static Target_Ptr nullimpl();
+
+    /** Refference to the RPM database */
+    target::rpm::RpmDb & rpmDb();
+
+    /** If the package is installed and provides the file
+     Needed to evaluate split provides during Resolver::Upgrade() */
+    bool providesFile (const std::string & name_str, const std::string & path_str) const;
+
+    /** Return name of package owning \a path_str
+     * or empty string if no installed package owns \a path_str.
+     **/
+    std::string whoOwnsFile (const std::string & path_str) const;
+
+    /** Return the root set for this target */
+    Pathname root() const;
+
+    /** Return the path prefixed by the target root, unless it already is prefixed. */
+    Pathname assertRootPrefix( const Pathname & path_r ) const
+    { return Pathname::assertprefix( root(), path_r ); }
+
+    /** return the last modification date of the target */
+    Date timestamp() const;
+
+    /**
+     * returns the target base installed product, also known as
+     * the distribution or platform.
+     *
+     * returns 0 if there is no base installed product in the
+     * pool.
+     *
+     * \note this method requires the target to be loaded,
+     * otherwise it will return 0 as no product is found.
+     *
+     * if you require some base product attributes when the
+     * target is not loaded into the pool, see
+     * \ref targetDistribution , \ref targetDistributionRelease
+     * and \ref distributionVersion that obtain the data
+     * on demand from the installed product information.
+     */
+    Product::constPtr baseProduct() const;
+
+  public:
+    /** \name Base product and registration.
+     *
+     * Static methods herein allow to retrieve the values without explicitly
+     * initializing the \ref Target. They take a targets root directory as
+     * argument. If an empty \ref Pathname is passed, an already existing
+     * Targets root is used, otherwise \c "\" is assumed.
+     */
+    //@{
+    /** This is \c register.target attribute of the installed base product.
+     * Used for registration and \ref Service refresh.
+     */
+    std::string targetDistribution() const;
+    /** \overload */
+    static std::string targetDistribution( const Pathname & root_r );
+
+    /** This is \c register.release attribute of the installed base product.
+     * Used for registration.
+     */
+    std::string targetDistributionRelease() const;
+    /** \overload */
+    static std::string targetDistributionRelease( const Pathname & root_r );
+
+    struct DistributionLabel { std::string shortName; std::string summary; };
+    /** This is \c shortName and \c summary attribute of the installed base product.
+     * Used e.g. for the bootloader menu.
+     */
+    DistributionLabel distributionLabel() const;
+    /** \overload */
+    static DistributionLabel distributionLabel( const Pathname & root_r );
+
+    /** This is \c version attribute of the installed base product.
+     * For example http://download.opensue.org/update/11.0
+     * The 11.0 corresponds to the base product version.
+     */
+    std::string distributionVersion() const;
+    /** \overload */
+    static std::string distributionVersion( const Pathname & root_r );
+
+    /**
+     * This is \c flavor attribute of the installed base product
+     * but does not require the target to be loaded as it remembers
+     * the last used one. It can be empty is the target has never
+     * been loaded, as the value is not present in the system
+     * but computer from a package provides
+     */
+    std::string distributionFlavor() const;
+    /** \overload */
+    static std::string distributionFlavor( const Pathname & root_r );
+
+    /**
+     * anonymous unique id
+     *
+     * This id is generated once and stays in the
+     * saved in the target.
+     * It is unique and is used only for statistics.
+     *
+     */
+    std::string anonymousUniqueId() const;
+    /** \overload */
+    static std::string anonymousUniqueId( const Pathname & root_r );
+    //@}
+
+  public:
+    /** Ctor. If \c doRebuild_r is \c true, an already existing
+     * database is rebuilt (rpm --rebuilddb ).
+    */
+    explicit
+    Target( const Pathname & root = "/", bool doRebuild_r = false );
+    /** Ctor */
+    explicit
+    Target( const Impl_Ptr & impl_r );
+
+  private:
+    friend std::ostream & operator<<( std::ostream & str, const Target & obj );
+    /** Stream output. */
+    std::ostream & dumpOn( std::ostream & str ) const;
+
+  private:
+    /** Direct access to Impl. */
+    friend class zypp_detail::ZYppImpl;
+
+    /** Pointer to implementation */
+    RW_pointer<Impl,rw_pointer::Intrusive<Impl> > _pimpl;
+
+    static Target_Ptr _nullimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Target Stream output. */
+  inline std::ostream & operator<<( std::ostream & str, const Target & obj )
+  { return obj.dumpOn( str ); }
+
+  /** \relates Target::DistributionLabel Stream output.
+   * Write out the content as key/value pairs:
+   * \code
+   * summary=Beautiful Name
+   * shortName=BN
+   * \endcode
+   */
+  std::ostream & operator<<( std::ostream & str, const Target::DistributionLabel & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_H
diff --git a/zypp/TmpPath.cc b/zypp/TmpPath.cc
new file mode 100644 (file)
index 0000000..dbe44e5
--- /dev/null
@@ -0,0 +1,302 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/TmpPath.cc
+ *
+*/
+
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+
+#include <iostream>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Logger.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace filesystem {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpPath::Impl
+    /**
+     * Clean or delete a directory on destruction.
+     **/
+    class TmpPath::Impl : public base::ReferenceCounted, private base::NonCopyable
+    {
+      public:
+
+        enum Flags
+          {
+            NoOp         = 0,
+            Autodelete   = 1L << 0,
+            KeepTopdir   = 1L << 1,
+            //
+            CtorDefault  = Autodelete
+          };
+
+      public:
+
+        Impl( const Pathname & path_r, Flags flags_r = CtorDefault )
+        : _path( path_r ), _flags( flags_r )
+        {}
+
+        ~Impl()
+        {
+          if ( ! (_flags & Autodelete) || _path.empty() )
+            return;
+
+          PathInfo p( _path, PathInfo::LSTAT );
+          if ( ! p.isExist() )
+            return;
+
+          int res = 0;
+          if ( p.isDir() )
+            {
+              if ( _flags & KeepTopdir )
+                res = clean_dir( _path );
+              else
+                res = recursive_rmdir( _path );
+            }
+          else
+            res = unlink( _path );
+
+          if ( res )
+            INT << "TmpPath cleanup error (" << res << ") " << p << endl;
+          else
+            DBG << "TmpPath cleaned up " << p << endl;
+        }
+
+        const Pathname &
+        path() const
+        { return _path; }
+
+      private:
+        Pathname _path;
+        Flags    _flags;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpPath
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpPath::TmpPath
+    // METHOD TYPE : Constructor
+    //
+    TmpPath::TmpPath()
+    :_impl( 0 ) // empty Pathname
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpPath::TmpPath
+    // METHOD TYPE : Constructor
+    //
+    TmpPath::TmpPath( const Pathname & tmpPath_r )
+    :_impl( tmpPath_r.empty() ? 0 : new Impl( tmpPath_r ) )
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpPath::~TmpPath
+    // METHOD TYPE : Destructor
+    //
+    TmpPath::~TmpPath()
+    {
+      // virtual not inlined dtor.
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //      METHOD NAME : TmpPath::operator const void *
+    //      METHOD TYPE :
+    //
+    TmpPath::operator const void * () const
+    {
+      return _impl.get();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpPath::path
+    // METHOD TYPE : Pathname
+    //
+    Pathname
+    TmpPath::path() const
+    {
+      return _impl.get() ? _impl->path() : Pathname();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpPath::defaultLocation
+    // METHOD TYPE : const Pathname &
+    //
+    const Pathname &
+    TmpPath::defaultLocation()
+    {
+      static Pathname p( getenv("ZYPPTMPDIR") ? getenv("ZYPPTMPDIR") : "/var/tmp" );
+      return p;
+    }
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpFile
+    //
+    ///////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpFile::TmpFile
+    // METHOD TYPE : Constructor
+    //
+    TmpFile::TmpFile( const Pathname & inParentDir_r,
+                      const std::string & prefix_r )
+    {
+      // parent dir must exist
+      if ( filesystem::assert_dir( inParentDir_r ) != 0 )
+      {
+        ERR << "Parent directory '" << inParentDir_r << "' can't be created." << endl;
+        return;
+      }
+
+      // create the temp file
+      Pathname tmpPath = (inParentDir_r + prefix_r).extend( "XXXXXX");
+      char * buf = ::strdup( tmpPath.asString().c_str() );
+      if ( ! buf )
+        {
+          ERR << "Out of memory" << endl;
+          return;
+        }
+
+      int tmpFd = ::mkstemp( buf );
+      if ( tmpFd != -1 )
+        {
+          // success; create _impl
+          ::close( tmpFd );
+          _impl = RW_pointer<Impl>( new Impl( buf ) );
+        }
+      else
+        ERR << "Cant create '" << buf << "' " << ::strerror( errno ) << endl;
+
+      ::free( buf );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpFile::makeSibling
+    // METHOD TYPE : TmpFile
+    //
+    TmpFile TmpFile::makeSibling( const Pathname & sibling_r )
+    {
+      TmpFile ret( sibling_r.dirname(), sibling_r.basename() );
+      // clone mode if sibling_r exists
+      PathInfo p( sibling_r );
+      if ( p.isFile() )
+      {
+        ::chmod( ret.path().c_str(), p.st_mode() );
+      }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpFile::defaultPrefix
+    // METHOD TYPE : const std::string &
+    //
+    const std::string &
+    TmpFile::defaultPrefix()
+    {
+      static string p( "TmpFile." );
+      return p;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpDir
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpDir::TmpDir
+    // METHOD TYPE : Constructor
+    //
+    TmpDir::TmpDir( const Pathname & inParentDir_r,
+                    const std::string & prefix_r )
+    {
+      // parent dir must exist
+      if ( filesystem::assert_dir( inParentDir_r ) != 0  )
+      {
+        ERR << "Parent directory '" << inParentDir_r << "' can't be created." << endl;
+        return;
+      }
+
+      // create the temp dir
+      Pathname tmpPath = (inParentDir_r + prefix_r).extend( "XXXXXX");
+      char * buf = ::strdup( tmpPath.asString().c_str() );
+      if ( ! buf )
+        {
+          ERR << "Out of memory" << endl;
+          return;
+        }
+
+      char * tmp = ::mkdtemp( buf );
+      if ( tmp )
+        // success; create _impl
+        _impl = RW_pointer<Impl>( new Impl( tmp ) );
+      else
+        ERR << "Cant create '" << tmpPath << "' " << ::strerror( errno ) << endl;
+
+      ::free( buf );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpDir::makeSibling
+    // METHOD TYPE : TmpDir
+    //
+    TmpDir TmpDir::makeSibling( const Pathname & sibling_r )
+    {
+      TmpDir ret( sibling_r.dirname(), sibling_r.basename() );
+      // clone mode if sibling_r exists
+      PathInfo p( sibling_r );
+      if ( p.isDir() )
+      {
+        ::chmod( ret.path().c_str(), p.st_mode() );
+      }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TmpDir::defaultPrefix
+    // METHOD TYPE : const std::string &
+    //
+    const std::string &
+    TmpDir::defaultPrefix()
+    {
+      static string p( "TmpDir." );
+      return p;
+    }
+
+  } // namespace filesystem
+} // namespace zypp
diff --git a/zypp/TmpPath.h b/zypp/TmpPath.h
new file mode 100644 (file)
index 0000000..bab81e3
--- /dev/null
@@ -0,0 +1,190 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/TmpPath.h
+ *
+*/
+#ifndef ZYPP_TMPPATH_H
+#define ZYPP_TMPPATH_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/base/PtrTypes.h"
+
+namespace zypp {
+  namespace filesystem {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpPath
+    /**
+     * @short Automaticaly deletes files or directories when no longer needed.
+     *
+     * TmpPath is constructed from a Pathname. Multiple TmpPath instances
+     * created by copy and assign, share the same reference counted internal
+     * repesentation.
+     *
+     * When the last reference drops any file or directory located at the path
+     * passed to the ctor is deleted (recursivly in case of directories).
+     *
+     * Principally serves as base class, but standalone usable.
+     **/
+    class TmpPath
+    {
+      public:
+        /**
+         * Default Ctor. An empty Pathname.
+         **/
+        TmpPath();
+
+        /**
+         * Ctor. Takes a Pathname.
+         **/
+        explicit
+        TmpPath( const Pathname & tmpPath_r );
+
+        /**
+         * Dtor.
+         **/
+        virtual
+        ~TmpPath();
+
+        /**
+         * Test whether the Pathname is valid (i.e. not empty. NOT whether
+         * it really denotes an existing file or directory).
+         **/
+        operator const void * () const;
+
+        /**
+         * @return The Pathname.
+         **/
+        Pathname
+        path() const;
+
+        /**
+         * Type conversion to Pathname.
+         **/
+        operator Pathname() const
+        { return path(); }
+
+      public:
+        /**
+         * @return The default directory where temporary
+         * files should be are created (/var/tmp).
+         **/
+        static const Pathname &
+        defaultLocation();
+
+      protected:
+        class Impl;
+        RW_pointer<Impl> _impl;
+
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Stream output as pathname.
+     **/
+    inline std::ostream &
+    operator<<( std::ostream & str, const TmpPath & obj )
+    { return str << static_cast<Pathname>(obj); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpFile
+    /**
+     * @short Provide a new empty temporary file and delete it when no
+     * longer needed.
+     *
+     * The temporary file is per default created in '/var/tmp' and named
+     * 'TmpFile.XXXXXX', with XXXXXX replaced by a string which makes the
+     * name unique. Different location and file prefix may be passed to
+     * the ctor. TmpFile is created with mode 0600.
+     *
+     * TmpFile provides the Pathname of the temporary file, or an empty
+     * path in case of any error.
+     **/
+    class TmpFile : public TmpPath
+    {
+      public:
+        /**
+         * Ctor. Takes a Pathname.
+         **/
+        explicit
+        TmpFile( const Pathname & inParentDir_r = defaultLocation(),
+                 const std::string & prefix_r = defaultPrefix() );
+
+        /** Provide a new empty temporary directory as sibling.
+         * \code
+         *   TmpFile s = makeSibling( "/var/lib/myfile" );
+         *   // returns: /var/lib/myfile.XXXXXX
+         * \endcode
+         * If \c sibling_r exists, sibling is created using the same mode.
+         */
+        static TmpFile makeSibling( const Pathname & sibling_r );
+
+      public:
+        /**
+         * @return The default prefix for temporary files (TmpFile.)
+         **/
+        static const std::string &
+        defaultPrefix();
+
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TmpDir
+    /**
+     * @short Provide a new empty temporary directory and recursively
+     * delete it when no longer needed.
+     *
+     * The temporary directory is per default created in '/var/tmp' and
+     * named 'TmpDir.XXXXXX', with XXXXXX replaced by a string which makes
+     * the  name unique. Different location and file prefix may be passed
+     * to the ctor. TmpDir is created with mode 0700.
+     *
+     * TmpDir provides the Pathname of the temporary directory , or an empty
+     * path in case of any error.
+     **/
+    class TmpDir : public TmpPath
+    {
+      public:
+        /**
+         * Ctor. Takes a Pathname.
+         **/
+        explicit
+        TmpDir( const Pathname & inParentDir_r = defaultLocation(),
+                const std::string & prefix_r = defaultPrefix() );
+
+        /** Provide a new empty temporary directory as sibling.
+         * \code
+         *   TmpDir s = makeSibling( "/var/lib/mydir" );
+         *   // returns: /var/lib/mydir.XXXXXX
+         * \endcode
+         * If \c sibling_r exists, sibling is created using the same mode.
+         */
+        static TmpDir makeSibling( const Pathname & sibling_r );
+
+      public:
+        /**
+         * @return The default prefix for temporary directories (TmpDir.)
+         **/
+        static const std::string &
+        defaultPrefix();
+    };
+    ///////////////////////////////////////////////////////////////////
+
+  } // namespace filesystem
+} // namespace zypp
+
+#endif // ZYPP_TMPPATH_H
diff --git a/zypp/TriBool.h b/zypp/TriBool.h
new file mode 100644 (file)
index 0000000..64488d1
--- /dev/null
@@ -0,0 +1,63 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/TriBool.h
+ *
+*/
+#ifndef ZYPP_TRIBOOL_H
+#define ZYPP_TRIBOOL_H
+
+#include <iosfwd>
+#include <boost/logic/tribool.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** 3-state boolean logic (\c true, \c false and \c indeterminate).
+   * \code
+   * namespace zypp
+   * {
+   *   typedef boost::logic::tribool TriBool;
+   *   using   boost::logic::tribool;
+   *   using   boost::logic::indeterminate;
+   * }
+   * \endcode
+   *
+   * \warning Be carefull.esp. when comparing \ref TriBool using
+   * \c operator==, as <b><tt>( indeterminate == indeterminate )</tt></b>
+   * does \b not evaluate \b true. It's \c indeterminate.
+   *
+   * \see http://www.boost.org/doc/html/tribool.html
+   * \ingroup BOOST
+  */
+  typedef boost::logic::tribool TriBool;
+  using   boost::logic::tribool;
+  using   boost::logic::indeterminate;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+namespace boost
+{
+    namespace logic
+    {
+      /** \relates TriBool stream output */
+      inline std::ostream & operator<<(std::ostream & s, const tribool & obj)
+      {
+        if (indeterminate(obj))
+          s << "indeterminate";
+        else if (obj)
+          s << "true";
+        else
+          s << "false";
+        return s;
+      }
+    }
+}
+#endif // ZYPP_TRIBOOL_H
diff --git a/zypp/Url.cc b/zypp/Url.cc
new file mode 100644 (file)
index 0000000..7f926ab
--- /dev/null
@@ -0,0 +1,864 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/Url.cc
+ */
+
+#include <zypp/Url.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Regex.h>
+#include <stdexcept>
+#include <iostream>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+
+  using namespace zypp::url;
+
+
+  // -----------------------------------------------------------------
+  /*
+   * url       = [scheme:] [//authority] /path [?query] [#fragment]
+   */
+  #define RX_SPLIT_URL                       "^([^:/?#]+:|)" \
+                                             "(//[^/?#]*|)"  \
+                                             "([^?#]*)"        \
+                                             "([?][^#]*|)"   \
+                                             "(#.*|)"
+
+
+  ////////////////////////////////////////////////////////////////////
+  namespace
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    class LDAPUrl: public UrlBase
+    {
+    public:
+      LDAPUrl(): UrlBase()
+      {
+        configure();
+      }
+
+      LDAPUrl(const LDAPUrl &url): UrlBase(url)
+      {}
+
+      virtual UrlBase *
+      clone() const
+      {
+        return new LDAPUrl(*this);
+      }
+
+      virtual UrlSchemes
+      getKnownSchemes() const
+      {
+        UrlSchemes schemes(2);
+        schemes[0] = "ldap";
+        schemes[1] = "ldaps";
+        return schemes;
+      }
+
+      virtual void
+      configure()
+      {
+        config("sep_pathparams",  "");
+
+        config("psep_querystr",   "?");
+        config("vsep_querystr",   "");
+
+        // host is required (isValid=>false)
+        // but not mandatory (see RFC 2255),
+        // that is, accept empty host.
+        config("require_host",    "y");
+
+        // not allowed here
+        config("rx_username",     "");
+        config("rx_password",     "");
+        config("rx_fragment",     "");
+        config("rx_pathparams",   "");
+      }
+
+      virtual zypp::url::ParamMap
+      getQueryStringMap(zypp::url::EEncoding eflag) const
+      {
+        static const char * const keys[] = {
+          "attrs", "scope", "filter", "exts", NULL
+        };
+        zypp::url::ParamMap pmap;
+        zypp::url::ParamVec pvec( getQueryStringVec());
+        if( pvec.size() <= 4)
+        {
+          for(size_t i=0; i<pvec.size(); i++)
+          {
+            if(eflag == zypp::url::E_ENCODED)
+              pmap[keys[i]] = pvec[i];
+            else
+              pmap[keys[i]] = zypp::url::decode( pvec[i]);
+          }
+        }
+        else
+        {
+          ZYPP_THROW(url::UrlNotSupportedException(
+            _("Invalid LDAP URL query string")
+          ));
+        }
+        return pmap;
+      }
+
+      virtual void
+      setQueryStringMap(const zypp::url::ParamMap &pmap)
+      {
+        static const char * const keys[] = {
+          "attrs", "scope", "filter", "exts", NULL
+        };
+
+        // remove psep ("?") from safe chars
+        std::string join_safe;
+        std::string safe(config("safe_querystr"));
+        std::string psep(config("psep_querystr"));
+        for(std::string::size_type i=0; i<safe.size(); i++)
+        {
+          if( psep.find(safe[i]) == std::string::npos)
+            join_safe.append(1, safe[i]);
+        }
+
+        zypp::url::ParamVec pvec(4);
+        zypp::url::ParamMap::const_iterator p;
+        for(p=pmap.begin(); p!=pmap.end(); ++p)
+        {
+          bool found=false;
+          for(size_t i=0; i<4; i++)
+          {
+            if(p->first == keys[i])
+            {
+              found=true;
+              pvec[i] = zypp::url::encode(p->second, join_safe);
+            }
+          }
+          if( !found)
+          {
+            ZYPP_THROW(url::UrlNotSupportedException(
+              str::form(_("Invalid LDAP URL query parameter '%s'"),
+                          p->first.c_str())
+            ));
+          }
+        }
+        setQueryStringVec(pvec);
+      }
+    };
+
+
+    // ---------------------------------------------------------------
+    // FIXME: hmm..
+    class UrlByScheme
+    {
+    private:
+      typedef std::map<std::string,UrlRef> UrlBySchemeMap;
+      UrlBySchemeMap urlByScheme;
+
+    public:
+      UrlByScheme()
+      {
+        UrlRef ref;
+
+        // =====================================
+        ref.reset( new LDAPUrl());
+        addUrlByScheme("ldap", ref);
+        addUrlByScheme("ldaps", ref);
+
+
+        // =====================================
+        ref.reset( new UrlBase());
+        ref->config("with_authority",   "n");   // disallow host,...
+        ref->config("require_pathname", "m");   // path is mandatory
+        addUrlByScheme("hd",     ref);
+        addUrlByScheme("cd",     ref);
+        addUrlByScheme("dvd",    ref);
+        addUrlByScheme("dir",    ref);
+        addUrlByScheme("iso",    ref);
+
+        // don't show empty authority
+        ref->setViewOptions( zypp::url::ViewOption::DEFAULTS -
+                             zypp::url::ViewOption::EMPTY_AUTHORITY);
+        addUrlByScheme("mailto", ref);
+        addUrlByScheme("urn",    ref);
+        addUrlByScheme("plugin", ref); // zypp plugable media handler:
+
+        // RFC1738, 3.10: may contain a host
+        ref->config("with_authority",   "y");   // allow host,
+        ref->config("with_port",        "n");   // but no port,
+        ref->config("rx_username",      "");    // username or
+        ref->config("rx_password",      "");    // password ...
+        addUrlByScheme("file",   ref);
+
+        // =====================================
+        ref.reset( new UrlBase());
+        ref->config("require_host",     "m");   // host is mandatory
+        addUrlByScheme("nfs",    ref);
+        addUrlByScheme("nfs4",   ref);
+        addUrlByScheme("smb",    ref);
+        addUrlByScheme("cifs",   ref);
+        addUrlByScheme("http",   ref);
+        addUrlByScheme("https",  ref);
+        ref->config("path_encode_slash2", "y"); // always encode 2. slash
+        addUrlByScheme("ftp",    ref);
+        addUrlByScheme("sftp",   ref);
+      }
+
+      bool
+      addUrlByScheme(const std::string &scheme,
+                     UrlRef            urlImpl)
+      {
+        if( urlImpl && urlImpl->isValidScheme(scheme))
+        {
+          UrlRef ref(urlImpl);
+          ref->clear();
+          urlByScheme[str::toLower(scheme)] = ref;
+          return true;
+        }
+        return false;
+      }
+
+      UrlRef
+      getUrlByScheme(const std::string &scheme) const
+      {
+        UrlBySchemeMap::const_iterator i(urlByScheme.find(str::toLower(scheme)));
+        if( i != urlByScheme.end())
+        {
+          return i->second;
+        }
+        return UrlRef();
+      }
+
+      bool
+      isRegisteredScheme(const std::string &scheme) const
+      {
+        return urlByScheme.find(str::toLower(scheme)) != urlByScheme.end();
+      }
+
+      UrlSchemes
+      getRegisteredSchemes() const
+      {
+        UrlBySchemeMap::const_iterator i(urlByScheme.begin());
+        UrlSchemes                     schemes;
+
+        schemes.reserve(urlByScheme.size());
+        for( ; i != urlByScheme.end(); ++i)
+        {
+          schemes.push_back(i->first);
+        }
+        return schemes;
+      }
+    };
+
+
+    // ---------------------------------------------------------------
+    UrlByScheme & g_urlSchemeRepository()
+    {
+      static UrlByScheme _v;
+      return _v;
+    }
+
+    //////////////////////////////////////////////////////////////////
+  } // anonymous namespace
+  ////////////////////////////////////////////////////////////////////
+
+
+  // -----------------------------------------------------------------
+  Url::~Url()
+  {
+  }
+
+
+  // -----------------------------------------------------------------
+  Url::Url()
+    : m_impl( new UrlBase())
+  {
+  }
+
+
+  // -----------------------------------------------------------------
+  Url::Url(const Url &url)
+    : m_impl( url.m_impl)
+  {
+    if( !m_impl)
+    {
+      ZYPP_THROW(url::UrlException(
+        _("Unable to clone Url object")
+      ));
+    }
+  }
+
+
+  // -----------------------------------------------------------------
+  Url::Url(const zypp::url::UrlRef &url)
+    : m_impl( url)
+  {
+    if( !m_impl)
+    {
+      ZYPP_THROW(url::UrlException(
+        _("Invalid empty Url object reference")
+      ));
+    }
+  }
+
+
+  // -----------------------------------------------------------------
+  Url::Url(const std::string &encodedUrl)
+    : m_impl( parseUrl(encodedUrl))
+  {
+    if( !m_impl)
+    {
+      ZYPP_THROW(url::UrlParsingException(
+        _("Unable to parse Url components")
+      ));
+    }
+  }
+
+
+  // -----------------------------------------------------------------
+  Url&
+  Url::operator = (const std::string &encodedUrl)
+  {
+    UrlRef url( parseUrl(encodedUrl));
+    if( !url)
+    {
+      ZYPP_THROW(url::UrlParsingException(
+        _("Unable to parse Url components")
+      ));
+    }
+    m_impl = url;
+    return *this;
+  }
+
+
+  // -----------------------------------------------------------------
+  Url&
+  Url::operator = (const Url &url)
+  {
+    m_impl = url.m_impl;
+    return *this;
+  }
+
+
+  // -----------------------------------------------------------------
+  // static
+  bool
+  Url::registerScheme(const std::string &scheme,
+                      UrlRef            urlImpl)
+  {
+    return g_urlSchemeRepository().addUrlByScheme(scheme, urlImpl);
+  }
+
+
+  // -----------------------------------------------------------------
+  // static
+  UrlRef
+  Url::parseUrl(const std::string &encodedUrl)
+  {
+    UrlRef      url;
+    str::smatch out;
+    bool        ret = false;
+
+    try
+    {
+      str::regex  rex(RX_SPLIT_URL);
+      ret = str::regex_match(encodedUrl, out, rex);
+    }
+    catch( ... )
+    {}
+
+    if(ret && out.size() == 5)
+    {
+      std::string scheme = out[1];
+      if (scheme.size() > 1)
+        scheme = scheme.substr(0, scheme.size()-1);
+      std::string authority = out[2];
+      if (authority.size() >= 2)
+        authority = authority.substr(2);
+      std::string query = out[4];
+      if (query.size() > 1)
+        query = query.substr(1);
+      std::string fragment = out[5];
+      if (fragment.size() > 1)
+        fragment = fragment.substr(1);
+
+      url = g_urlSchemeRepository().getUrlByScheme(scheme);
+      if( !url)
+      {
+        url.reset( new UrlBase());
+      }
+      url->init(scheme, authority, out[3],
+                query, fragment);
+    }
+    return url;
+  }
+
+
+  // -----------------------------------------------------------------
+  // static
+  zypp::url::UrlSchemes
+  Url::getRegisteredSchemes()
+  {
+    return g_urlSchemeRepository().getRegisteredSchemes();
+  }
+
+
+  // -----------------------------------------------------------------
+  // static
+  bool
+  Url::isRegisteredScheme(const std::string &scheme)
+  {
+    return g_urlSchemeRepository().isRegisteredScheme(scheme);
+  }
+
+
+  // -----------------------------------------------------------------
+  zypp::url::UrlSchemes
+  Url::getKnownSchemes() const
+  {
+    return m_impl->getKnownSchemes();
+  }
+
+
+  // -----------------------------------------------------------------
+  bool
+  Url::isValidScheme(const std::string &scheme) const
+  {
+    return m_impl->isValidScheme(scheme);
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  {
+    inline bool isInList( const char ** begin_r, const char ** end_r, const std::string & scheme_r )
+    {
+      for ( ; begin_r != end_r; ++begin_r )
+        if ( scheme_r == *begin_r )
+          return true;
+      return false;
+    }
+  }
+  bool Url::schemeIsLocal( const std::string & scheme_r )
+  {
+    static const char * val[] = { "cd", "dvd", "dir", "hd", "iso", "file" };
+    return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
+  }
+
+  bool Url::schemeIsRemote( const std::string & scheme_r )
+  {
+    static const char * val[] = { "http", "https", "nfs", "nfs4", "smb", "cifs", "ftp", "sftp" };
+    return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
+  }
+
+  bool Url::schemeIsVolatile( const std::string & scheme_r )
+  {
+    static const char * val[] = { "cd", "dvd" };
+    return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
+  }
+
+  bool Url::schemeIsDownloading( const std::string & scheme_r )
+  {
+    static const char * val[] = { "http", "https", "ftp", "sftp" };
+    return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
+  }
+  ///////////////////////////////////////////////////////////////////
+
+  // -----------------------------------------------------------------
+  bool
+  Url::isValid() const
+  {
+    return m_impl->isValid();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::asString() const
+  {
+    return m_impl->asString();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::asCompleteString() const
+  {
+    // make sure, all url components are included;
+    // regardless of the current configuration...
+    ViewOptions opts(getViewOptions() +
+                     ViewOption::WITH_SCHEME +
+                     ViewOption::WITH_USERNAME +
+                     ViewOption::WITH_PASSWORD +
+                     ViewOption::WITH_HOST +
+                     ViewOption::WITH_PORT +
+                     ViewOption::WITH_PATH_NAME +
+                     ViewOption::WITH_PATH_PARAMS +
+                     ViewOption::WITH_QUERY_STR +
+                     ViewOption::WITH_FRAGMENT);
+    return m_impl->asString(opts);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::asString(const ViewOptions &opts) const
+  {
+    return m_impl->asString(opts);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getScheme() const
+  {
+    return m_impl->getScheme();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getAuthority() const
+  {
+    return m_impl->getAuthority();
+  }
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPathData() const
+  {
+    return m_impl->getPathData();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getQueryString() const
+  {
+    return m_impl->getQueryString();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getFragment(zypp::url::EEncoding eflag) const
+  {
+    return m_impl->getFragment(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getUsername(EEncoding eflag) const
+  {
+    return m_impl->getUsername(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPassword(EEncoding eflag) const
+  {
+    return m_impl->getPassword(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getHost(EEncoding eflag) const
+  {
+    return m_impl->getHost(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPort() const
+  {
+    return m_impl->getPort();
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPathName(EEncoding eflag) const
+  {
+    return m_impl->getPathName(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPathParams() const
+  {
+    return m_impl->getPathParams();
+  }
+
+
+  // -----------------------------------------------------------------
+  zypp::url::ParamVec
+  Url::getPathParamsVec() const
+  {
+    return m_impl->getPathParamsVec();
+  }
+
+
+  // -----------------------------------------------------------------
+  zypp::url::ParamMap
+  Url::getPathParamsMap(EEncoding eflag) const
+  {
+    return m_impl->getPathParamsMap(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getPathParam(const std::string &param, EEncoding eflag) const
+  {
+    return m_impl->getPathParam(param, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  zypp::url::ParamVec
+  Url::getQueryStringVec() const
+  {
+    return m_impl->getQueryStringVec();
+  }
+
+
+  // -----------------------------------------------------------------
+  zypp::url::ParamMap
+  Url::getQueryStringMap(EEncoding eflag) const
+  {
+    return m_impl->getQueryStringMap(eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  std::string
+  Url::getQueryParam(const std::string &param, EEncoding eflag) const
+  {
+    return m_impl->getQueryParam(param, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setScheme(const std::string &scheme)
+  {
+    if(scheme == m_impl->getScheme())
+    {
+      return;
+    }
+    if( m_impl->isKnownScheme(scheme))
+    {
+      m_impl->setScheme(scheme);
+      return;
+    }
+
+    UrlRef url = g_urlSchemeRepository().getUrlByScheme(scheme);
+    if( !url)
+    {
+      url.reset( new UrlBase());
+    }
+    url->init(
+      scheme,
+      m_impl->getAuthority(),
+      m_impl->getPathData(),
+      m_impl->getQueryString(),
+      m_impl->getFragment(zypp::url::E_ENCODED)
+    );
+    m_impl = url;
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setAuthority(const std::string &authority)
+  {
+    m_impl->setAuthority(authority);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathData(const std::string &pathdata)
+  {
+    m_impl->setPathData(pathdata);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setQueryString(const std::string &querystr)
+  {
+    m_impl->setQueryString(querystr);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setFragment(const std::string &fragment, EEncoding eflag)
+  {
+    m_impl->setFragment(fragment, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setUsername(const std::string &user,
+                   EEncoding         eflag)
+  {
+    m_impl->setUsername(user, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPassword(const std::string &pass,
+                   EEncoding         eflag)
+  {
+    m_impl->setPassword(pass, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setHost(const std::string &host)
+  {
+    m_impl->setHost(host);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPort(const std::string &port)
+  {
+    m_impl->setPort(port);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathName(const std::string &path,
+                   EEncoding         eflag)
+  {
+    m_impl->setPathName(path, eflag);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathParams(const std::string &params)
+  {
+    m_impl->setPathParams(params);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathParamsVec(const zypp::url::ParamVec &pvec)
+  {
+    m_impl->setPathParamsVec(pvec);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathParamsMap(const zypp::url::ParamMap &pmap)
+  {
+    m_impl->setPathParamsMap(pmap);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setPathParam(const std::string &param, const std::string &value)
+  {
+    m_impl->setPathParam(param, value);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setQueryStringVec(const zypp::url::ParamVec &pvec)
+  {
+    m_impl->setQueryStringVec(pvec);
+  }
+
+
+  // -----------------------------------------------------------------
+  void
+  Url::setQueryStringMap(const zypp::url::ParamMap &pmap)
+  {
+    m_impl->setQueryStringMap(pmap);
+  }
+
+  // -----------------------------------------------------------------
+  void
+  Url::setQueryParam(const std::string &param, const std::string &value)
+  {
+    m_impl->setQueryParam(param, value);
+  }
+
+  // -----------------------------------------------------------------
+  void
+  Url::delQueryParam(const std::string &param)
+  {
+    m_impl->delQueryParam(param);
+  }
+
+  // -----------------------------------------------------------------
+  ViewOptions
+  Url::getViewOptions() const
+  {
+    return m_impl->getViewOptions();
+  }
+
+  // -----------------------------------------------------------------
+  void
+  Url::setViewOptions(const ViewOptions &vopts)
+  {
+    m_impl->setViewOptions(vopts);
+  }
+
+  // -----------------------------------------------------------------
+  std::ostream & operator<<( std::ostream & str, const Url & url )
+  {
+    return str << url.asString();
+  }
+
+  bool operator<( const Url &lhs, const Url &rhs )
+  {
+    return (lhs.asCompleteString() < rhs.asCompleteString());
+  }
+
+  bool operator==( const Url &lhs, const Url &rhs )
+  {
+    return (lhs.asCompleteString() == rhs.asCompleteString());
+  }
+
+  bool operator!=( const Url &lhs, const Url &rhs )
+  {
+    return (lhs.asCompleteString() != rhs.asCompleteString());
+  }
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/Url.h b/zypp/Url.h
new file mode 100644 (file)
index 0000000..5c94a69
--- /dev/null
@@ -0,0 +1,832 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/Url.h
+ */
+#ifndef   ZYPP_URL_H
+#define   ZYPP_URL_H
+
+#include <zypp/url/UrlBase.h>
+#include <zypp/url/UrlUtils.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+
+  /**
+   * \class Url
+   * \brief Url manipulation class.
+   *
+   * The generic URL (URI) syntax and its main components are defined in
+   * RFC3986 (http://rfc.net/rfc3986.html) Section 3, "Syntax Components".
+   * The scheme specific URL syntax and semantics is defined in the
+   * specification of the particular scheme. See also RFC1738
+   * (http://rfc.net/rfc1738.html), that defines specific syntax for
+   * several URL schemes.
+   *
+   * This class provides methods to access and manipulate generic and
+   * common scheme-specific URL components (or using the more general
+   * term, URI components).
+   * To consider the scheme-specifics of a URL, the Url class contains
+   * a reference object pointing to a UrlBase or derived object, that
+   * implements the scheme specifics.
+   *
+   * Using the Url::registerScheme() method, it is possible to register
+   * a preconfigured or derived UrlBase object for a specific scheme
+   * name. The registered object will be cloned to handle all URL's
+   * containing the specified scheme name.
+   *
+   * \par RFC3986, Syntax Components:
+   *
+   * The generic URI syntax consists of a hierarchical sequence of
+   * components referred to as the scheme, authority, path, query,
+   * and fragment.
+   *
+   * \code
+   *    URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+   *
+   *    hier-part   = "//" authority path-abempty
+   *                / path-absolute
+   *                / path-rootless
+   *                / path-empty
+   * \endcode
+   *
+   * The scheme and path components are required, though the path may be
+   * empty (no characters).
+   * When authority is present, the path must either be empty or begin
+   * with a slash ("/") character.
+   * When authority is not present, the path cannot begin with two slash
+   * characters ("//").
+   * These restrictions result in five different ABNF rules for a path
+   * (Section 3.3), only one of which will match any given URI reference.
+   *
+   * The following are two example URIs and their component parts:
+   * \code
+   *      foo://example.com:8042/over/there?name=ferret#nose
+   *      \_/   \______________/\_________/ \_________/ \__/
+   *       |           |            |            |        |
+   *    scheme     authority       path        query   fragment
+   *       |   _____________________|__
+   *      / \ /                        \
+   *      urn:example:animal:ferret:nose
+   * \endcode
+   *
+   */
+  class Url
+  {
+  public:
+    /**
+     * Encoding flags.
+     */
+    typedef zypp::url::EEncoding    EEncoding;
+
+    /**
+     * View options.
+     */
+    typedef zypp::url::ViewOptions  ViewOptions;
+
+
+    ~Url();
+    Url();
+
+    /**
+     * Create a new Url object as shared copy of the given one.
+     *
+     * Upon return, both objects will point to the same underlying
+     * object. This state will remain until one of the object is
+     * modified.
+     *
+     * \param url The Url object to make a copy of.
+     * \throws url::UrlException if copy fails (should not happen).
+     */
+    Url(const Url &url);
+
+
+    /**
+     * Create a new Url object as shared copy of the given reference.
+     *
+     * Upon return, both objects will point to the same underlying
+     * object. This state will remain until one of the object is
+     * modified.
+     *
+     * \param url The URL implementation reference to make a copy of.
+     * \throws url::UrlException if reference is empty.
+     */
+    Url(const zypp::url::UrlRef &url);
+
+
+    /**
+     * \brief Construct a Url object from percent-encoded URL string.
+     *
+     * Parses the \p encodedUrl string using the parseUrl() method
+     * and assings the result to the new created object.
+     *
+     * \param encodedUrl A percent-encoded URL string.
+     * \throws url::UrlParsingException if parsing of the url fails.
+     * \throws url::UrlNotAllowedException if one of the components
+     *         is not allowed for the scheme.
+     * \throws url::UrlBadComponentException if one of the components
+     *         contains an invalid character.
+     */
+    Url(const std::string &encodedUrl);
+
+
+    // -----------------
+    /**
+     * \brief Parse a percent-encoded URL string.
+     *
+     * Trys to parses the given string into generic URL components
+     * and created a clone of a scheme-specialized object or a new
+     * UrlBase object.
+     *
+     * \param encodedUrl A percent-encoded URL string.
+     * \return           A reference to a (derived) UrlBase object or
+     *                   empty reference if the \p encodedUrl string
+     *                   does not match the generic URL syntax.
+     * \throws url::UrlNotAllowedException if one of the components
+     *         is not allowed for the scheme.
+     * \throws url::UrlBadComponentException if one of the components
+     *         contains an invalid character.
+     */
+    static url::UrlRef
+    parseUrl(const std::string &encodedUrl);
+
+
+    // -----------------
+    /**
+     * \brief Assigns parsed percent-encoded URL string to the object.
+     *
+     * Parses \p encodedUrl string using the parseUrl() method
+     * and assigns the result to the current object.
+     *
+     * \param encodedUrl A percent-encoded URL string.
+     * \return A reference to this Url object.
+     * \throws url::UrlParsingException if parsing of the url fails.
+     * \throws url::UrlNotAllowedException if one of the components
+     *         is not allowed for the scheme.
+     * \throws url::UrlBadComponentException if one of the components
+     *         contains an invalid character.
+     */
+    Url&
+    operator = (const std::string &encodedUrl);
+
+
+    /**
+     * \brief Assign shared copy of \p url to the current object.
+     *
+     * Upon return, both objects will point to the same underlying
+     * object. This state will remain until one of the object is
+     * modified.
+     *
+     * \param url The Url object to make a copy of.
+     * \return A reference to this Url object.
+     */
+    Url&
+    operator = (const Url &url);
+
+
+    // -----------------
+    /**
+     * \brief Register a scheme-specific implementation.
+     *
+     * \param scheme  A name of a scheme.
+     * \param urlImpl A UrlBase object specialized for this scheme.
+     * \return True, if the object claims to implement the scheme.
+     */
+    static bool
+    registerScheme(const std::string &scheme,
+                   url::UrlRef       urlImpl);
+
+    /**
+     * \brief Returns all registered scheme names.
+     * \return A vector with registered URL scheme names.
+     */
+    static zypp::url::UrlSchemes
+    getRegisteredSchemes();
+
+    /**
+     * \brief Returns if scheme name is registered.
+     * \return True, if scheme name is registered.
+     */
+    static bool
+    isRegisteredScheme(const std::string &scheme);
+
+
+    // -----------------
+    /**
+     * \brief Returns scheme names known to this object.
+     * \return A vector with scheme names known by this object.
+     */
+    zypp::url::UrlSchemes
+    getKnownSchemes() const;
+
+
+    /**
+     * \brief Verifies specified scheme name.
+     *
+     * Verifies the generic syntax of the specified \p scheme name
+     * and if it is contained in the current object's list of known
+     * schemes (see getKnownSchemes()) if the list is not empty.
+     *
+     * The default implementation in the UrlBase class returns an
+     * emtpy list of known schemes, causing a check of the generic
+     * syntax only.
+     *
+     * \return True, if generic scheme name syntax is valid and
+     *         the scheme name is known to the current object.
+     */
+    bool
+    isValidScheme(const std::string &scheme) const;
+
+
+    /** hd cd dvd dir file iso */
+    static bool schemeIsLocal( const std::string & scheme_r );
+    /** \overload nonstatic version */
+    bool schemeIsLocal() const { return schemeIsLocal( getScheme() ); }
+
+    /** nfs nfs4 smb cifs http https ftp sftp */
+    static bool schemeIsRemote( const std::string & scheme_r );
+    /** \overload nonstatic version */
+    bool schemeIsRemote() const { return schemeIsRemote( getScheme() ); }
+
+    /** cd dvd */
+    static bool schemeIsVolatile( const std::string & scheme_r );
+    /** \overload nonstatic version */
+    bool schemeIsVolatile() const { return schemeIsVolatile( getScheme() ); }
+
+    /** http https ftp sftp */
+    static bool schemeIsDownloading( const std::string & scheme_r );
+    /** \overload nonstatic version */
+    bool schemeIsDownloading() const { return schemeIsDownloading( getScheme() ); }
+
+    /**
+     * \brief Verifies the Url.
+     *
+     * Verifies if the current object contains a non-empty scheme
+     * name. Additional semantical URL checks may be performed by
+     * derived UrlBase objects.
+     *
+     * \return True, if the Url seems to be valid.
+     */
+    bool
+    isValid() const;
+
+
+    // -----------------
+    /**
+     * Returns a default string representation of the Url object.
+     *
+     * By default, a password in the URL will be hidden.
+     *
+     * \return A default string representation of the Url object.
+     */
+    std::string
+    asString() const;
+
+    /**
+     * Returns a string representation of the Url object.
+     *
+     * To include a password in the resulting Url string, use:
+     * \code
+     *    url.asString(url.getViewOptions() +
+     *                 url::ViewOptions::WITH_PASSWORD);
+     * \endcode
+     *
+     * \param opts  A combination of view options.
+     * \return A string representation of the Url object.
+     */
+    std::string
+    asString(const ViewOptions &opts) const;
+
+    /**
+     * Returns a complete string representation of the Url object.
+     *
+     * This function ignores the configuration of the view options
+     * in the current object (see setViewOption()) and forces to
+     * return an string with all URL components included.
+     *
+     * \return A complete string representation of the Url object.
+     */
+    std::string
+    asCompleteString() const;
+
+
+    // -----------------
+    /**
+     * Returns the scheme name of the URL.
+     * \return Scheme name of the current Url object.
+     */
+    std::string
+    getScheme() const;
+
+
+    // -----------------
+    /**
+     * Returns the encoded authority component of the URL.
+     *
+     * The returned authority string does not contain the leading
+     * "//" separator characters, but just its "user:pass@host:port"
+     * content only.
+     *
+     * \return The encoded authority component string.
+     */
+    std::string
+    getAuthority() const;
+
+    /**
+     * Returns the username from the URL authority.
+     * \param eflag Flag if the usename should be percent-decoded or not.
+     * \return The username sub-component from the URL authority.
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getUsername(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Returns the password from the URL authority.
+     * \param eflag Flag if the password should be percent-decoded or not.
+     * \return The password sub-component from the URL authority.
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getPassword(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Returns \c true if username \b and password are encoded in the authority component.
+     */
+    bool hasCredentialsInAuthority() const
+    { return ! ( getUsername().empty() || getPassword().empty() ); }
+
+    /**
+     * Returns the hostname or IP from the URL authority.
+     *
+     * In case the Url contains an IP number, it may be surrounded
+     * by "[" and "]" characters, for example "[::1]" for an IPv6
+     * localhost address.
+     *
+     * \param eflag Flag if the host should be percent-decoded or not.
+     * \return The host sub-component from the URL authority.
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getHost(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Returns the port from the URL authority.
+     * \return The port sub-component from the URL authority.
+     */
+    std::string
+    getPort() const;
+
+
+    // -----------------
+    /**
+     * Returns the encoded path component of the URL.
+     *
+     * The path data contains the path name, optionally
+     * followed by path parameters separated with a ";"
+     * character, for example "/foo/bar;version=1.1".
+     *
+     * \return The encoded path component of the URL.
+     */
+    std::string
+    getPathData() const;
+
+    /**
+     * Returns the path name from the URL.
+     * \param eflag Flag if the path should be decoded or not.
+     * \return The path name sub-component without path parameters
+     *  from Path-Data component of the URL.
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getPathName(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Returns the path parameters from the URL.
+     * \return The encoded path parameters from the URL.
+     */
+    std::string
+    getPathParams() const;
+
+    /**
+     * Returns a vector with path parameter substrings.
+     *
+     * The default path parameter separator is the \c ',' character.
+     * A schema specific object may overide the default separators.
+     *
+     * For example, the path parameters string "foo=1,bar=2" is splited
+     * by default into a vector containing the substrings "foo=1" and
+     * "bar=2".
+     *
+     * \return The path parameters splited into a vector of substrings.
+     */
+    zypp::url::ParamVec
+    getPathParamsVec() const;
+
+    /**
+     * Returns a string map with path parameter keys and values.
+     *
+     * The default path parameter separator is the \c ',' character,
+     * the default key/value separator for the path parameters is
+     * the \c '=' character.
+     * A schema specific object may overide the default separators.
+     *
+     * For example, the path parameters string "foo=1,bar=2" is splited
+     * into a map containing "foo" = "1" and "bar" = "2" by default.
+     *
+     * \param eflag Flag if the path parameter keys and values should
+     *               be decoded or not.
+     * \return The path parameters key and values as a string map.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    zypp::url::ParamMap
+    getPathParamsMap(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Return the value for the specified path parameter.
+     *
+     * For example, if the path parameters string is "foo=1,bar=2"
+     * the method will return the substring "1" for the param key
+     * "foo" and "2" for the param key "bar".
+     *
+     * \param param The path parameter key.
+     * \param eflag Flag if the path parameter keys and values should
+     *              be decoded or not.
+     * \return The value for the path parameter key or empty string.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getPathParam(const std::string &param,
+                 EEncoding eflag = zypp::url::E_DECODED) const;
+
+
+    // -----------------
+    /**
+     * Returns the encoded query string component of the URL.
+     *
+     * The query string is returned without first "?" (separator)
+     * character. Further "?" characters as in e.g. LDAP URL's
+     * remains in the returned string.
+     *
+     * \return The encoded query string component of the URL.
+     */
+    std::string
+    getQueryString() const;
+
+    /**
+     * Returns a vector with query string parameter substrings.
+     *
+     * The default query string parameter separator is the \c '&'
+     * character.
+     * A schema specific object may overide the default separators.
+     *
+     * For example, the query string "foo=1&bar=2" is splited by
+     * default into a vector containing the substrings "foo=1" and
+     * "bar=2".
+     *
+     * \return The query string splited into a vector of substrings.
+     */
+    zypp::url::ParamVec
+    getQueryStringVec() const;
+
+    /**
+     * Returns a string map with query parameter and their values.
+     *
+     * The default query string parameter separator is the \c ','
+     * character, the default key/value separator the \c '=' character.
+     * A schema specific object may overide the default separators.
+     *
+     * For example, the query string "foo=1&bar=2" is splited by
+     * default into a map containing "foo" = "1" and "bar" = "2".
+     *
+     * \param eflag Flag if the query string keys and values should
+     *               be decoded or not.
+     * \return The query string as a key/value string map.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    zypp::url::ParamMap
+    getQueryStringMap(EEncoding eflag = zypp::url::E_DECODED) const;
+
+    /**
+     * Return the value for the specified query parameter.
+     *
+     * For example, if the query string is "foo=1,bar=2" the method
+     * will return the substring "1" for the param key "foo" and
+     * "2" for the param key "bar".
+     *
+     * \param param The query parameter key.
+     * \param eflag Flag if the query parameter keys and values should
+     *              be decoded or not.
+     * \return The value for the query parameter key or empty string.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getQueryParam(const std::string &param,
+                  EEncoding eflag = zypp::url::E_DECODED) const;
+
+
+    // -----------------
+    /**
+     * Returns the encoded fragment component of the URL.
+     * \param eflag Flag if the fragment should be percent-decoded or not.
+     * \return The encoded fragment component of the URL.
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    std::string
+    getFragment(EEncoding eflag = zypp::url::E_DECODED) const;
+
+
+    // -----------------
+    /**
+     * \brief Set the scheme name in the URL.
+     * \param scheme The new scheme name.
+     * \throws url::UrlBadComponentException if the \p scheme
+     *         contains an invalid character or is empty.
+     */
+    void
+    setScheme(const std::string &scheme);
+
+
+    // -----------------
+    /**
+     * \brief Set the authority component in the URL.
+     *
+     * The \p authority string shoud contain the "user:pass@host:port"
+     * sub-components without any leading "//" separator characters.
+     *
+     * \param authority The encoded authority component string.
+     * \throws url::UrlNotAllowedException if the \p authority
+     *         has to be empty in for the current scheme.
+     * \throws url::UrlBadComponentException if the \p authority
+     *         contains an invalid character.
+     * \throws url::UrlParsingException if \p authority parsing fails.
+     */
+    void
+    setAuthority(const std::string &authority);
+
+    /**
+     * \brief Set the username in the URL authority.
+     * \param user  The new username.
+     * \param eflag If the \p username is encoded or not.
+     * \throws url::UrlNotAllowedException if the \p user
+     *         has to be empty in for the current scheme
+     * \throws url::UrlBadComponentException if the \p user
+     *         contains an invalid character.
+     */
+    void
+    setUsername(const std::string &user,
+                EEncoding         eflag = zypp::url::E_DECODED);
+
+    /**
+     * \brief Set the password in the URL authority.
+     * \param pass  The new password.
+     * \param eflag If the \p password is encoded or not.
+     * \throws url::UrlNotAllowedException if the \p pass
+     *         has to be empty in for the current scheme.
+     * \throws url::UrlBadComponentException if the \p pass
+     *         contains an invalid character.
+     */
+    void
+    setPassword(const std::string &pass,
+                EEncoding         eflag = zypp::url::E_DECODED);
+
+    /**
+     * \brief Set the hostname or IP in the URL authority.
+     *
+     * The \p host parameter may contain a hostname, an IPv4 address
+     * in dotted-decimal form or an IPv6 address literal encapsulated
+     * within square brackets (RFC3513, Sect. 2.2).
+     *
+     * A hostname may contain national alphanumeric UTF8 characters
+     * (letters other than ASCII a-z0-9), that will be encoded.
+     * This function allows to specify both, a encoded or decoded
+     * hostname.
+     *
+     * Other IP literals in "[v ... ]" square bracket format are not
+     * supported by the implementation in UrlBase class.
+     *
+     * \param host The new hostname or IP address.
+     * \throws url::UrlNotAllowedException if the \p host (authority)
+     *         has to be empty in for the current scheme.
+     * \throws url::UrlBadComponentException if the \p host is invalid.
+     */
+    void
+    setHost(const std::string &host);
+
+    /**
+     * \brief Set the port number in the URL authority.
+     * \param port The new port number.
+     * \throws url::UrlNotAllowedException if the \p port (authority)
+     *         has to be empty in for the current scheme.
+     * \throws url::UrlBadComponentException if the \p port is invalid.
+     */
+    void
+    setPort(const std::string &port);
+
+
+    // -----------------
+    /**
+     * \brief Set the path data component in the URL.
+     *
+     * By default, the \p pathdata string may include path
+     * parameters separated by the ";" separator character.
+     *
+     * \param pathdata The encoded path data component string.
+     * \throws url::UrlBadComponentException if the \p pathdata
+     *         contains an invalid character.
+     */
+    void
+    setPathData(const std::string &pathdata);
+
+    /**
+     * \brief Set the path name.
+     * \param path  The new path name.
+     * \param eflag If the \p path name is encoded or not.
+     * \throws url::UrlBadComponentException if the \p path name
+     *         contains an invalid character.
+     */
+    void
+    setPathName(const std::string &path,
+                EEncoding         eflag = zypp::url::E_DECODED);
+
+    /**
+     * \brief Set the path parameters.
+     * \param params The new encoded path parameter string.
+     * \throws url::UrlBadComponentException if the path \p params
+     *         contains an invalid character.
+     */
+    void
+    setPathParams(const std::string &params);
+
+    /**
+     * \brief Set the path parameters.
+     * \param pvec The vector with encoded path parameters.
+     * \throws url::UrlBadComponentException if the \p pvec
+     *         contains an invalid character.
+     */
+    void
+    setPathParamsVec(const zypp::url::ParamVec &pvec);
+
+    /**
+     * \brief Set the path parameters.
+     * \param pmap The map with decoded path parameters.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     */
+    void
+    setPathParamsMap(const zypp::url::ParamMap &pmap);
+
+    /**
+     * \brief Set or add value for the specified path parameter.
+     * \param param The decoded path parameter name.
+     * \param value The decoded path parameter value.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    void
+    setPathParam(const std::string &param, const std::string &value);
+
+
+    // -----------------
+    /**
+     * \brief Set the query string in the URL.
+     * \param querystr The new encoded query string.
+     * \throws url::UrlBadComponentException if the \p querystr
+     *         contains an invalid character.
+     */
+    void
+    setQueryString(const std::string &querystr);
+
+    /**
+     * \brief Set the query parameters.
+     * \param qvec The vector with encoded query parameters.
+     * \throws url::UrlBadComponentException if the \p qvec
+     *         contains an invalid character.
+     */
+    void
+    setQueryStringVec(const zypp::url::ParamVec &qvec);
+
+    /**
+     * \brief Set the query parameters.
+     * \param qmap The map with decoded query parameters.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     */
+    void
+    setQueryStringMap(const zypp::url::ParamMap &qmap);
+
+    /**
+     * \brief Set or add value for the specified query parameter.
+     * \param param The decoded query parameter name.
+     * \param value The decoded query parameter value.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    void
+    setQueryParam(const std::string &param, const std::string &value);
+
+    /**
+     * \brief remove the specified query parameter.
+     * \param param The decoded query parameter name.
+     * \throws url::UrlNotSupportedException if parameter parsing
+     *         is not supported for a URL (scheme).
+     * \throws url::UrlDecodingException if the decoded result string
+     *         would contain a '\\0' character.
+     */
+    void
+    delQueryParam(const std::string &param);
+
+
+    // -----------------
+    /**
+     * \brief Set the fragment string in the URL.
+     * \param fragment The new fragment string.
+     * \param eflag If the \p fragment is encoded or not.
+     * \throws url::UrlBadComponentException if the \p fragment
+     *         contains an invalid character.
+     */
+    void
+    setFragment(const std::string &fragment,
+                EEncoding         eflag = zypp::url::E_DECODED);
+
+
+    // -----------------
+    /**
+     * Return the view options of the current object.
+     *
+     * This method is used to query the view options
+     * used by the asString() method.
+     *
+     * \return The current view option combination.
+     */
+    ViewOptions
+    getViewOptions() const;
+
+    /**
+     * Change the view options of the current object.
+     *
+     * This method is used to change the view options
+     * used by the asString() method.
+     *
+     * \param vopts New view options combination.
+     */
+    void
+    setViewOptions(const ViewOptions &vopts);
+
+  private:
+    url::UrlRef m_impl;
+  };
+
+  std::ostream & operator<<( std::ostream & str, const Url & url );
+
+  /**
+   * needed for std::set
+   */
+  bool operator<( const Url &lhs, const Url &rhs );
+
+  /**
+   * needed for find
+   */
+  bool operator==( const Url &lhs, const Url &rhs );
+
+
+  bool operator!=( const Url &lhs, const Url &rhs );
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_URL_H */
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/Vendor.h b/zypp/Vendor.h
new file mode 100644 (file)
index 0000000..8de5063
--- /dev/null
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/Vendor.h
+ *
+*/
+#ifndef ZYPP_VENDOR_H
+#define ZYPP_VENDOR_H
+
+#include <string>
+#include "zypp/IdString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  typedef std::string Vendor;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#include "VendorAttr.h"
+
+#endif // ZYPP_VENDOR_H
diff --git a/zypp/VendorAttr.cc b/zypp/VendorAttr.cc
new file mode 100644 (file)
index 0000000..d111b41
--- /dev/null
@@ -0,0 +1,326 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/*
+  File:       VendorAttr.cc
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose: Manage vendor attributes
+
+/-*/
+
+#include <iostream>
+#include <fstream>
+#include <set>
+#include <map>
+#include <vector>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/VendorAttr.h"
+#include "zypp/ZYppFactory.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/PathInfo.h"
+#include "zypp/parser/IniDict.h"
+
+using namespace std;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::VendorAttr"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    typedef map<Vendor,unsigned int> VendorMap;
+    VendorMap _vendorMap;
+    unsigned int vendorGroupCounter;
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+    typedef DefaultIntegral<int,0>                             VendorMatchEntry;
+    typedef std::tr1::unordered_map<IdString, VendorMatchEntry>        VendorMatch;
+    int         _nextId = -1;
+    VendorMatch _vendorMatch;
+
+    /** Reset match cache if global VendorMap was changed. */
+    inline void vendorMatchIdReset()
+    {
+      _nextId = -1;
+      _vendorMatch.clear();
+    }
+
+    /**
+     * Helper mapping vendor string to eqivalence class ID.
+     *
+     * \li Return the vendor strings eqivalence class ID stored in _vendorMatch.
+     * \li If not found, assign and return the eqivalence class ID of the lowercased string.
+     * \li If not found, assign and return a new ID (look into the predefined VendorMap (id>0),
+     *     otherwise create a new ID (<0)).
+     */
+    inline unsigned vendorMatchId( IdString vendor )
+    {
+      VendorMatchEntry & ent( _vendorMatch[vendor] );
+      if ( ! ent )
+      {
+        IdString lcvendor( str::toLower( vendor.asString() ) );
+        VendorMatchEntry & lcent( _vendorMatch[lcvendor] );
+        if ( ! lcent )
+        {
+          unsigned myid = 0;
+          // Compare this entry with the global vendor map.
+          // Reversed to get the longes prefix.
+          for ( VendorMap::reverse_iterator it = _vendorMap.rbegin(); it != _vendorMap.rend(); ++it )
+          {
+            if ( str::hasPrefix( lcvendor.c_str(), it->first ) )
+            {
+              myid = it->second;
+              break; // found
+            }
+          }
+          if ( ! myid )
+          {
+            myid = --_nextId; // get a new class ID
+          }
+          ent = lcent = myid; // remember the new DI
+        }
+        else
+        {
+          ent = lcent; // take the ID from the lowercased vendor string
+        }
+      }
+      return ent;
+    }
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  const VendorAttr & VendorAttr::instance()
+  {
+      static VendorAttr _val;
+      return _val;
+  }
+
+  VendorAttr::VendorAttr ()
+  {
+      vendorGroupCounter = 1;
+      Pathname vendorPath (ZConfig::instance().vendorPath());
+      try
+      {
+         Target_Ptr trg( getZYpp()->target() );
+         if ( trg )
+             vendorPath = trg->root() / vendorPath;
+      }
+      catch ( ... )
+      {
+         // noop: Someone decided to let target() throw if the ptr is NULL ;(
+      }
+
+      // creating entries
+      addVendorDirectory (vendorPath);
+
+      // Checking if suse,opensuse has been defined, else create entries:
+      // - if both are defined we leve them as thay are.
+      // - if only one of them is defined, we add the other to the same group.
+      // - if both are undefined they make up a new group
+      VendorMap::const_iterator suseit( _vendorMap.find("suse") );
+      VendorMap::const_iterator opensuseit( _vendorMap.find("opensuse") );
+      if ( suseit == _vendorMap.end() )
+      {
+        if ( opensuseit == _vendorMap.end() )
+        {
+          // both are undefined
+          _vendorMap["suse"] = _vendorMap["opensuse"] = ++vendorGroupCounter;
+        }
+        else
+        {
+          // add suse to opensuse
+          _vendorMap["suse"] = opensuseit->second;
+        }
+      }
+      else if ( opensuseit == _vendorMap.end() )
+      {
+        // add opensuse to suse
+        _vendorMap["opensuse"] = suseit->second;
+      }
+
+      // Take care 'opensuse build service' gets it's own class.
+      VendorMap::const_iterator obsit( _vendorMap.find("opensuse build service") );
+      if ( obsit == _vendorMap.end() )
+      {
+        _vendorMap["opensuse build service"] = ++vendorGroupCounter;
+      }
+
+
+      MIL << *this << endl;
+  }
+
+  void VendorAttr::_addVendorList( VendorList & vendorList_r ) const
+  {
+    unsigned int nextId = vendorGroupCounter + 1;
+       // convert to lowercase and check if a vendor is already defined
+       // in an existing group.
+
+    for_( it, vendorList_r.begin(), vendorList_r.end() )
+    {
+      *it = str::toLower( *it );
+      if (_vendorMap.find(*it) != _vendorMap.end())
+      {
+        if (nextId != vendorGroupCounter + 1 &&
+            nextId != _vendorMap[*it])
+        {
+                   // We have at least 3 groups which has to be mixed --> mix the third group to the first
+          unsigned int moveID = _vendorMap[*it];
+          for_( itMap, _vendorMap.begin(), _vendorMap.end() )
+          {
+            if (itMap->second == moveID)
+              itMap->second = nextId;
+          }
+        }
+        else
+        {
+          nextId = _vendorMap[*it];
+          WAR << "Vendor " << *it << " is already used in another vendor group. --> mixing these groups" << endl;
+        }
+      }
+    }
+       // add new entries
+    for_( it, vendorList_r.begin(), vendorList_r.end() )
+    {
+      _vendorMap[*it] = nextId;
+    }
+
+    if (nextId == vendorGroupCounter + 1)
+      ++vendorGroupCounter;
+
+    // invalidate any match cache
+    vendorMatchIdReset();
+  }
+
+  bool VendorAttr::addVendorFile( const Pathname & filename ) const
+  {
+      parser::IniDict dict;
+
+      if ( PathInfo(filename).isExist())
+      {
+          InputStream is(filename);
+          dict.read(is);
+      }
+      else
+      {
+          MIL << filename << " not found." << endl;
+          return false;
+      }
+
+      for ( parser::IniDict::section_const_iterator sit = dict.sectionsBegin();
+           sit != dict.sectionsEnd();
+           ++sit )
+      {
+          string section(*sit);
+          //MIL << section << endl;
+          for ( parser::IniDict::entry_const_iterator it = dict.entriesBegin(*sit);
+                it != dict.entriesEnd(*sit);
+                ++it )
+          {
+             string entry(it->first);
+             string value(it->second);
+             if ( section == "main" )
+             {
+                 if ( entry == "vendors" )
+                 {
+                     VendorList vendorlist;
+                     str::split( value, back_inserter(vendorlist), "," );
+                     _addVendorList (vendorlist);
+                     break;
+                 }
+             }
+         }
+      }
+
+      return true;
+  }
+
+  bool VendorAttr::addVendorDirectory( const Pathname & dirname ) const
+  {
+      parser::IniDict dict;
+
+      if ( PathInfo(dirname).isExist())
+      {
+          InputStream is(dirname);
+          dict.read(is);
+      }
+      else
+      {
+          MIL << dirname << " not found." << endl;
+          return false;
+      }
+
+      list<Pathname> filenames;
+
+      filesystem::readdir( filenames,
+                          dirname, false );
+      for (list<Pathname>::iterator it = filenames.begin();
+          it != filenames.end(); ++it) {
+         MIL << "Adding file " << *it << endl;
+         addVendorFile( *it );
+      }
+      return true;
+  }
+
+  //////////////////////////////////////////////////////////////////
+  // vendor equivalence:
+  //////////////////////////////////////////////////////////////////
+
+  bool VendorAttr::equivalent( IdString lVendor, IdString rVendor ) const
+  {
+    if ( lVendor == rVendor )
+      return true;
+    return vendorMatchId( lVendor ) == vendorMatchId( rVendor );
+  }
+
+  bool VendorAttr::equivalent( const Vendor & lVendor, const Vendor & rVendor ) const
+  { return equivalent( IdString( lVendor ), IdString( rVendor ) );
+  }
+
+  bool VendorAttr::equivalent( sat::Solvable lVendor, sat::Solvable rVendor ) const
+  { return equivalent( lVendor.vendor(), rVendor.vendor() ); }
+
+  bool VendorAttr::equivalent( const PoolItem & lVendor, const PoolItem & rVendor ) const
+  { return equivalent( lVendor.satSolvable().vendor(), rVendor.satSolvable().vendor() ); }
+
+  //////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<( std::ostream & str, const VendorAttr & /*obj*/ )
+  {
+    str << "Equivalent vendors:";
+    for_( it, _vendorMap.begin(), _vendorMap.end() )
+    {
+      str << endl << "   [" << it->second << "] " << it->first;
+    }
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/VendorAttr.h b/zypp/VendorAttr.h
new file mode 100644 (file)
index 0000000..22ef3b5
--- /dev/null
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/VendorAttr.h
+ *
+*/
+#ifndef ZYPP_VENDORATTR_H
+#define ZYPP_VENDORATTR_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "zypp/PathInfo.h"
+#include "zypp/Vendor.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp {
+//////////////////////////////////////////////////////////////////
+
+  class PoolItem;
+  namespace sat
+  {
+    class Solvable;
+  }
+
+/** Definition of vendor equivalence.
+ *
+ * Packages with equivalment vendor strings may replace themself without
+ * creating a solver error.
+ *
+ * Per default vendor strings starting with \c "suse' or \c 'opensuse"
+ * are treated equivalent. This may be changed by providing customized
+ * vendor description files in \c /etc/zypp/vendors.d.
+*/
+class VendorAttr
+{
+  public:
+    typedef std::vector<std::string> VendorList;
+
+    /** Singleton */
+    static const VendorAttr & instance();
+
+    /**
+     * Adding new equivalent vendors described in a directory
+     **/
+    bool addVendorDirectory( const Pathname & dirname ) const;
+
+    /**
+     * Adding new equivalent vendors described in a file
+     **/
+    bool addVendorFile( const Pathname & filename ) const;
+
+    /**
+     * Adding new equivalent vendor set from list
+     **/
+    template <class _Iterator>
+    void addVendorList( _Iterator begin, _Iterator end ) const
+    { VendorList tmp( begin, end ); _addVendorList( tmp ); }
+
+    /** Return whether two vendor strings shold be treated as the same vendor.
+     * Usually the solver is allowed to automatically select a package of an
+     * equivalent vendor when updating. Replacing a package with one of a
+     * different vendor usually must be confirmed by the user.
+    */
+    bool equivalent( const Vendor & lVendor, const Vendor & rVendor ) const;
+    /** \overload using \ref IdStrings */
+    bool equivalent( IdString lVendor, IdString rVendor ) const;
+    /** \overload using \ref sat::Solvable */
+    bool equivalent( sat::Solvable lVendor, sat::Solvable rVendor ) const;
+    /** \overload using \ref PoolItem */
+    bool equivalent( const PoolItem & lVendor, const PoolItem & rVendor ) const;
+
+  private:
+    VendorAttr();
+    void _addVendorList( VendorList & ) const;
+};
+
+/** \relates VendorAttr Stream output */
+std::ostream & operator<<( std::ostream & str, const VendorAttr & obj );
+
+///////////////////////////////////////////////////////////////////
+}; // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_VENDORATTR_H
diff --git a/zypp/VendorSupportOptions.cc b/zypp/VendorSupportOptions.cc
new file mode 100644 (file)
index 0000000..8839906
--- /dev/null
@@ -0,0 +1,61 @@
+
+#include "zypp/VendorSupportOptions.h"
+#include "zypp/base/Gettext.h"
+
+namespace zypp
+{
+    
+std::string
+asUserString( VendorSupportOption opt )
+{
+    switch (opt)
+    {
+    case VendorSupportUnknown:
+        return _("unknown");
+        break;
+    case VendorSupportUnsupported:
+        return _("unsupported");
+        break;        
+    case VendorSupportLevel1:
+        return _("Level 1");
+        break;
+    case VendorSupportLevel2:
+        return _("Level 2");
+        break;
+    case VendorSupportLevel3:
+        return _("Level 3");
+        break;
+    case VendorSupportACC:
+        return _("Additional Customer Contract Necessary");
+    }
+    return _("invalid");
+}
+    
+std::string asUserStringDescription( VendorSupportOption opt )
+{
+    switch (opt)
+    {
+    case VendorSupportUnknown:
+        return _("The level of support is unspecified");
+        break;
+    case VendorSupportUnsupported:
+        return _("The vendor does not provide support.");
+        break;        
+    case VendorSupportLevel1:
+        return _("Problem determination, which means technical support designed to provide compatibility information, installation assistance, usage support, on-going maintenance and basic troubleshooting. Level 1 Support is not intended to correct product defect errors.");
+        break;
+    case VendorSupportLevel2:
+        return _("Problem isolation, which means technical support designed to duplicate customer problems, isolate problem area and provide resolution for problems not resolved by Level 1 Support.");
+        break;
+    case VendorSupportLevel3:
+        return _("Problem resolution, which means technical support designed to resolve complex problems by engaging engineering in resolution of product defects which have been identified by Level 2 Support.");
+        break;
+    case VendorSupportACC:
+        return _("An additional customer contract is necessary for getting support.");
+    }
+    return _("Unknown support option. Description not available");
+}
+    
+}
+
+
diff --git a/zypp/VendorSupportOptions.h b/zypp/VendorSupportOptions.h
new file mode 100644 (file)
index 0000000..497ba48
--- /dev/null
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/VendorSupportOptions.h
+ *
+*/
+#ifndef ZYPP_VendorSupportOptions_H
+#define ZYPP_VendorSupportOptions_H
+
+#include <string>
+#include "zypp/base/Flags.h"
+
+namespace zypp
+{
+
+    enum VendorSupportOption
+    {
+      /**
+       * The support for this package is unknown
+       */
+      VendorSupportUnknown     = 0,
+      /**
+       * The package is known to be unsupported by the vendor
+       */
+      VendorSupportUnsupported = (1<<0),
+      /**
+       * Additional Customer Contract necessary
+       */
+      VendorSupportACC         = (1<<1),
+      /**
+       * Problem determination, which means technical support
+       * designed to provide compatibility information,
+       * installation assistance, usage support, on-going maintenance
+       * and basic troubleshooting. Level 1 Support is not intended to
+       * correct product defect errors.
+       *
+       * May have different semantics for different organizations.
+       */
+      VendorSupportLevel1      = (1<<2),
+      /**
+       * Problem isolation, which means technical support designed
+       * to duplicate customer problems, isolate problem area and provide
+       * resolution for problems not resolved by Level 1 Support.
+       *
+       * May have different semantics for different organizations.
+       */
+      VendorSupportLevel2      = (1<<3),
+      /**
+       * Problem resolution, which means technical support designed
+       * to resolve complex problems by engaging engineering in resolution
+       * of product defects which have been identified by Level 2 Support.
+       *
+       * May have different semantics for different organizations.
+       */
+      VendorSupportLevel3      = (1<<4)
+    };
+
+    // Make a flag set for this
+    ZYPP_DECLARE_FLAGS(VendorSupportOptions,VendorSupportOption);
+    ZYPP_DECLARE_OPERATORS_FOR_FLAGS(VendorSupportOptions)
+
+    /**
+     * converts the support option to a name intended to be printed
+     * to the user.
+     *
+     * Note the description is based in the way Novell defines the support
+     * levels, and the semantics may be different for other vendors.
+     */
+    std::string asUserString( VendorSupportOption );
+
+    /**
+     * converts the support option to a description intended to be printed
+     * to the user.
+     *
+     * Note the description is based in the way Novell defines the support
+     * levels, and the semantics may be different for other vendors.
+     */
+    std::string asUserStringDescription( VendorSupportOption );
+
+}
+
+#endif
diff --git a/zypp/ZConfig.cc b/zypp/ZConfig.cc
new file mode 100644 (file)
index 0000000..54ff1f5
--- /dev/null
@@ -0,0 +1,850 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZConfig.cc
+ *
+*/
+extern "C"
+{
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <satsolver/satversion.h>
+}
+#include <iostream>
+#include <fstream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/PathInfo.h"
+#include "zypp/parser/IniDict.h"
+
+#include "zypp/sat/Pool.h"
+
+using namespace std;
+using namespace zypp::filesystem;
+using namespace zypp::parser;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zconfig"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /** Determine system architecture evaluating \c uname and \c /proc/cpuinfo.
+    */
+    Arch _autodetectSystemArchitecture()
+    {
+      struct ::utsname buf;
+      if ( ::uname( &buf ) < 0 )
+      {
+        ERR << "Can't determine system architecture" << endl;
+        return Arch_noarch;
+      }
+
+      Arch architecture( buf.machine );
+      MIL << "Uname architecture is '" << buf.machine << "'" << endl;
+
+      if ( architecture == Arch_i686 )
+      {
+       // some CPUs report i686 but dont implement cx8 and cmov
+       // check for both flags in /proc/cpuinfo and downgrade
+       // to i586 if either is missing (cf bug #18885)
+        std::ifstream cpuinfo( "/proc/cpuinfo" );
+        if ( cpuinfo )
+        {
+          for( iostr::EachLine in( cpuinfo ); in; in.next() )
+          {
+            if ( str::hasPrefix( *in, "flags" ) )
+            {
+              if (    in->find( "cx8" ) == std::string::npos
+                   || in->find( "cmov" ) == std::string::npos )
+              {
+                architecture = Arch_i586;
+                WAR << "CPU lacks 'cx8' or 'cmov': architecture downgraded to '" << architecture << "'" << endl;
+              }
+              break;
+            }
+          }
+        }
+        else
+        {
+          ERR << "Cant open " << PathInfo("/proc/cpuinfo") << endl;
+        }
+      }
+      else if ( architecture == Arch_sparc || architecture == Arch_sparc64 )
+      {
+       // Check for sun4[vum] to get the real arch. (bug #566291)
+       std::ifstream cpuinfo( "/proc/cpuinfo" );
+        if ( cpuinfo )
+        {
+          for( iostr::EachLine in( cpuinfo ); in; in.next() )
+          {
+            if ( str::hasPrefix( *in, "type" ) )
+            {
+              if ( in->find( "sun4v" ) != std::string::npos )
+              {
+                architecture = ( architecture == Arch_sparc64 ? Arch_sparc64v : Arch_sparcv9v );
+                WAR << "CPU has 'sun4v': architecture upgraded to '" << architecture << "'" << endl;
+              }
+              else if ( in->find( "sun4u" ) != std::string::npos )
+              {
+                architecture = ( architecture == Arch_sparc64 ? Arch_sparc64 : Arch_sparcv9 );
+                WAR << "CPU has 'sun4u': architecture upgraded to '" << architecture << "'" << endl;
+              }
+              else if ( in->find( "sun4m" ) != std::string::npos )
+              {
+                architecture = Arch_sparcv8;
+                WAR << "CPU has 'sun4m': architecture upgraded to '" << architecture << "'" << endl;
+              }
+              break;
+            }
+          }
+        }
+        else
+        {
+          ERR << "Cant open " << PathInfo("/proc/cpuinfo") << endl;
+        }
+      }
+      return architecture;
+    }
+
+     /** The locale to be used for texts and messages.
+     *
+     * For the encoding to be used the preference is
+     *
+     *    LC_ALL, LC_CTYPE, LANG
+     *
+     * For the language of the messages to be used, the preference is
+     *
+     *    LANGUAGE, LC_ALL, LC_MESSAGES, LANG
+     *
+     * Note that LANGUAGE can contain more than one locale name, it can be
+     * a list of locale names like for example
+     *
+     *    LANGUAGE=ja_JP.UTF-8:de_DE.UTF-8:fr_FR.UTF-8
+
+     * \todo Support dynamic fallbacklists defined by LANGUAGE
+     */
+    Locale _autodetectTextLocale()
+    {
+      Locale ret( "en" );
+      const char * envlist[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
+      for ( const char ** envvar = envlist; *envvar; ++envvar )
+      {
+        const char * envlang = getenv( *envvar );
+        if ( envlang )
+        {
+          std::string envstr( envlang );
+          if ( envstr != "POSIX" && envstr != "C" )
+          {
+            Locale lang( envstr );
+            if ( ! lang.code().empty() )
+            {
+              MIL << "Found " << *envvar << "=" << envstr << endl;
+              ret = lang;
+              break;
+            }
+          }
+        }
+      }
+      MIL << "Default text locale is '" << ret << "'" << endl;
+#warning HACK AROUND BOOST_TEST_CATCH_SYSTEM_ERRORS
+      setenv( "BOOST_TEST_CATCH_SYSTEM_ERRORS", "no", 1 );
+      return ret;
+    }
+
+   /////////////////////////////////////////////////////////////////
+  } // namespace zypp
+  ///////////////////////////////////////////////////////////////////
+
+  /** Mutable option. */
+  template<class _Tp>
+      struct Option
+      {
+       typedef _Tp value_type;
+
+       /** No default ctor, explicit initialisation! */
+       Option( const value_type & initial_r )
+         : _val( initial_r )
+       {}
+
+       /** Get the value.  */
+       const value_type & get() const
+       { return _val; }
+
+        /** Autoconversion to value_type.  */
+        operator const value_type &() const
+        { return _val; }
+
+       /** Set a new value.  */
+       void set( const value_type & newval_r )
+       { _val = newval_r; }
+
+        /** Non-const reference to set a new value. */
+        value_type & ref()
+        { return _val; }
+
+       private:
+         value_type _val;
+      };
+
+  /** Mutable option with initial value also remembering a config value. */
+  template<class _Tp>
+      struct DefaultOption : public Option<_Tp>
+      {
+       typedef _Tp         value_type;
+       typedef Option<_Tp> option_type;
+
+        DefaultOption( const value_type & initial_r )
+          : Option<_Tp>( initial_r ), _default( initial_r )
+        {}
+
+       /** Reset value to the current default. */
+       void restoreToDefault()
+       { this->set( _default.get() ); }
+
+       /** Reset value to a new default. */
+       void restoreToDefault( const value_type & newval_r )
+       { setDefault( newval_r ); restoreToDefault(); }
+
+       /** Get the current default value. */
+       const value_type & getDefault() const
+       { return _default.get(); }
+
+       /** Set a new default value. */
+       void setDefault( const value_type & newval_r )
+       { _default.set( newval_r ); }
+
+       private:
+         option_type _default;
+      };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZConfig::Impl
+  //
+  /** ZConfig implementation.
+   * \todo Enrich section and entry definition by some comment
+   * (including the default setting and provide some method to
+   * write this into a sample zypp.conf.
+  */
+  class ZConfig::Impl
+  {
+    public:
+      Impl( const Pathname & override_r = Pathname() )
+        : _parsedZyppConf              ( override_r )
+        , cfg_arch                     ( defaultSystemArchitecture() )
+        , cfg_textLocale               ( defaultTextLocale() )
+        , updateMessagesNotify         ( "single | /usr/lib/zypp/notify-message -p %p" )
+        , repo_add_probe               ( false )
+        , repo_refresh_delay           ( 10 )
+        , repoLabelIsAlias              ( false )
+        , download_use_deltarpm        ( true )
+        , download_use_deltarpm_always  ( false )
+        , download_media_prefer_download( true )
+        , download_max_concurrent_connections( 5 )
+        , download_min_download_speed  ( 0 )
+        , download_max_download_speed  ( 0 )
+        , download_max_silent_tries    ( 5 )
+        , commit_downloadMode          ( DownloadDefault )
+        , solver_onlyRequires          ( false )
+        , solver_allowVendorChange     ( false )
+        , solver_cleandepsOnRemove     ( false )
+        , solver_upgradeTestcasesToKeep        ( 2 )
+        , solverUpgradeRemoveDroppedPackages( true )
+        , apply_locks_file             ( true )
+        , pluginsPath                  ( "/usr/lib/zypp/plugins" )
+      {
+        MIL << "libzypp: " << VERSION << " built " << __DATE__ << " " <<  __TIME__ << endl;
+        // override_r has higest prio
+        // ZYPP_CONF might override /etc/zypp/zypp.conf
+        if ( _parsedZyppConf.empty() )
+        {
+          const char *env_confpath = getenv( "ZYPP_CONF" );
+          _parsedZyppConf = env_confpath ? env_confpath : "/etc/zypp/zypp.conf";
+        }
+        else
+        {
+          // Inject this into ZConfig. Be shure this is
+          // allocated via new. See: reconfigureZConfig
+          INT << "Reconfigure to " << _parsedZyppConf << endl;
+          ZConfig::instance()._pimpl.reset( this );
+        }
+        if ( PathInfo(_parsedZyppConf).isExist() )
+        {
+          parser::IniDict dict( _parsedZyppConf );
+          for ( IniDict::section_const_iterator sit = dict.sectionsBegin();
+                sit != dict.sectionsEnd();
+                ++sit )
+          {
+            string section(*sit);
+            //MIL << section << endl;
+            for ( IniDict::entry_const_iterator it = dict.entriesBegin(*sit);
+                  it != dict.entriesEnd(*sit);
+                  ++it )
+            {
+              string entry(it->first);
+              string value(it->second);
+              //DBG << (*it).first << "=" << (*it).second << endl;
+              if ( section == "main" )
+              {
+                if ( entry == "arch" )
+                {
+                  Arch carch( value );
+                  if ( carch != cfg_arch )
+                  {
+                    WAR << "Overriding system architecture (" << cfg_arch << "): " << carch << endl;
+                    cfg_arch = carch;
+                  }
+                }
+                else if ( entry == "cachedir" )
+                {
+                  cfg_cache_path = Pathname(value);
+                }
+                else if ( entry == "metadatadir" )
+                {
+                  cfg_metadata_path = Pathname(value);
+                }
+                else if ( entry == "solvfilesdir" )
+                {
+                  cfg_solvfiles_path = Pathname(value);
+                }
+                else if ( entry == "packagesdir" )
+                {
+                  cfg_packages_path = Pathname(value);
+                }
+                else if ( entry == "configdir" )
+                {
+                  cfg_config_path = Pathname(value);
+                }
+                else if ( entry == "reposdir" )
+                {
+                  cfg_known_repos_path = Pathname(value);
+                }
+                else if ( entry == "servicesdir" )
+                {
+                  cfg_known_services_path = Pathname(value);
+                }
+                else if ( entry == "repo.add.probe" )
+                {
+                  repo_add_probe = str::strToBool( value, repo_add_probe );
+                }
+                else if ( entry == "repo.refresh.delay" )
+                {
+                  str::strtonum(value, repo_refresh_delay);
+                }
+                else if ( entry == "download.use_deltarpm" )
+                {
+                  download_use_deltarpm = str::strToBool( value, download_use_deltarpm );
+                }
+                else if ( entry == "download.use_deltarpm.always" )
+                {
+                  download_use_deltarpm_always = str::strToBool( value, download_use_deltarpm_always );
+                }
+               else if ( entry == "download.media_preference" )
+                {
+                 download_media_prefer_download.restoreToDefault( str::compareCI( value, "volatile" ) != 0 );
+                }
+                else if ( entry == "download.max_concurrent_connections" )
+                {
+                  str::strtonum(value, download_max_concurrent_connections);
+                }
+                else if ( entry == "download.min_download_speed" )
+                {
+                  str::strtonum(value, download_min_download_speed);
+                }
+                else if ( entry == "download.max_download_speed" )
+                {
+                  str::strtonum(value, download_max_download_speed);
+                }
+                else if ( entry == "download.max_silent_tries" )
+                {
+                  str::strtonum(value, download_max_silent_tries);
+                }
+                else if ( entry == "commit.downloadMode" )
+                {
+                  commit_downloadMode.set( deserializeDownloadMode( value ) );
+                }
+                else if ( entry == "vendordir" )
+                {
+                  cfg_vendor_path = Pathname(value);
+                }
+                else if ( entry == "solver.onlyRequires" )
+                {
+                  solver_onlyRequires.set( str::strToBool( value, solver_onlyRequires ) );
+                }
+                else if ( entry == "solver.allowVendorChange" )
+                {
+                  solver_allowVendorChange.set( str::strToBool( value, solver_allowVendorChange ) );
+                }
+                else if ( entry == "solver.cleandepsOnRemove" )
+                {
+                  solver_cleandepsOnRemove.set( str::strToBool( value, solver_cleandepsOnRemove ) );
+                }
+                else if ( entry == "solver.upgradeTestcasesToKeep" )
+                {
+                  solver_upgradeTestcasesToKeep.set( str::strtonum<unsigned>( value ) );
+                }
+                else if ( entry == "solver.upgradeRemoveDroppedPackages" )
+                {
+                  solverUpgradeRemoveDroppedPackages.restoreToDefault( str::strToBool( value, solverUpgradeRemoveDroppedPackages.getDefault() ) );
+                }
+                else if ( entry == "solver.checkSystemFile" )
+                {
+                  solver_checkSystemFile = Pathname(value);
+                }
+                else if ( entry == "multiversion" )
+                {
+                  str::split( value, inserter( multiversion, multiversion.end() ), ", \t" );
+                }
+                else if ( entry == "locksfile.path" )
+                {
+                  locks_file = Pathname(value);
+                }
+                else if ( entry == "locksfile.apply" )
+                {
+                  apply_locks_file = str::strToBool( value, apply_locks_file );
+                }
+                else if ( entry == "update.datadir" )
+                {
+                  update_data_path = Pathname(value);
+                }
+                else if ( entry == "update.scriptsdir" )
+                {
+                  update_scripts_path = Pathname(value);
+                }
+                else if ( entry == "update.messagessdir" )
+                {
+                  update_messages_path = Pathname(value);
+                }
+                else if ( entry == "update.messages.notify" )
+                {
+                  updateMessagesNotify.set( value );
+                }
+                else if ( entry == "rpm.install.excludedocs" )
+                {
+                  rpmInstallFlags.setFlag( target::rpm::RPMINST_EXCLUDEDOCS,
+                                           str::strToBool( value, false ) );
+                }
+                else if ( entry == "history.logfile" )
+                {
+                  history_log_path = Pathname(value);
+                }
+                else if ( entry == "credentials.global.dir" )
+                {
+                  credentials_global_dir_path = Pathname(value);
+                }
+                else if ( entry == "credentials.global.file" )
+                {
+                  credentials_global_file_path = Pathname(value);
+                }
+              }
+            }
+          }
+        }
+        else
+        {
+          MIL << _parsedZyppConf << " not found, using defaults instead." << endl;
+          _parsedZyppConf = _parsedZyppConf.extend( " (NOT FOUND)" );
+        }
+
+        // legacy:
+        if ( getenv( "ZYPP_TESTSUITE_FAKE_ARCH" ) )
+        {
+          Arch carch( getenv( "ZYPP_TESTSUITE_FAKE_ARCH" ) );
+          if ( carch != cfg_arch )
+          {
+            WAR << "ZYPP_TESTSUITE_FAKE_ARCH: Overriding system architecture (" << cfg_arch << "): " << carch << endl;
+            cfg_arch = carch;
+          }
+        }
+        MIL << "ZConfig singleton created." << endl;
+      }
+
+      ~Impl()
+      {}
+
+    public:
+    /** Remember any parsed zypp.conf. */
+    Pathname _parsedZyppConf;
+
+    Arch     cfg_arch;
+    Locale   cfg_textLocale;
+
+    Pathname cfg_cache_path;
+    Pathname cfg_metadata_path;
+    Pathname cfg_solvfiles_path;
+    Pathname cfg_packages_path;
+
+    Pathname cfg_config_path;
+    Pathname cfg_known_repos_path;
+    Pathname cfg_known_services_path;
+
+    Pathname cfg_vendor_path;
+    Pathname locks_file;
+
+    Pathname update_data_path;
+    Pathname update_scripts_path;
+    Pathname update_messages_path;
+    DefaultOption<std::string> updateMessagesNotify;
+
+    bool repo_add_probe;
+    unsigned repo_refresh_delay;
+    bool repoLabelIsAlias;
+
+    bool download_use_deltarpm;
+    bool download_use_deltarpm_always;
+    DefaultOption<bool> download_media_prefer_download;
+
+    int download_max_concurrent_connections;
+    int download_min_download_speed;
+    int download_max_download_speed;
+    int download_max_silent_tries;
+
+    Option<DownloadMode> commit_downloadMode;
+
+    Option<bool>       solver_onlyRequires;
+    Option<bool>       solver_allowVendorChange;
+    Option<bool>       solver_cleandepsOnRemove;
+    Option<unsigned>   solver_upgradeTestcasesToKeep;
+    DefaultOption<bool> solverUpgradeRemoveDroppedPackages;
+
+    Pathname solver_checkSystemFile;
+
+    std::set<std::string> multiversion;
+
+    bool apply_locks_file;
+
+    target::rpm::RpmInstFlags rpmInstallFlags;
+
+    Pathname history_log_path;
+    Pathname credentials_global_dir_path;
+    Pathname credentials_global_file_path;
+
+    Option<Pathname> pluginsPath;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  // Backdoor to redirect ZConfig from within the running
+  // TEST-application. HANDLE WITH CARE!
+  void reconfigureZConfig( const Pathname & override_r )
+  {
+    // ctor puts itself unter smart pointer control.
+    new ZConfig::Impl( override_r );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZConfig::instance
+  //   METHOD TYPE : ZConfig &
+  //
+  ZConfig & ZConfig::instance()
+  {
+    static ZConfig _instance; // The singleton
+    return _instance;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZConfig::ZConfig
+  //   METHOD TYPE : Ctor
+  //
+  ZConfig::ZConfig()
+  : _pimpl( new Impl )
+  {
+    about( MIL );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZConfig::~ZConfig
+  //   METHOD TYPE : Dtor
+  //
+  ZConfig::~ZConfig( )
+  {}
+
+  Pathname ZConfig::systemRoot() const
+  {
+    Target_Ptr target( getZYpp()->getTarget() );
+    return target ? target->root() : Pathname();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // system architecture
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  Arch ZConfig::defaultSystemArchitecture()
+  {
+    static Arch _val( _autodetectSystemArchitecture() );
+    return _val;
+  }
+
+  Arch ZConfig::systemArchitecture() const
+  { return _pimpl->cfg_arch; }
+
+  void ZConfig::setSystemArchitecture( const Arch & arch_r )
+  {
+    if ( arch_r != _pimpl->cfg_arch )
+    {
+      WAR << "Overriding system architecture (" << _pimpl->cfg_arch << "): " << arch_r << endl;
+      _pimpl->cfg_arch = arch_r;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // text locale
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  Locale ZConfig::defaultTextLocale()
+  {
+    static Locale _val( _autodetectTextLocale() );
+    return _val;
+  }
+
+  Locale ZConfig::textLocale() const
+  { return _pimpl->cfg_textLocale; }
+
+  void ZConfig::setTextLocale( const Locale & locale_r )
+  {
+    if ( locale_r != _pimpl->cfg_textLocale )
+    {
+      WAR << "Overriding text locale (" << _pimpl->cfg_textLocale << "): " << locale_r << endl;
+      _pimpl->cfg_textLocale = locale_r;
+#warning prefer signal
+      sat::Pool::instance().setTextLocale( locale_r );
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  Pathname ZConfig::repoCachePath() const
+  {
+    return ( _pimpl->cfg_cache_path.empty()
+        ? Pathname("/var/cache/zypp") : _pimpl->cfg_cache_path );
+  }
+
+  Pathname ZConfig::repoMetadataPath() const
+  {
+    return ( _pimpl->cfg_metadata_path.empty()
+        ? (repoCachePath()/"raw") : _pimpl->cfg_metadata_path );
+  }
+
+  Pathname ZConfig::repoSolvfilesPath() const
+  {
+    return ( _pimpl->cfg_solvfiles_path.empty()
+        ? (repoCachePath()/"solv") : _pimpl->cfg_solvfiles_path );
+  }
+
+  Pathname ZConfig::repoPackagesPath() const
+  {
+    return ( _pimpl->cfg_packages_path.empty()
+        ? (repoCachePath()/"packages") : _pimpl->cfg_packages_path );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  Pathname ZConfig::configPath() const
+  {
+    return ( _pimpl->cfg_config_path.empty()
+        ? Pathname("/etc/zypp") : _pimpl->cfg_config_path );
+  }
+
+  Pathname ZConfig::knownReposPath() const
+  {
+    return ( _pimpl->cfg_known_repos_path.empty()
+        ? (configPath()/"repos.d") : _pimpl->cfg_known_repos_path );
+  }
+
+  Pathname ZConfig::knownServicesPath() const
+  {
+    return ( _pimpl->cfg_known_services_path.empty()
+        ? (configPath()/"services.d") : _pimpl->cfg_known_repos_path );
+  }
+
+  Pathname ZConfig::vendorPath() const
+  {
+    return ( _pimpl->cfg_vendor_path.empty()
+        ? (configPath()/"vendors.d") : _pimpl->cfg_vendor_path );
+  }
+
+  Pathname ZConfig::locksFile() const
+  {
+    return ( _pimpl->locks_file.empty()
+        ? (configPath()/"locks") : _pimpl->locks_file );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  bool ZConfig::repo_add_probe() const
+  {
+    return _pimpl->repo_add_probe;
+  }
+
+  unsigned ZConfig::repo_refresh_delay() const
+  {
+    return _pimpl->repo_refresh_delay;
+  }
+
+  bool ZConfig::repoLabelIsAlias() const
+  { return _pimpl->repoLabelIsAlias; }
+
+  void ZConfig::repoLabelIsAlias( bool yesno_r )
+  { _pimpl->repoLabelIsAlias = yesno_r; }
+
+  bool ZConfig::download_use_deltarpm() const
+  { return _pimpl->download_use_deltarpm; }
+
+  bool ZConfig::download_use_deltarpm_always() const
+  { return download_use_deltarpm() && _pimpl->download_use_deltarpm_always; }
+
+  bool ZConfig::download_media_prefer_download() const
+  { return _pimpl->download_media_prefer_download; }
+
+  void ZConfig::set_download_media_prefer_download( bool yesno_r )
+  { _pimpl->download_media_prefer_download.set( yesno_r ); }
+
+  void ZConfig::set_default_download_media_prefer_download()
+  { _pimpl->download_media_prefer_download.restoreToDefault(); }
+
+  long ZConfig::download_max_concurrent_connections() const
+  { return _pimpl->download_max_concurrent_connections; }
+
+  long ZConfig::download_min_download_speed() const
+  { return _pimpl->download_min_download_speed; }
+
+  long ZConfig::download_max_download_speed() const
+  { return _pimpl->download_max_download_speed; }
+
+  long ZConfig::download_max_silent_tries() const
+  { return _pimpl->download_max_silent_tries; }
+
+  DownloadMode ZConfig::commit_downloadMode() const
+  { return _pimpl->commit_downloadMode; }
+
+  bool ZConfig::solver_onlyRequires() const
+  { return _pimpl->solver_onlyRequires; }
+
+  bool ZConfig::solver_allowVendorChange() const
+  { return _pimpl->solver_allowVendorChange; }
+
+  bool ZConfig::solver_cleandepsOnRemove() const
+  { return _pimpl->solver_cleandepsOnRemove; }
+
+  Pathname ZConfig::solver_checkSystemFile() const
+  { return ( _pimpl->solver_checkSystemFile.empty()
+      ? (configPath()/"systemCheck") : _pimpl->solver_checkSystemFile ); }
+
+  unsigned ZConfig::solver_upgradeTestcasesToKeep() const
+  { return _pimpl->solver_upgradeTestcasesToKeep; }
+
+  bool ZConfig::solverUpgradeRemoveDroppedPackages() const             { return _pimpl->solverUpgradeRemoveDroppedPackages; }
+  void ZConfig::setSolverUpgradeRemoveDroppedPackages( bool val_r )    { _pimpl->solverUpgradeRemoveDroppedPackages.set( val_r ); }
+  void ZConfig::resetSolverUpgradeRemoveDroppedPackages()              { _pimpl->solverUpgradeRemoveDroppedPackages.restoreToDefault(); }
+
+  const std::set<std::string> & ZConfig::multiversionSpec() const      { return _pimpl->multiversion; }
+  void ZConfig::addMultiversionSpec( const std::string & name_r )      { _pimpl->multiversion.insert( name_r ); }
+  void ZConfig::removeMultiversionSpec( const std::string & name_r )   { _pimpl->multiversion.erase( name_r ); }
+
+  bool ZConfig::apply_locks_file() const
+  { return _pimpl->apply_locks_file; }
+
+  Pathname ZConfig::update_dataPath() const
+  {
+    return ( _pimpl->update_data_path.empty()
+        ? Pathname("/var/adm") : _pimpl->update_data_path );
+  }
+
+  Pathname ZConfig::update_messagesPath() const
+  {
+    return ( _pimpl->update_messages_path.empty()
+             ? Pathname(update_dataPath()/"update-messages") : _pimpl->update_messages_path );
+  }
+
+  Pathname ZConfig::update_scriptsPath() const
+  {
+    return ( _pimpl->update_scripts_path.empty()
+             ? Pathname(update_dataPath()/"update-scripts") : _pimpl->update_scripts_path );
+  }
+
+  std::string ZConfig::updateMessagesNotify() const
+  { return _pimpl->updateMessagesNotify; }
+
+  void ZConfig::setUpdateMessagesNotify( const std::string & val_r )
+  { _pimpl->updateMessagesNotify.set( val_r ); }
+
+  void ZConfig::resetUpdateMessagesNotify()
+  { _pimpl->updateMessagesNotify.restoreToDefault(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  target::rpm::RpmInstFlags ZConfig::rpmInstallFlags() const
+  { return _pimpl->rpmInstallFlags; }
+
+
+  Pathname ZConfig::historyLogFile() const
+  {
+    return ( _pimpl->history_log_path.empty() ?
+        Pathname("/var/log/zypp/history") : _pimpl->history_log_path );
+  }
+
+
+  Pathname ZConfig::credentialsGlobalDir() const
+  {
+    return ( _pimpl->credentials_global_dir_path.empty() ?
+        Pathname("/etc/zypp/credentials.d") : _pimpl->credentials_global_dir_path );
+  }
+
+  Pathname ZConfig::credentialsGlobalFile() const
+  {
+    return ( _pimpl->credentials_global_file_path.empty() ?
+        Pathname("/etc/zypp/credentials.cat") : _pimpl->credentials_global_file_path );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::string ZConfig::distroverpkg() const
+  { return "redhat-release"; }
+
+  ///////////////////////////////////////////////////////////////////
+
+  Pathname ZConfig::pluginsPath() const
+  { return _pimpl->pluginsPath.get(); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::ostream & ZConfig::about( std::ostream & str ) const
+  {
+    str << "libzypp: " << VERSION << " built " << __DATE__ << " " <<  __TIME__ << endl;
+
+    str << "satsolver: " << sat_version;
+    if ( ::strcmp( sat_version, SATSOLVER_VERSION_STRING ) )
+      str << " (built against " << SATSOLVER_VERSION_STRING << ")";
+    str << endl;
+
+    str << "zypp.conf: '" << _pimpl->_parsedZyppConf << "'" << endl;
+    str << "TextLocale: '" << textLocale() << "' (" << defaultTextLocale() << ")" << endl;
+    str << "SystemArchitecture: '" << systemArchitecture() << "' (" << defaultSystemArchitecture() << ")" << endl;
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ZConfig.h b/zypp/ZConfig.h
new file mode 100644 (file)
index 0000000..2d6345b
--- /dev/null
@@ -0,0 +1,403 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZConfig.h
+ *
+*/
+#ifndef ZYPP_ZCONFIG_H
+#define ZYPP_ZCONFIG_H
+
+#include <iosfwd>
+#include <set>
+#include <string>
+
+#include "zypp/base/Deprecated.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Arch.h"
+#include "zypp/Locale.h"
+#include "zypp/Pathname.h"
+#include "zypp/IdString.h"
+
+#include "zypp/DownloadMode.h"
+#include "zypp/target/rpm/RpmFlags.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZConfig
+  //
+  /** Interim helper class to collect global options and settings.
+   * Use it to avoid hardcoded values and calls to getZYpp() just
+   * to retrieve some value like architecture, languages or tmppath.
+   *
+   * It reads /etc/zypp/zypp.conf, the filename can be overridden by
+   * setting the ZYPP_CONF environment variable to a different file.
+   *
+   * Note, if you add settings to this file, please follow the following
+   * convention:
+   *
+   * namespace.settingname
+   *
+   * should become
+   *
+   * namespace_settingName()
+   *
+   * \ingroup Singleton
+  */
+  class ZConfig : private base::NonCopyable
+  {
+    public:
+
+      /** Singleton ctor */
+      static ZConfig & instance();
+
+      /** Print some detail about the current libzypp version.*/
+      std::ostream & about( std::ostream & str ) const;
+
+    public:
+
+      /** The target root directory.
+       * Returns an empty path if no target is set.
+       */
+      Pathname systemRoot() const;
+
+    public:
+
+      /** The autodetected system architecture. */
+      static Arch defaultSystemArchitecture();
+
+      /** The system architecture zypp uses. */
+      Arch systemArchitecture() const;
+
+      /** Override the zypp system architecture.
+       * This is useful for test scenarious. <b>But be warned</b>, zypp does
+       * not expect the system architecture to change at runtime. So
+       * set it at the verry beginning before you acess any other
+       * zypp component.
+      */
+      void setSystemArchitecture( const Arch & arch_r );
+
+      /** Reset the zypp system architecture to the default. */
+      void resetSystemArchitecture()
+      { setSystemArchitecture( defaultSystemArchitecture() ); }
+
+    public:
+      /** The autodetected prefered locale for translated texts.
+       */
+      static Locale defaultTextLocale();
+
+      /** The locale for translated texts zypp uses.
+       */
+      Locale textLocale() const;
+
+      /** Set the prefered locale for translated texts. */
+      void setTextLocale( const Locale & locale_r );
+
+      /** Reset the locale for translated texts to the default. */
+      void resetTextLocale()
+      { setTextLocale( defaultTextLocale() ); }
+
+    public:
+      /**
+       * Path where the caches are kept (/var/cache/zypp)
+       * \ingroup g_ZC_REPOCACHE
+       */
+      Pathname repoCachePath() const;
+
+     /**
+       * Path where the repo metadata is downloaded and kept (repoCachePath()/raw).
+        * \ingroup g_ZC_REPOCACHE
+      */
+      Pathname repoMetadataPath() const;
+
+     /**
+       * Path where the repo solv files are created and kept (repoCachePath()/solv).
+        * \ingroup g_ZC_REPOCACHE
+      */
+      Pathname repoSolvfilesPath() const;
+
+      /**
+       * Path where the repo packages are downloaded and kept (repoCachePath()/packages).
+        * \ingroup g_ZC_REPOCACHE
+      */
+      Pathname repoPackagesPath() const;
+
+      /**
+       * Path where the configfiles are kept (/etc/zypp).
+       * \ingroup g_ZC_CONFIGFILES
+       */
+      Pathname configPath() const;
+
+      /**
+       * Path where the known repositories .repo files are kept (configPath()/repos.d).
+       * \ingroup g_ZC_CONFIGFILES
+       */
+      Pathname knownReposPath() const;
+
+      /**
+       * Path where the known services .service files are kept (configPath()/services.d).
+       * \ingroup g_ZC_CONFIGFILES
+       */
+      Pathname knownServicesPath() const;
+
+      /**
+       * Whether repository urls should be probed.
+       / config option
+       * repo.add.probe
+       */
+      bool repo_add_probe() const;
+
+      /**
+       * Amount of time in minutes that must pass before another refresh.
+       */
+      unsigned repo_refresh_delay() const;
+
+      /**
+       * Whether to use repository alias or name in user messages (progress,
+       * exceptions, ...).
+       * True: use alias, false: use name.
+       */
+      bool repoLabelIsAlias() const;
+
+      /**
+       * Whether to use repository alias or name in user messages (progress,
+       * exceptions, ...). Console applications might prefer to use and display
+       * the shorter alias instead of full repository name.
+       *
+       * Default: false; i.e. repo label is 'name'
+       */
+      void repoLabelIsAlias( bool yesno_r );
+
+      /**
+       * Maximum number of concurrent connections for a single transfer
+       */
+      long download_max_concurrent_connections() const;
+
+      /**
+       * Minimum download speed (bytes per second)
+       * until the connection is dropped
+       */
+      long download_min_download_speed() const;
+
+      /**
+       * Maximum download speed (bytes per second)
+       */
+      long download_max_download_speed() const;
+
+      /**
+       * Maximum silent tries
+       */
+      long download_max_silent_tries() const;
+
+
+      /** Whether to consider using a deltarpm when downloading a package.
+       * Config option <tt>download.use_deltarpm (true)</tt>
+       */
+      bool download_use_deltarpm() const;
+
+      /** Whether to consider using a deltarpm even when rpm is local.
+       * This requires \ref download_use_deltarpm being \c true.
+       * Config option <tt>download.use_deltarpm.always (false)</tt>
+       */
+      bool download_use_deltarpm_always() const;
+
+      /**
+       * Hint which media to prefer when installing packages (download vs. CD).
+       * \see class \ref media::MediaPriority
+       */
+      bool download_media_prefer_download() const;
+      /** \overload */
+      bool download_media_prefer_volatile() const
+      { return ! download_media_prefer_download(); }
+      /**
+       * Set \ref download_media_prefer_download to a specific value.
+       */
+      void set_download_media_prefer_download( bool yesno_r );
+      /**
+       * Set \ref download_media_prefer_download to the configfiles default.
+       */
+      void set_default_download_media_prefer_download();
+
+      /**
+       * Commit download policy to use as default.
+       */
+      DownloadMode commit_downloadMode() const;
+
+      /**
+       * Directory for equivalent vendor definitions  (configPath()/vendors.d)
+       * \ingroup g_ZC_CONFIGFILES
+       */
+      Pathname vendorPath() const;
+
+      /**
+       * Solver regards required packages,patterns,... only
+       */
+      bool solver_onlyRequires() const;
+
+      /**
+       * File in which dependencies described which has to be
+       * fulfilled for a running system.
+       */
+      Pathname solver_checkSystemFile() const;
+
+      /**
+       * Whether vendor check is by default enabled.
+       */
+      bool solver_allowVendorChange() const;
+
+      /**
+       * Whether removing a package should also remove no longer needed requirements.
+       */
+      bool solver_cleandepsOnRemove() const;
+
+      /**
+       * When committing a dist upgrade (e.g. <tt>zypper dup</tt>)
+       * a solver testcase is written. It is needed in bugreports,
+       * in case something went wrong. This returns the number of
+       * testcases to keep on the system. Old cases will be deleted,
+       * as new ones are created. Use \c 0 to write no testcase at all.
+       */
+      unsigned solver_upgradeTestcasesToKeep() const;
+
+      /** Whether dist upgrade should remove a products dropped packages (true).
+       *
+       * A new product may suggest a list of old and no longer supported
+       * packages (dropped packages). Performing a dist upgrade the solver
+       * may try to delete them, even if they do not cause any dependency
+       * problem.
+       *
+       * Turning this option off, the solver will not try to remove those
+       * packages unless they actually do cause dependency trouble. At any
+       * time you may use zypper to detect orphaned packages, and do the
+       * cleanup manually. Or simply leave them installed as long as you don't
+       * need the disk space.
+       */
+      bool solverUpgradeRemoveDroppedPackages() const;
+      /** Set \ref solverUpgradeRemoveDroppedPackages to \a val_r. */
+      void setSolverUpgradeRemoveDroppedPackages( bool val_r );
+      /** Reset \ref solverUpgradeRemoveDroppedPackages to the \c zypp.conf default. */
+      void resetSolverUpgradeRemoveDroppedPackages();
+
+      /** \name Packages which can be installed in different versions at the same time.
+       * This returns the config file values (\c names or \c provides:...). For the corresponding
+       * packages use e.g \ref sat::Pool::multiversionBegin, or \ref sat::Solbale::multiversionInstall
+       * (\ref ui::Selectable::multiversionInstall).
+       */
+      //@{
+      const std::set<std::string> & multiversionSpec() const;
+      void addMultiversionSpec( const std::string & name_r );
+      void removeMultiversionSpec( const std::string & name_r );
+      //@}
+
+      /**
+       * Path where zypp can find or create lock file (configPath()/locks)
+       * \ingroup g_ZC_CONFIGFILES
+       */
+      Pathname locksFile() const;
+
+      /**
+       * Whether locks file should be read and applied after start (true)
+       */
+      bool apply_locks_file() const;
+
+      /**
+       * Path where the update items are kept (/var/adm)
+       */
+      Pathname update_dataPath() const;
+
+     /**
+      * Path where the repo metadata is downloaded and kept (update_dataPath()/).
+      * \ingroup g_ZC_REPOCACHE
+      */
+      Pathname update_scriptsPath() const;
+
+     /**
+      * Path where the repo solv files are created and kept (update_dataPath()/solv).
+      * \ingroup g_ZC_REPOCACHE
+      */
+      Pathname update_messagesPath() const;
+
+      /** \name Command to be invoked to send update messages. */
+      //@{
+      /** Command definition for sending update messages.*/
+      std::string updateMessagesNotify() const;
+      /** Set a new command definition (see update.messages.notify in zypp.conf). */
+      void setUpdateMessagesNotify( const std::string & val_r );
+      /** Reset to the zypp.conf default. */
+      void resetUpdateMessagesNotify();
+     //@}
+
+      /** \name Options for package installation */
+      //@{
+      /** The default \ref target::rpm::RpmInstFlags for \ref ZYppCommitPolicy.
+       * Or-combination of \ref target::rpm::RpmInstFlag.
+       * \code
+       * ZConfig.instance().rpmInstallFlags().testFlag( target::rpm::RPMINST_EXCLUDEDOCS );
+       * \endcode
+       */
+      target::rpm::RpmInstFlags rpmInstallFlags() const;
+      //@}
+
+      /**
+       * Path where ZYpp install history is logged. Defaults to
+       * /var/log/zypp/history.
+       *
+       * \see http://en.opensuse.org/Libzypp/Package_History
+       */
+      Pathname historyLogFile() const;
+
+      /**
+       * Defaults to /etc/zypp/credentials.d
+       */
+      Pathname credentialsGlobalDir() const;
+
+      /**
+       * Defaults to /etc/zypp/credentials.cat
+       */
+      Pathname credentialsGlobalFile() const;
+
+      /** Package telling the "product version" on systems not using /etc/product.d/baseproduct.
+       *
+       * On RHEL, Fedora and others the "product version" is determined by the first package
+       * providing 'redhat-release'. This value is not hardcoded in YUM and can be configured
+       * with the $distroverpkg variable.
+       *
+       * Defaults to 'redhat-release'.
+       */
+      std::string distroverpkg() const;
+
+      /** \name Plugins */
+      //@{
+      /**
+       * Defaults to \c /usr/lib/zypp/plugins
+       */
+      Pathname pluginsPath() const;
+
+      //@}
+    public:
+      class Impl;
+      /** Dtor */
+      ~ZConfig();
+    private:
+      friend class Impl;
+      /** Default ctor. */
+      ZConfig();
+      /** Pointer to implementation */
+      RW_pointer<Impl, rw_pointer::Scoped<Impl> > _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZCONFIG_H
diff --git a/zypp/ZYpp.cc b/zypp/ZYpp.cc
new file mode 100644 (file)
index 0000000..08900fb
--- /dev/null
@@ -0,0 +1,126 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYpp.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/ZYpp.h"
+#include "zypp/zypp_detail/ZYppImpl.h"
+#include "zypp/sat/Pool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYpp::ZYpp
+  //   METHOD TYPE : Ctor
+  //
+  ZYpp::ZYpp( const Impl_Ptr & impl_r )
+  : _pimpl( impl_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYpp::~ZYpp
+  //   METHOD TYPE : Dtor
+  //
+  ZYpp::~ZYpp()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYpp::dumpOn
+  //   METHOD TYPE : std::ostream &
+  //
+  std::ostream & ZYpp::dumpOn( std::ostream & str ) const
+  {
+    return str << *_pimpl;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to Impl:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ResPool ZYpp::pool() const
+  { return _pimpl->pool(); }
+
+  DiskUsageCounter::MountPointSet ZYpp::diskUsage()
+  { return _pimpl->diskUsage(); }
+
+  void ZYpp::setPartitions(const DiskUsageCounter::MountPointSet &mp)
+  { return _pimpl->setPartitions(mp); }
+
+  DiskUsageCounter::MountPointSet ZYpp::getPartitions() const
+  { return _pimpl->getPartitions(); }
+
+  ResPoolProxy ZYpp::poolProxy() const
+  { return _pimpl->poolProxy(); }
+
+  Resolver_Ptr ZYpp::resolver() const
+  { return _pimpl->resolver(); }
+
+  KeyRing_Ptr ZYpp::keyRing() const
+  { return _pimpl->keyRing(); }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Forward to Impl:
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  Target_Ptr ZYpp::target() const
+  { return _pimpl->target(); }
+
+  Target_Ptr ZYpp::getTarget() const
+  { return _pimpl->getTarget(); }
+
+  void ZYpp::initializeTarget( const Pathname & root, bool doRebuild_r )
+  { _pimpl->initializeTarget( root, doRebuild_r ); }
+
+  void ZYpp::finishTarget()
+  { _pimpl->finishTarget(); }
+
+  ZYppCommitResult ZYpp::commit( const ZYppCommitPolicy & policy_r )
+  { return _pimpl->commit( policy_r ); }
+
+  void ZYpp::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
+  { _pimpl->installSrcPackage( srcPackage_r ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  void ZYpp::setRequestedLocales( const LocaleSet & locales_r )
+  { sat::Pool::instance().setRequestedLocales( locales_r ); }
+
+  const LocaleSet & ZYpp::getRequestedLocales() const
+  { return sat::Pool::instance().getRequestedLocales(); }
+
+  const LocaleSet & ZYpp::getAvailableLocales() const
+  { return sat::Pool::instance().getAvailableLocales(); }
+
+
+  Pathname ZYpp::homePath() const
+  { return _pimpl->homePath(); }
+
+  Pathname ZYpp::tmpPath() const
+  { return _pimpl->tmpPath(); }
+
+  void ZYpp::setHomePath( const Pathname & path )
+  { _pimpl->setHomePath(path); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ZYpp.h b/zypp/ZYpp.h
new file mode 100644 (file)
index 0000000..9452972
--- /dev/null
@@ -0,0 +1,218 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYpp.h
+ *
+*/
+#ifndef ZYPP_ZYPP_H
+#define ZYPP_ZYPP_H
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/ZConfig.h"
+
+#include "zypp/ZYppCommit.h"
+#include "zypp/ResTraits.h"
+
+#include "zypp/Target.h"
+#include "zypp/Resolver.h"
+#include "zypp/KeyRing.h"
+#include "zypp/DiskUsageCounter.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace zypp_detail
+  {
+    class ZYppImpl;
+  }
+
+  class ZYppFactory;
+  class ResPool;
+  class ResPoolProxy;
+  class KeyRing;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYpp
+  //
+  /**
+   * \todo define Exceptions
+   * ZYpp API main interface
+   */
+  class ZYpp : public base::ReferenceCounted, private base::NonCopyable
+  {
+  public:
+
+    typedef intrusive_ptr<ZYpp>       Ptr;
+    typedef intrusive_ptr<const ZYpp> constPtr;
+
+  public:
+
+    /**
+     * Access to the global resolvable pool.
+     * Same as \ref zypp::ResPool::instance
+     */
+    ResPool pool() const;
+
+    /** Pool of ui::Selectable.
+     * Based on the ResPool, ui::Selectable groups ResObjetcs of
+     * same kind and name.
+    */
+    ResPoolProxy poolProxy() const;
+
+    DiskUsageCounter::MountPointSet diskUsage();
+
+    void setPartitions(const DiskUsageCounter::MountPointSet &mp);
+
+    DiskUsageCounter::MountPointSet getPartitions() const;
+
+  public:
+    /**
+     * \throws Exception
+     */
+    Target_Ptr target() const;
+
+    /** Same as \ref target but returns NULL if target is not
+     *  initialized, instead of throwing.
+     */
+    Target_Ptr getTarget() const;
+
+    /**
+     * \throws Exception
+     * Just init the target, dont populate store or pool.
+     * If \c doRebuild_r is \c true, an already existing
+     * database is rebuilt (rpm --rebuilddb ).
+     */
+    void initializeTarget(const Pathname & root, bool doRebuild_r = false);
+
+    /**
+     * \throws Exception
+     */
+    void finishTarget();
+
+
+  public:
+    typedef ZYppCommitResult CommitResult;
+
+    /** Commit changes and transactions.
+     * \param \ref CommitPolicy
+     * \return \ref CommitResult
+     * \throws Exception
+    */
+    ZYppCommitResult commit( const ZYppCommitPolicy & policy_r );
+
+    /** Install a source package on the Target.
+     * \throws Exception
+     */
+    void installSrcPackage( const SrcPackage_constPtr & srcPackage_r );
+
+  public:
+    /** */
+    Resolver_Ptr resolver() const;
+    KeyRing_Ptr keyRing() const;
+  public:
+    /** Set the preferred locale for translated labels, descriptions,
+     *  etc. passed to the UI.
+     * \deprecated Use ZConfig diretcly.
+     */
+    ZYPP_DEPRECATED void setTextLocale( const Locale & textLocale_r )
+    { ZConfig::instance().setTextLocale( textLocale_r ); }
+    /** \deprecated Use ZConfig diretcly. */
+    ZYPP_DEPRECATED Locale getTextLocale() const
+    { return ZConfig::instance().textLocale(); }
+
+  public:
+    /** \name move to pool
+     * \deprecated Use ResPool diretcly.
+    */
+    //@{
+    /** Set the requested locales.
+     * Languages to be supported by the system, e.g. language specific
+     * packages to be installed. This function operates on the pool,
+     * so only the locales that are available as resolvables
+     * are marked as requested. The rest is ignored.
+     * \deprecated Use ResPool diretcly.
+    */
+    void setRequestedLocales( const LocaleSet & locales_r ) ZYPP_DEPRECATED;
+
+    /** \deprecated Use ResPool diretcly. */
+    const LocaleSet & getRequestedLocales() const ZYPP_DEPRECATED;
+
+    /**
+     * Get the set of available locales.
+     * This is computed from the package data so it actually
+     * represents all locales packages claim to support.
+     * \deprecated Use ResPool diretcly.
+     */
+    const LocaleSet & getAvailableLocales() const ZYPP_DEPRECATED;
+    //@}
+
+  public:
+    /** Get the path where zypp related plugins store persistent data and caches   */
+    Pathname homePath() const;
+
+    /** Get the path where zypp related plugins store temp data   */
+    Pathname tmpPath() const;
+
+    /** set the home, if you need to change it */
+    void setHomePath( const Pathname & path );
+
+    /** Get the system architecture.
+      * \deprecated Use ZConfig diretcly.
+    */
+    ZYPP_DEPRECATED Arch architecture() const
+    { return ZConfig::instance().systemArchitecture(); }
+    /** Set the system architecture.
+     * This should be used for testing/debugging only since the Target backend
+     * won't be able to install incompatible packages ;-)
+     * \deprecated Use ZConfig diretcly.
+    */
+    ZYPP_DEPRECATED void setArchitecture( const Arch & arch )
+    { ZConfig::instance().setSystemArchitecture( arch ); }
+
+  public:
+
+   /**
+    * \deprecated Persistent locks are automatically maintained, kept in the pool, loaded and saved together with the Target.
+    * \ref ZConfig::apply_locks_file tells whether locks are applied or not.
+    */
+    ZYPP_DEPRECATED int applyLocks()
+    { return 0; }
+
+  protected:
+    /** Dtor */
+    virtual ~ZYpp();
+    /** Stream output */
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+  private:
+    /** Factory */
+    friend class ZYppFactory;
+
+    /** */
+    typedef zypp_detail::ZYppImpl Impl;
+    typedef shared_ptr<Impl>      Impl_Ptr;
+    /** Factory ctor */
+    explicit
+    ZYpp( const Impl_Ptr & impl_r );
+  private:
+    /** Pointer to implementation */
+    RW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPP_H
diff --git a/zypp/ZYppCallbacks.h b/zypp/ZYppCallbacks.h
new file mode 100644 (file)
index 0000000..d894717
--- /dev/null
@@ -0,0 +1,720 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ZYppCallbacks.h
+ *
+*/
+#ifndef ZYPP_ZYPPCALLBACKS_H
+#define ZYPP_ZYPPCALLBACKS_H
+
+#include "zypp/Callback.h"
+#include "zypp/Resolvable.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/Pathname.h"
+#include "zypp/Package.h"
+#include "zypp/Patch.h"
+#include "zypp/Url.h"
+#include "zypp/ProgressData.h"
+#include "zypp/media/MediaUserAuth.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  struct ProgressReport : public callback::ReportBase
+  {
+    virtual void start( const ProgressData &/*task*/ )
+    {}
+
+    virtual bool progress( const ProgressData &/*task*/ )
+    { return true; }
+
+//     virtual Action problem(
+//         Repo /*source*/
+//         , Error /*error*/
+//         , const std::string &/*description*/ )
+//     { return ABORT; }
+
+    virtual void finish( const ProgressData &/*task*/ )
+    {}
+
+  };
+
+  struct ProgressReportAdaptor
+  {
+
+    ProgressReportAdaptor( const ProgressData::ReceiverFnc &fnc,
+                           callback::SendReport<ProgressReport> &report )
+      : _fnc(fnc)
+      , _report(report)
+      , _first(true)
+    {
+    }
+
+    bool operator()( const ProgressData &progress )
+    {
+      if ( _first )
+      {
+        _report->start(progress);
+        _first = false;
+      }
+
+      _report->progress(progress);
+      bool value = true;
+      if ( _fnc )
+        value = _fnc(progress);
+
+
+      if ( progress.finalReport() )
+      {
+        _report->finish(progress);
+      }
+      return value;
+    }
+
+    ProgressData::ReceiverFnc _fnc;
+    callback::SendReport<ProgressReport> &_report;
+    bool _first;
+  };
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  namespace repo
+  {
+    // progress for downloading a resolvable
+    struct DownloadResolvableReport : public callback::ReportBase
+    {
+      enum Action {
+        ABORT,  // abort and return error
+        RETRY, // retry
+        IGNORE, // ignore this resolvable but continue
+      };
+
+      enum Error {
+        NO_ERROR,
+        NOT_FOUND,     // the requested Url was not found
+        IO,            // IO error
+        INVALID                // the downloaded file is invalid
+      };
+
+      virtual void start(
+        Resolvable::constPtr /*resolvable_ptr*/
+        , const Url &/*url*/
+      ) {}
+
+
+      // Dowmload delta rpm:
+      // - path below url reported on start()
+      // - expected download size (0 if unknown)
+      // - download is interruptable
+      // - problems are just informal
+      virtual void startDeltaDownload( const Pathname & /*filename*/, const ByteCount & /*downloadsize*/ )
+      {}
+
+      virtual bool progressDeltaDownload( int /*value*/ )
+      { return true; }
+
+      virtual void problemDeltaDownload( const std::string &/*description*/ )
+      {}
+
+      virtual void finishDeltaDownload()
+      {}
+
+      // Apply delta rpm:
+      // - local path of downloaded delta
+      // - aplpy is not interruptable
+      // - problems are just informal
+      virtual void startDeltaApply( const Pathname & /*filename*/ )
+      {}
+
+      virtual void progressDeltaApply( int /*value*/ )
+      {}
+
+      virtual void problemDeltaApply( const std::string &/*description*/ )
+      {}
+
+      virtual void finishDeltaApply()
+      {}
+
+      // Dowmload patch rpm:
+      // - path below url reported on start()
+      // - expected download size (0 if unknown)
+      // - download is interruptable
+      virtual void startPatchDownload( const Pathname & /*filename*/, const ByteCount & /*downloadsize*/ )
+      {}
+
+      virtual bool progressPatchDownload( int /*value*/ )
+      { return true; }
+
+      virtual void problemPatchDownload( const std::string &/*description*/ )
+      {}
+
+      virtual void finishPatchDownload()
+      {}
+
+
+      // return false if the download should be aborted right now
+      virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable_ptr*/)
+      { return true; }
+
+      virtual Action problem(
+        Resolvable::constPtr /*resolvable_ptr*/
+       , Error /*error*/
+       , const std::string &/*description*/
+      ) { return ABORT; }
+
+      virtual void finish(Resolvable::constPtr /*resolvable_ptr*/
+        , Error /*error*/
+        , const std::string &/*reason*/
+      ) {}
+    };
+
+    // progress for probing a source
+    struct ProbeRepoReport : public callback::ReportBase
+    {
+      enum Action {
+        ABORT,  // abort and return error
+        RETRY  // retry
+      };
+
+      enum Error {
+        NO_ERROR,
+        NOT_FOUND,     // the requested Url was not found
+        IO,            // IO error
+        INVALID,               // th source is invalid
+        UNKNOWN
+      };
+
+      virtual void start(const Url &/*url*/) {}
+      virtual void failedProbe( const Url &/*url*/, const std::string &/*type*/ ) {}
+      virtual void successProbe( const Url &/*url*/, const std::string &/*type*/ ) {}
+      virtual void finish(const Url &/*url*/, Error /*error*/, const std::string &/*reason*/ ) {}
+
+      virtual bool progress(const Url &/*url*/, int /*value*/)
+      { return true; }
+
+      virtual Action problem( const Url &/*url*/, Error /*error*/, const std::string &/*description*/ ) { return ABORT; }
+    };
+
+    struct RepoCreateReport : public callback::ReportBase
+    {
+      enum Action {
+        ABORT,  // abort and return error
+        RETRY, // retry
+        IGNORE  // skip refresh, ignore failed refresh
+      };
+
+      enum Error {
+        NO_ERROR,
+        NOT_FOUND,     // the requested Url was not found
+        IO,            // IO error
+        REJECTED,
+        INVALID, // th source is invali
+        UNKNOWN
+      };
+
+      virtual void start( const zypp::Url &/*url*/ ) {}
+      virtual bool progress( int /*value*/ )
+      { return true; }
+
+      virtual Action problem(
+          const zypp::Url &/*url*/
+          , Error /*error*/
+          , const std::string &/*description*/ )
+      { return ABORT; }
+
+      virtual void finish(
+          const zypp::Url &/*url*/
+          , Error /*error*/
+          , const std::string &/*reason*/ )
+      {}
+    };
+
+    struct RepoReport : public callback::ReportBase
+    {
+      enum Action {
+        ABORT,  // abort and return error
+        RETRY, // retry
+        IGNORE  // skip refresh, ignore failed refresh
+      };
+
+      enum Error {
+        NO_ERROR,
+        NOT_FOUND,     // the requested Url was not found
+        IO,            // IO error
+        INVALID                // th source is invalid
+      };
+
+      virtual void start( const ProgressData &/*task*/, const RepoInfo /*repo*/  ) {}
+      virtual bool progress( const ProgressData &/*task*/ )
+      { return true; }
+
+      virtual Action problem(
+          Repository /*source*/
+          , Error /*error*/
+          , const std::string &/*description*/ )
+      { return ABORT; }
+
+      virtual void finish(
+          Repository /*source*/
+          , const std::string &/*task*/
+          , Error /*error*/
+          , const std::string &/*reason*/ )
+      {}
+    };
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace source
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  {
+    // media change request callback
+    struct MediaChangeReport : public callback::ReportBase
+    {
+      enum Action {
+        ABORT,  // abort and return error
+        RETRY, // retry
+        IGNORE, // ignore this media in future, not available anymore
+        IGNORE_ID,     // ignore wrong medium id
+        CHANGE_URL,    // change media URL
+        EJECT          // eject the medium
+      };
+
+      enum Error {
+        NO_ERROR,
+        NOT_FOUND,  // the medie not found at all
+        IO,    // error accessing the media
+        INVALID, // media is broken
+        WRONG, // wrong media, need a different one
+        IO_SOFT       /**< IO error which can happen on worse connection like timeout exceed */
+      };
+
+      /**
+       *
+       * \param url         in: url for which the media is requested,
+       *                    out: url to use instead of the original one
+       * \param mediumNr    requested medium number
+       * \param label       label of requested medium
+       * \param error       type of error from \ref Error enum
+       * \param description error message (media not desired or error foo occured)
+       * \param devices     list of the available devices (for eject)
+       * \param dev_current in: index of the currently used device in the \a devices list
+       *                    out: index of the devices to be ejected in the \a devices list
+       * \return \ref Action (ABORT by default)
+       */
+      virtual Action requestMedia(
+        Url & /* url (I/O parameter) */
+        , unsigned /*mediumNr*/
+        , const std::string & /* label */
+        , Error /*error*/
+        , const std::string & /*description*/
+        , const std::vector<std::string> & /* devices */
+        , unsigned int & /* dev_current (I/O param) */
+      ) { return ABORT; }
+    };
+
+    // progress for downloading a file
+    struct DownloadProgressReport : public callback::ReportBase
+    {
+        enum Action {
+          ABORT,  // abort and return error
+          RETRY,       // retry
+          IGNORE       // ignore the failure
+        };
+
+        enum Error {
+          NO_ERROR,
+          NOT_FOUND,   // the requested Url was not found
+          IO,          // IO error
+          ACCESS_DENIED, // user authent. failed while accessing restricted file
+          ERROR // other error
+        };
+
+        virtual void start( const Url &/*file*/, Pathname /*localfile*/ ) {}
+
+        /**
+         * Download progress.
+         *
+         * \param value        Percentage value.
+         * \param file         File URI.
+         * \param dbps_avg     Average download rate so far. -1 if unknown.
+         * \param dbps_current Current download (cca last 1 sec). -1 if unknown.
+         */
+        virtual bool progress(int /*value*/, const Url &/*file*/,
+                              double dbps_avg = -1,
+                              double dbps_current = -1)
+        { return true; }
+
+        virtual Action problem(
+          const Url &/*file*/
+         , Error /*error*/
+         , const std::string &/*description*/
+        ) { return ABORT; }
+
+        virtual void finish(
+          const Url &/*file*/
+          , Error /*error*/
+         , const std::string &/*reason*/
+        ) {}
+    };
+
+    // authentication issues report
+    struct AuthenticationReport : public callback::ReportBase
+    {
+      /**
+       * Prompt for authentication data.
+       *
+       * \param url       URL which required the authentication
+       * \param msg       prompt text
+       * \param auth_data input/output object for handling authentication
+       *        data. As an input parameter auth_data can be prefilled with
+       *        username (from previous try) or auth_type (available
+       *        authentication methods aquired from server (only CurlAuthData)).
+       *        As an output parameter it serves for sending username, pasword
+       *        or other special data (derived AuthData objects).
+       * \return true if user chooses to continue with authentication,
+       *         false otherwise
+       */
+      virtual bool prompt(const Url & /* url */,
+        const std::string & /* msg */,
+        AuthData & /* auth_data */)
+      {
+        return false;
+      }
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  {
+    /** Request to display the pre commit message of a patch. */
+    struct PatchMessageReport : public callback::ReportBase
+    {
+      /** Display \c patch->message().
+       * Return \c true to continue, \c false to abort commit.
+      */
+      virtual bool show( Patch::constPtr & /*patch*/ )
+      { return true; }
+    };
+
+    /** Indicate execution of a patch script. This is a sort of
+     * \c %post script shipped by a package and to be executed
+     * after the package was installed.
+    */
+    struct PatchScriptReport : public callback::ReportBase
+    {
+      enum Notify { OUTPUT, PING };
+      enum Action {
+        ABORT,  // abort commit and return error
+        RETRY, // (re)try to execute this script
+        IGNORE // ignore any failue and continue
+      };
+
+      /** Start executing the script provided by package.
+      */
+      virtual void start( const Package::constPtr & /*package*/,
+                          const Pathname & /*script path*/ )
+      {}
+      /** Progress provides the script output. If the script is quiet,
+       * from time to time still-alive pings are sent to the ui. Returning \c FALSE
+       * aborts script execution.
+      */
+      virtual bool progress( Notify /*OUTPUT or PING*/,
+                             const std::string & /*output*/ = std::string() )
+      { return true; }
+      /** Report error. */
+      virtual Action problem( const std::string & /*description*/ )
+      { return ABORT; }
+      /** Report success. */
+      virtual void finish()
+      {}
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    namespace rpm
+    {
+
+      // progress for installing a resolvable
+      struct InstallResolvableReport : public callback::ReportBase
+      {
+        enum Action {
+          ABORT,  // abort and return error
+          RETRY,       // retry
+         IGNORE        // ignore the failure
+        };
+
+        enum Error {
+         NO_ERROR,
+          NOT_FOUND,   // the requested Url was not found
+         IO,           // IO error
+         INVALID               // th resolvable is invalid
+        };
+
+        // the level of RPM pushing
+        /** \deprecated We fortunately no longer do 3 attempts. */
+        enum RpmLevel {
+            RPM,
+            RPM_NODEPS,
+            RPM_NODEPS_FORCE
+        };
+
+        virtual void start(
+         Resolvable::constPtr /*resolvable*/
+        ) {}
+
+        virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+        { return true; }
+
+        virtual Action problem(
+          Resolvable::constPtr /*resolvable*/
+         , Error /*error*/
+         , const std::string &/*description*/
+         , RpmLevel /*level*/
+        ) { return ABORT; }
+
+        virtual void finish(
+          Resolvable::constPtr /*resolvable*/
+          , Error /*error*/
+         , const std::string &/*reason*/
+         , RpmLevel /*level*/
+        ) {}
+      };
+
+      // progress for removing a resolvable
+      struct RemoveResolvableReport : public callback::ReportBase
+      {
+        enum Action {
+          ABORT,  // abort and return error
+          RETRY,       // retry
+         IGNORE        // ignore the failure
+        };
+
+        enum Error {
+         NO_ERROR,
+          NOT_FOUND,   // the requested Url was not found
+         IO,           // IO error
+         INVALID               // th resolvable is invalid
+        };
+
+        virtual void start(
+         Resolvable::constPtr /*resolvable*/
+        ) {}
+
+        virtual bool progress(int /*value*/, Resolvable::constPtr /*resolvable*/)
+        { return true; }
+
+        virtual Action problem(
+          Resolvable::constPtr /*resolvable*/
+         , Error /*error*/
+         , const std::string &/*description*/
+        ) { return ABORT; }
+
+        virtual void finish(
+          Resolvable::constPtr /*resolvable*/
+          , Error /*error*/
+         , const std::string &/*reason*/
+        ) {}
+      };
+
+      // progress for rebuilding the database
+      struct RebuildDBReport : public callback::ReportBase
+      {
+        enum Action {
+          ABORT,  // abort and return error
+          RETRY,       // retry
+         IGNORE        // ignore the failure
+        };
+
+        enum Error {
+         NO_ERROR,
+         FAILED                // failed to rebuild
+        };
+
+        virtual void start(Pathname /*path*/) {}
+
+        virtual bool progress(int /*value*/, Pathname /*path*/)
+        { return true; }
+
+        virtual Action problem(
+         Pathname /*path*/
+        , Error /*error*/
+        , const std::string &/*description*/
+        ) { return ABORT; }
+
+        virtual void finish(
+         Pathname /*path*/
+          , Error /*error*/
+         , const std::string &/*reason*/
+        ) {}
+      };
+
+      // progress for converting the database
+      struct ConvertDBReport : public callback::ReportBase
+      {
+        enum Action {
+          ABORT,  // abort and return error
+          RETRY,       // retry
+         IGNORE        // ignore the failure
+        };
+
+        enum Error {
+         NO_ERROR,
+         FAILED                // conversion failed
+        };
+
+        virtual void start(
+         Pathname /*path*/
+        ) {}
+
+        virtual bool progress(int /*value*/, Pathname /*path*/)
+        { return true; }
+
+        virtual Action problem(
+         Pathname /*path*/
+         , Error /*error*/
+        , const std::string &/*description*/
+        ) { return ABORT; }
+
+        virtual void finish(
+         Pathname /*path*/
+          , Error /*error*/
+          , const std::string &/*reason*/
+        ) {}
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace rpm
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+
+  class PoolQuery;
+
+  /** \name Locks */
+  //@{
+  /**
+   * Callback for cleaning locks which doesn't lock anything in pool.
+   */
+
+  struct CleanEmptyLocksReport : public callback::ReportBase
+  {
+    /**
+     * action performed by cleaning api to specific lock
+     */
+    enum Action {
+      ABORT,  /**< abort and return error */
+      DELETE, /**< delete empty lock    */
+      IGNORE  /**< skip empty lock */
+    };
+
+    /**
+     * result of cleaning
+     */
+    enum Error {
+      NO_ERROR, /**< no problem */
+      ABORTED /** cleaning aborted by user */
+    };
+
+    /**
+     * cleaning is started
+     */
+    virtual void start(
+    ) {}
+
+    /**
+     * progress of cleaning specifies in percents
+     * \return if continue
+     */
+    virtual bool progress(int /*value*/)
+    { return true; }
+
+    /**
+     * When find empty lock ask what to do with it
+     * \return action
+     */
+    virtual Action execute(
+        const PoolQuery& /*error*/
+     ) { return DELETE; }
+
+      /**
+       * cleaning is done
+       */
+     virtual void finish(
+       Error /*error*/
+      ) {}
+
+  };
+
+  /**
+   * this callback handles merging old locks with newly added or removed
+   */
+  struct SavingLocksReport : public callback::ReportBase
+  {
+    /**
+     * action for old lock which is in conflict
+     * \see ConflictState
+     */
+    enum Action {
+      ABORT,  /**< abort and return error*/
+      DELETE, /**< delete conflicted lock    */
+      IGNORE  /**< skip conflict lock */
+    };
+
+    /**
+     * result of merging
+     */
+    enum Error {
+      NO_ERROR, /**< no problem */
+      ABORTED  /**< cleaning aborted by user */
+    };
+
+    /**
+     * type of conflict of old and new lock
+     */
+    enum ConflictState{
+      SAME_RESULTS, /**< locks lock same item in pool but his parameters is different */
+      INTERSECT /**< locks lock some file and unlocking lock unlock only part
+      * of iti, so removing old lock can unlock more items in pool */
+    };
+
+    virtual void start() {}
+
+    /**
+     * merging still live
+     * \return if continue
+     */
+    virtual bool progress()
+    { return true; }
+
+    /**
+     * When user unlock something which is locked by non-identical query
+     */
+    virtual Action conflict(
+        const PoolQuery&, /**< problematic query*/
+       ConflictState
+     ) { return DELETE; }
+
+     virtual void finish(
+       Error /*error*/
+      ) {}
+  };
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_ZYPPCALLBACKS_H
diff --git a/zypp/ZYppCommit.h b/zypp/ZYppCommit.h
new file mode 100644 (file)
index 0000000..959ad73
--- /dev/null
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppCommit.h
+ *
+*/
+#ifndef ZYPP_ZYPPCOMMIT_H
+#define ZYPP_ZYPPCOMMIT_H
+
+#include <iosfwd>
+
+#include "zypp/ZYppCommitPolicy.h"
+#include "zypp/ZYppCommitResult.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPPCOMMIT_H
diff --git a/zypp/ZYppCommitPolicy.cc b/zypp/ZYppCommitPolicy.cc
new file mode 100644 (file)
index 0000000..8527486
--- /dev/null
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppCommitPolicy.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/String.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/ZYppCommitPolicy.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitPolicy::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  class ZYppCommitPolicy::Impl
+  {
+    public:
+      Impl()
+      : _restrictToMedia       ( 0 )
+      , _dryRun                        ( false )
+      , _downloadMode          ( ZConfig::instance().commit_downloadMode() )
+      , _rpmInstFlags          ( ZConfig::instance().rpmInstallFlags() )
+      , _syncPoolAfterCommit   ( true )
+      {}
+
+    public:
+      unsigned                 _restrictToMedia;
+      bool                     _dryRun;
+      DownloadMode             _downloadMode;
+      target::rpm::RpmInstFlags        _rpmInstFlags;
+      bool                     _syncPoolAfterCommit;
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const { return new Impl( *this ); }
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitPolicy
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ZYppCommitPolicy::ZYppCommitPolicy()
+  : _pimpl( new Impl )
+  {}
+
+
+  ZYppCommitPolicy & ZYppCommitPolicy::restrictToMedia( unsigned mediaNr_r )
+  { _pimpl->_restrictToMedia = ( mediaNr_r == 1 ) ? 1 : 0; return *this; }
+
+  unsigned ZYppCommitPolicy::restrictToMedia() const
+  { return _pimpl->_restrictToMedia; }
+
+
+  ZYppCommitPolicy & ZYppCommitPolicy::dryRun( bool yesNo_r )
+  { _pimpl->_dryRun = yesNo_r; return *this; }
+
+  bool ZYppCommitPolicy::dryRun() const
+  { return _pimpl->_dryRun; }
+
+
+  ZYppCommitPolicy & ZYppCommitPolicy::downloadMode( DownloadMode val_r )
+  { _pimpl->_downloadMode = val_r; return *this; }
+
+  DownloadMode ZYppCommitPolicy::downloadMode() const
+  { return _pimpl->_downloadMode; }
+
+
+  ZYppCommitPolicy &  ZYppCommitPolicy::rpmInstFlags( target::rpm::RpmInstFlags newFlags_r )
+  { _pimpl->_rpmInstFlags = newFlags_r; return *this; }
+
+  ZYppCommitPolicy & ZYppCommitPolicy::rpmNoSignature( bool yesNo_r )
+  { _pimpl->_rpmInstFlags.setFlag( target::rpm::RPMINST_NOSIGNATURE, yesNo_r ); return *this; }
+
+  ZYppCommitPolicy & ZYppCommitPolicy::rpmExcludeDocs( bool yesNo_r )
+  { _pimpl->_rpmInstFlags.setFlag( target::rpm::RPMINST_EXCLUDEDOCS, yesNo_r ); return *this; }
+
+  target::rpm::RpmInstFlags ZYppCommitPolicy::rpmInstFlags() const
+  { return _pimpl->_rpmInstFlags; }
+
+  bool ZYppCommitPolicy::rpmNoSignature() const
+  { return _pimpl->_rpmInstFlags.testFlag( target::rpm::RPMINST_NOSIGNATURE ); }
+
+  bool ZYppCommitPolicy::rpmExcludeDocs() const
+  { return _pimpl->_rpmInstFlags.testFlag( target::rpm::RPMINST_EXCLUDEDOCS ); }
+
+
+  ZYppCommitPolicy & ZYppCommitPolicy::syncPoolAfterCommit( bool yesNo_r )
+  { _pimpl->_syncPoolAfterCommit = yesNo_r; return *this; }
+
+  bool ZYppCommitPolicy::syncPoolAfterCommit() const
+  { return _pimpl->_syncPoolAfterCommit; }
+
+
+  std::ostream & operator<<( std::ostream & str, const ZYppCommitPolicy & obj )
+  {
+    str << "CommitPolicy(";
+    if ( obj.restrictToMedia() )
+      str << " restrictToMedia:" << obj.restrictToMedia();
+    if ( obj.dryRun() )
+      str << " dryRun";
+    str << " " << obj.downloadMode();
+    if ( obj.syncPoolAfterCommit() )
+      str << " syncPoolAfterCommit";
+    if ( obj.rpmInstFlags() )
+      str << " rpmInstFlags{" << str::hexstring(obj.rpmInstFlags()) << "}";
+    return str << " )";
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ZYppCommitPolicy.h b/zypp/ZYppCommitPolicy.h
new file mode 100644 (file)
index 0000000..69624e0
--- /dev/null
@@ -0,0 +1,108 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppCommitPolicy.h
+ *
+*/
+#ifndef ZYPP_ZYPPCOMMITPOLICY_H
+#define ZYPP_ZYPPCOMMITPOLICY_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/DownloadMode.h"
+#include "zypp/target/rpm/RpmFlags.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitPolicy
+  //
+  /** Options and policies for ZYpp::commit.
+   * \see \ref ZYpp::commit
+   */
+  class ZYppCommitPolicy
+  {
+    public:
+
+      ZYppCommitPolicy();
+
+    public:
+      /** Restrict commit to media 1.
+       * Fake outstanding YCP fix: Honour restriction to media 1
+       * at installation, but install all remaining packages if
+       * post-boot (called with <tt>mediaNr_r &gt; 1</tt>).
+       */
+      ZYppCommitPolicy & restrictToMedia( unsigned mediaNr_r );
+
+      /** Process all media (default) */
+      ZYppCommitPolicy & allMedia()
+      { return restrictToMedia( 0 ); }
+
+      unsigned restrictToMedia() const;
+
+
+      /** Set dry run (default: false).
+       * Dry-run should not change anything on the system, unless
+       * the \ref downloadMode is set to \ref DownloadOnly. In that
+       * case packages are downloaded to the local cache.
+      */
+      ZYppCommitPolicy & dryRun( bool yesNo_r );
+
+      bool dryRun() const;
+
+
+      /** Commit download policy to use. (default: \ref DownloadDefault)
+       *  \note \ref DownloadOnly also implies a \ref dryRun.
+       */
+      ZYppCommitPolicy & downloadMode( DownloadMode val_r );
+
+      DownloadMode downloadMode() const;
+
+
+      /** The default \ref target::rpm::RpmInstFlags. (default: none)*/
+      ZYppCommitPolicy &  rpmInstFlags( target::rpm::RpmInstFlags newFlags_r );
+
+      /** Use rpm option --nosignature (default: false) */
+      ZYppCommitPolicy & rpmNoSignature( bool yesNo_r );
+
+      /** Use rpm option --excludedocs (default: false) */
+      ZYppCommitPolicy & rpmExcludeDocs( bool yesNo_r );
+
+      target::rpm::RpmInstFlags rpmInstFlags() const;
+
+      bool rpmNoSignature() const;
+
+      bool rpmExcludeDocs() const;
+
+
+      /** Kepp pool in sync with the Target databases after commit (default: true) */
+      ZYppCommitPolicy & syncPoolAfterCommit( bool yesNo_r );
+
+      bool syncPoolAfterCommit() const;
+
+    public:
+      /** Implementation  */
+      class Impl;
+    private:
+      /** Pointer to data. */
+      RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ZYppCommitPolicy Stream output. */
+  std::ostream & operator<<( std::ostream & str, const ZYppCommitPolicy & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPPCOMMITPOLICY_H
diff --git a/zypp/ZYppCommitResult.cc b/zypp/ZYppCommitResult.cc
new file mode 100644 (file)
index 0000000..521439e
--- /dev/null
@@ -0,0 +1,108 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppCommitResult.cc
+ *
+*/
+
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/ZYppCommitResult.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitResult::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  class ZYppCommitResult::Impl
+  {
+    public:
+      Impl()
+      {}
+
+    public:
+      Pathname                 _root;
+      sat::Transaction          _transaction;
+      TransactionStepList       _transactionStepList;
+      UpdateNotifications      _updateMessages;
+
+    private:
+      friend Impl * rwcowClone<Impl>( const Impl * rhs );
+      /** clone for RWCOW_pointer */
+      Impl * clone() const { return new Impl( *this ); }
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitResult
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ZYppCommitResult::ZYppCommitResult()
+  : _result(0), _pimpl( new Impl )
+  {}
+
+  ZYppCommitResult::ZYppCommitResult( const Pathname & root_r )
+  : _result(0), _pimpl( new Impl )
+  { _pimpl->_root = root_r; }
+
+  const Pathname & ZYppCommitResult::root() const
+  { return _pimpl->_root; }
+
+  const sat::Transaction & ZYppCommitResult::transaction() const
+  { return _pimpl->_transaction; }
+
+  sat::Transaction & ZYppCommitResult::rTransaction()
+  { return _pimpl->_transaction; }
+
+  const ZYppCommitResult::TransactionStepList & ZYppCommitResult::transactionStepList() const
+  { return _pimpl->_transactionStepList; }
+
+  ZYppCommitResult::TransactionStepList & ZYppCommitResult::rTransactionStepList()
+  { return _pimpl->_transactionStepList; }
+
+  const UpdateNotifications & ZYppCommitResult::updateMessages() const
+  { return _pimpl->_updateMessages; }
+
+  UpdateNotifications & ZYppCommitResult::rUpdateMessages()
+  { return _pimpl->_updateMessages; }
+
+  ///////////////////////////////////////////////////////////////////
+
+  std::ostream & operator<<( std::ostream & str, const ZYppCommitResult & obj )
+  {
+    DefaultIntegral<unsigned,0> result[4];
+    for_( it, obj.transaction().actionBegin(), obj.transaction().actionEnd() )
+    {
+      ++result[0];
+      switch ( it->stepStage() )
+      {
+       case sat::Transaction::STEP_DONE :      ++result[1]; break;
+       case sat::Transaction::STEP_ERROR :     ++result[2]; break;
+       case sat::Transaction::STEP_TODO :      ++result[3]; break;
+      }
+    }
+    str << "CommitResult "
+        << " (total " << result[0]
+        << ", done " << result[1]
+        << ", error " << result[2]
+        << ", skipped " << result[3]
+        << ", updateMessages " << obj.updateMessages().size()
+        << ")";
+    return str;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ZYppCommitResult.h b/zypp/ZYppCommitResult.h
new file mode 100644 (file)
index 0000000..2ff53d0
--- /dev/null
@@ -0,0 +1,205 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppCommitResult.h
+ *
+*/
+#ifndef ZYPP_ZYPPCOMMITRESULT_H
+#define ZYPP_ZYPPCOMMITRESULT_H
+
+#include <iosfwd>
+#include <vector>
+#include <list>
+
+#include "zypp/PoolItem.h"
+#include "zypp/sat/Transaction.h"
+#include "zypp/base/DefaultIntegral.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace sat
+  {
+    class Transaction;
+  }
+
+  /** Pair of \ref sat::Solvable and \ref Pathname. */
+  class UpdateNotificationFile
+  {
+    public:
+      UpdateNotificationFile( sat::Solvable solvable_r, const Pathname & file_r )
+      : _solvable( solvable_r ), _file( file_r )
+      {}
+    public:
+      sat::Solvable solvable() const { return _solvable; }
+      const Pathname & file() const { return _file; }
+    private:
+      sat::Solvable _solvable;
+      Pathname      _file;
+  };
+
+  typedef std::list<UpdateNotificationFile> UpdateNotifications;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppCommitResult
+  //
+  /** Result returned from ZYpp::commit.
+   *
+   * \note Transaction data are provided and maintained during commit.
+   * Though the interface does not inhibit manipulation of transaction
+   * data outside commit (those methods could have been made \c private:),
+   * this is not recommended as you may easily mess up things.
+   *
+   * \see \ref ZYpp::commit
+   */
+  class ZYppCommitResult
+  {
+    public:
+      typedef std::vector<sat::Transaction::Step> TransactionStepList;
+
+    public:
+      ZYppCommitResult();
+      ZYppCommitResult( const Pathname & root_r );
+
+    public:
+      /** Remembered root directory of the target.
+       *  \Note Pathnames within this class are relative to the
+       * targets root directory.
+      */
+      const Pathname & root() const;
+
+      /** The full transaction list.
+       * The complete list including transaction steps that do not require
+       * any action (like obsoletes or non-package actions). Depending on
+       * \ref ZYppCommitPolicy::restrictToMedia only a subset of this
+       * transaction might have been executed.
+       * \see \ref transactionStepList.
+       */
+      const sat::Transaction & transaction() const;
+
+      /** Manipulate \ref transaction */
+      sat::Transaction & rTransaction();
+
+      /** List of \ref sat::Transaction::Step to be executed by commit.
+       * The list of transaction step commit actually tried to execute.
+       */
+      const TransactionStepList & transactionStepList() const;
+
+      /** Manipulate \ref transactionStepList. */
+      TransactionStepList & rTransactionStepList();
+
+      /** List of update messages installed during this commit.
+       * \Note Pathnames are relative to the targets root directory.
+       * \code
+       *   ZYppCommitResult result;
+       *   ...
+       *   if ( ! result.updateMessages().empty() )
+       *   {
+       *     MIL << "Received " << result.updateMessages().size() << " update notification(s):" << endl;
+       *     for_( it, result.updateMessages().begin(), result.updateMessages().end() )
+       *     {
+       *       MIL << "- From " << it->solvable().asString() << " in file " << Pathname::showRootIf( result.root(), it->file() ) << ":" << endl;
+       *       {
+       *         // store message files content in a string:
+       *         InputStream istr( Pathname::assertprefix( result.root(), it->file() ) );
+       *         std::ostringstream strstr;
+       *         iostr::copy( istr, strstr );
+       *         std::string message( strstr.str() ); // contains the message
+       *       }
+       *       {
+       *         // or write out the message file indented:
+       *         InputStream istr( Pathname::assertprefix( result.root(), it->file() ) );
+       *         iostr::copyIndent( istr, MIL, "> " ) << endl;
+       *       }
+       *     }
+       *   }
+       * \endcode
+       */
+      const UpdateNotifications & updateMessages() const;
+
+      /** Manipulate \ref updateMessages
+       * \Note Pathnames are relative to the targets root directory.
+       */
+      UpdateNotifications & rUpdateMessages();
+
+    public:
+
+      /** \name Some statistics based on \ref Transaction
+       *
+       * Class \ref Transaction allows to count and iterate the action steps to
+       * get more detailed information about the transaction result. Here are just
+       * a few convenience methods for easy evaluation.
+       *
+       * \code
+       *    ZYppCommitResult result;
+       *    const sat::Transaction & trans( result.transaction() );
+       *    for_( it, trans.actionBegin(~sat::Transaction::STEP_DONE), trans.actionEnd() )
+       *    {
+       *       // process all steps not DONE (ERROR and TODO)
+       *       if ( it->satSolvable() )
+       *         std::cout << it->satSolvable() << endl;
+       *       else // deleted @System solvable: print post mortem data available
+       *         std::cout << it->ident() << endl;
+       *    }
+       * \endcode
+       * \see \ref Transaction, \ref transaction()
+       */
+      //@{
+       /** Whether all steps were performed successfully (none skipped or error) */
+       bool allDone() const
+       { return transaction().actionEmpty( ~sat::Transaction::STEP_DONE ); }
+
+       /** Whether an error ocurred (skipped streps are ok). */
+       bool noError() const
+       { return transaction().actionEmpty( sat::Transaction::STEP_ERROR ); }
+      //@}
+
+    public:
+      /** \name Oldstlye interface to be removed asap.
+       * \deprecated PoolItem is not suitable for reporting errors about
+       * packages to be deteled, as reloading the rpm database after commit
+       * invalidates them.
+       */
+      //@{
+      typedef std::list<PoolItem> PoolItemList;
+      /**
+       * number of committed resolvables
+       **/
+      int          _result ZYPP_DEPRECATED;
+      /**
+       * list of resolvables with error
+       **/
+      PoolItemList _errors ZYPP_DEPRECATED;
+      /**
+       * list of resolvables remaining (due to wrong media)
+       **/
+      PoolItemList _remaining ZYPP_DEPRECATED;
+      /**
+       * list of kind:source resolvables remaining (due to wrong media)
+       **/
+      PoolItemList _srcremaining ZYPP_DEPRECATED;
+      //@}
+
+    public:
+      /** Implementation  */
+      class Impl;
+    private:
+      /** Pointer to data. */
+      RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ZYppCommitResult Stream output. */
+  std::ostream & operator<<( std::ostream & str, const ZYppCommitResult & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPPCOMMITRESULT_H
diff --git a/zypp/ZYppFactory.cc b/zypp/ZYppFactory.cc
new file mode 100644 (file)
index 0000000..1b99bfa
--- /dev/null
@@ -0,0 +1,412 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppFactory.cc
+ *
+*/
+extern "C"
+{
+#include <sys/file.h>
+}
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/zypp_detail/ZYppImpl.h"
+#include "zypp/zypp_detail/ZYppReadOnlyHack.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace env
+  {
+    /** Hack to circumvent the currently poor --root support. */
+    inline Pathname ZYPP_LOCKFILE_ROOT()
+    { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace zypp_readonly_hack
+  { /////////////////////////////////////////////////////////////////
+
+    static bool active = false;
+
+    void IWantIt()
+    {
+      active = true;
+      MIL << "ZYPP_READONLY promised." <<  endl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace zypp_readonly_hack
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : ZYppGlobalLock
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  class ZYppGlobalLock
+  {
+    public:
+
+      ZYppGlobalLock()
+      : _clean_lock(false)
+      , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
+      , _zypp_lockfile(0)
+      , _locker_pid(0)
+    {
+      filesystem::assert_dir(_zyppLockFilePath.dirname());
+    }
+
+    ~ZYppGlobalLock()
+    {
+      try
+        {
+          pid_t curr_pid = getpid();
+          if ( _zypp_lockfile )
+            {
+              unLockFile();
+              closeLockFile();
+
+              if ( _clean_lock )
+              {
+                MIL << "Cleaning lock file. (" << curr_pid << ")" << std::endl;
+                if ( filesystem::unlink(_zyppLockFilePath) == 0 )
+                  MIL << "Lockfile cleaned. (" << curr_pid << ")" << std::endl;
+                else
+                  ERR << "Cant clean lockfile. (" << curr_pid << ")" << std::endl;
+              }
+            }
+        }
+      catch(...) {} // let no exception escape.
+    }
+
+    pid_t locker_pid() const
+    { return _locker_pid; }
+
+    const std::string & locker_name() const
+    { return _locker_name; }
+
+
+    bool _clean_lock;
+
+    private:
+    Pathname _zyppLockFilePath;
+    FILE *_zypp_lockfile;
+    pid_t _locker_pid;
+    std::string _locker_name;
+
+    void openLockFile(const char *mode)
+    {
+
+      _zypp_lockfile = fopen(_zyppLockFilePath.asString().c_str(), mode);
+      if (_zypp_lockfile == 0)
+        ZYPP_THROW (Exception( "Cant open " + _zyppLockFilePath.asString() + " in mode " + std::string(mode) ) );
+    }
+
+    void closeLockFile()
+    {
+      fclose(_zypp_lockfile);
+    }
+
+    void shLockFile()
+    {
+      int fd = fileno(_zypp_lockfile);
+      int lock_error = flock(fd, LOCK_SH);
+      if (lock_error != 0)
+        ZYPP_THROW (Exception( "Cant get shared lock"));
+      else
+        MIL << "locked (shared)" << std::endl;
+    }
+
+    void exLockFile()
+    {
+      int fd = fileno(_zypp_lockfile);
+    // lock access to the file
+      int lock_error = flock(fd, LOCK_EX);
+      if (lock_error != 0)
+        ZYPP_THROW (Exception( "Cant get exclusive lock" ));
+      else
+        MIL << "locked (exclusive)" << std::endl;
+    }
+
+    void unLockFile()
+    {
+      int fd = fileno(_zypp_lockfile);
+    // lock access to the file
+      int lock_error = flock(fd, LOCK_UN);
+      if (lock_error != 0)
+        ZYPP_THROW (Exception( "Cant release lock" ));
+      else
+        MIL << "unlocked" << std::endl;
+    }
+
+    bool lockFileExists()
+    {
+      // check if the file already existed.
+      PathInfo pi(_zyppLockFilePath);
+      DBG << pi << endl;
+      return pi.isExist();
+    }
+
+    void createLockFile()
+    {
+      pid_t curr_pid = getpid();
+      openLockFile("w");
+      exLockFile();
+      fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid);
+      fflush(_zypp_lockfile);
+      unLockFile();
+      MIL << "written lockfile with pid " << curr_pid << std::endl;
+      closeLockFile();
+    }
+
+    bool isProcessRunning(pid_t pid_r)
+    {
+      // it is another program, not me, see if it is still running
+      Pathname procdir( "/proc"/str::numstring(pid_r) );
+      PathInfo status( procdir );
+      MIL << "Checking " <<  status << endl;
+
+      if ( ! status.isDir() )
+      {
+       DBG << "No such process." << endl;
+       return false;
+      }
+
+      static char buffer[513];
+      buffer[0] = buffer[512] = 0;
+      // man proc(5): /proc/[pid]/cmdline is empty if zombie.
+      if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
+      {
+       _locker_name = buffer;
+       DBG << "Is running: " <<  _locker_name << endl;
+       return true;
+      }
+
+      DBG << "In zombie state." << endl;
+      return false;
+    }
+
+    pid_t lockerPid()
+    {
+      pid_t curr_pid = getpid();
+      pid_t locker_pid = 0;
+      long readpid = 0;
+
+      fscanf(_zypp_lockfile, "%ld", &readpid);
+      MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << curr_pid << ") "<< std::endl;
+      locker_pid = (pid_t) readpid;
+      return locker_pid;
+    }
+
+  public:
+
+    bool zyppLocked()
+    {
+      pid_t curr_pid = getpid();
+
+      if ( lockFileExists() )
+      {
+        MIL << "found lockfile " << _zyppLockFilePath << std::endl;
+        openLockFile("r");
+        shLockFile();
+
+        pid_t locker_pid = lockerPid();
+        _locker_pid = locker_pid;
+       if ( locker_pid == curr_pid )
+        {
+        // alles ok, we are requesting the instance again
+          //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl;
+          return false;
+        }
+        else
+        {
+          if ( isProcessRunning(locker_pid) )
+          {
+            if ( geteuid() == 0 )
+            {
+              // i am root
+              MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl;
+              return true;
+            }
+            else
+            {
+              MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
+              return false;
+            }
+          }
+          else
+          {
+            if ( geteuid() == 0 )
+            {
+              MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl;
+              if ( filesystem::unlink(_zyppLockFilePath) == 0 )
+              {
+                createLockFile();
+              // now open it for reading
+                openLockFile("r");
+                shLockFile();
+                return false;
+              }
+              else
+              {
+                ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl;
+                return true;
+              }
+            }
+            else
+            {
+              MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
+              return false;
+            }
+          }
+        }
+      }
+      else
+      {
+        MIL << "no lockfile " << _zyppLockFilePath << " found" << std::endl;
+        if ( geteuid() == 0 )
+        {
+          MIL << "running as root. Will attempt to create " << _zyppLockFilePath << std::endl;
+          createLockFile();
+        // now open it for reading
+          openLockFile("r");
+          shLockFile();
+        }
+        else
+        {
+          MIL << "running as user. Skipping creating " << _zyppLockFilePath << std::endl;
+        }
+        return false;
+      }
+      return true;
+    }
+
+  };
+
+  namespace
+  {
+    ZYppGlobalLock globalLock;
+    bool           _haveZYpp = false;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppFactoryException
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
+    : Exception( msg_r )
+    , _lockerPid( lockerPid_r )
+    , _lockerName( lockerName_r )
+  {}
+
+  ZYppFactoryException::~ZYppFactoryException() throw ()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppFactory
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYppFactory::instance
+  //   METHOD TYPE : ZYppFactory
+  //
+  ZYppFactory ZYppFactory::instance()
+  {
+    return ZYppFactory();
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYppFactory::ZYppFactory
+  //   METHOD TYPE : Ctor
+  //
+  ZYppFactory::ZYppFactory()
+  {
+
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : ZYppFactory::~ZYppFactory
+  //   METHOD TYPE : Dtor
+  //
+  ZYppFactory::~ZYppFactory()
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  ZYpp::Ptr ZYppFactory::getZYpp() const
+  {
+    static ZYpp::Ptr _instance;
+
+    if ( ! _instance )
+    {
+      /*--------------------------------------------------*/
+      if ( zypp_readonly_hack::active )
+      {
+          _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
+          MIL << "ZYPP_READONLY active." << endl;
+      }
+      /*--------------------------------------------------*/
+      else if ( globalLock.zyppLocked() )
+      {
+       std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
+                                     "Close this application before trying again."),
+                                  globalLock.locker_pid(),
+                                  globalLock.locker_name().c_str()
+                                 );
+       ZYPP_THROW(ZYppFactoryException(t, globalLock.locker_pid(),globalLock.locker_name() ));
+      }
+      else
+      {
+        _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
+        globalLock._clean_lock = true;
+      }
+
+      if ( _instance )
+        _haveZYpp = true;
+    }
+
+    return _instance;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  bool ZYppFactory::haveZYpp() const
+  { return _haveZYpp; }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
+  {
+    return str << "ZYppFactory";
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ZYppFactory.h b/zypp/ZYppFactory.h
new file mode 100644 (file)
index 0000000..5ef9d01
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ZYppFactory.h
+ *
+*/
+#ifndef ZYPP_ZYPPFACTORY_H
+#define ZYPP_ZYPPFACTORY_H
+
+#include <iosfwd>
+
+#include "zypp/base/Exception.h"
+#include "zypp/ZYpp.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class ZYppFactoryException : public Exception
+  {
+  public:
+    ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r );
+    virtual ~ZYppFactoryException() throw ();
+  public:
+    pid_t lockerPid() const { return _lockerPid; }
+    const std::string & lockerName() const { return _lockerName; }
+    /** \deprecated User lockerPid */
+    ZYPP_DEPRECATED pid_t locker_pid() const { return lockerPid(); }
+  private:
+    pid_t _lockerPid;
+    std::string _lockerName;
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : ZYppFactory
+  //
+  /** ZYpp factory class (Singleton)
+  */
+  class ZYppFactory
+  {
+    friend std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj );
+
+  public:
+    /** Singleton ctor */
+    static ZYppFactory instance();
+    /** Dtor */
+    ~ZYppFactory();
+
+  public:
+    /** \return Pointer to the ZYpp instance.
+     * \throw EXCEPTION In case we can't acquire a lock.
+    */
+    ZYpp::Ptr getZYpp() const;
+
+    /** Whether the ZYpp instance is already created.*/
+    bool haveZYpp() const;
+
+  private:
+    /** Default ctor. */
+    ZYppFactory();
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates ZYppFactory Stream output */
+  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj );
+
+  /** \relates ZYppFactory Convenience to get the Pointer
+   * to the ZYpp instance.
+   * \see ZYppFactory::getZYpp
+  */
+  inline ZYpp::Ptr getZYpp()
+  { return ZYppFactory::instance().getZYpp(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPPFACTORY_H
diff --git a/zypp/base/Algorithm.h b/zypp/base/Algorithm.h
new file mode 100644 (file)
index 0000000..b67db98
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Algorithm.h
+ *
+*/
+#ifndef ZYPP_BASE_ALGORITHM_H
+#define ZYPP_BASE_ALGORITHM_H
+
+#include <algorithm>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Iterate through <tt>[begin_r,end_r)</tt> and invoke \a fnc_r
+   *  on each item that passes \a filter_r.
+   *
+   * Iteration aborts if \a fnc_r returns \c false.
+   *
+   * \return Number of invokations of \a fnc_r, negative if
+   * loop was aborted by \a fnc_.
+  */
+  template <class _Iterator, class _Filter, class _Function>
+    inline int invokeOnEach( _Iterator begin_r, _Iterator end_r,
+                             _Filter filter_r,
+                             _Function fnc_r )
+    {
+      int cnt = 0;
+      for ( _Iterator it = begin_r; it != end_r; ++it )
+        {
+          if ( filter_r( *it ) )
+            {
+              ++cnt;
+              if ( ! fnc_r( *it ) )
+                  return -cnt;
+            }
+        }
+      return cnt;
+    }
+
+  /** Iterate through <tt>[begin_r,end_r)</tt> and invoke \a fnc_r
+   *  on each item.
+   *
+   * Iteration aborts if \a fnc_r returns \c false.
+   *
+   * \return Number of invokations of \a fnc_r, negative if
+   * loop was aborted by \a fnc_.
+  */
+  template <class _Iterator, class _Function>
+    inline int invokeOnEach( _Iterator begin_r, _Iterator end_r,
+                             _Function fnc_r )
+    {
+      int cnt = 0;
+      for ( _Iterator it = begin_r; it != end_r; ++it )
+        {
+          ++cnt;
+          if ( ! fnc_r( *it ) )
+            return -cnt;
+        }
+      return cnt;
+    }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_ALGORITHM_H
diff --git a/zypp/base/Collector.h b/zypp/base/Collector.h
new file mode 100644 (file)
index 0000000..605e7b4
--- /dev/null
@@ -0,0 +1,66 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Collector.h
+ *
+*/
+#ifndef ZYPP_BASE_COLLECTOR_H
+#define ZYPP_BASE_COLLECTOR_H
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace functor
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : _Collector<_OutputIterator>
+  //
+  /** Functor feeding values to an output_iterator.
+   *
+   * \code
+   * LocaleSet locales;
+   * for_each( begin(), end(),
+   *           Collector( std::inserter( locales_r, locales_r.begin() ) ) );
+   * \endcode
+   *
+   * \see Convenience constructor \ref Collector.
+   */
+  template<class _OutputIterator>
+  struct _Collector
+  {
+    _Collector( _OutputIterator iter_r ) : _iter( iter_r ) {}
+
+    template<class _Tp>
+    bool operator()( const _Tp & value_r ) const
+    {
+      *_iter++ = value_r;
+      return true;
+    }
+
+    private:
+      mutable _OutputIterator _iter;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates _Collector Convenience constructor. */
+  template<class _OutputIterator>
+  inline _Collector<_OutputIterator> Collector( _OutputIterator iter_r )
+  { return _Collector<_OutputIterator>( iter_r ); }
+
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace functor
+///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_COLLECTOR_H
diff --git a/zypp/base/Counter.h b/zypp/base/Counter.h
new file mode 100644 (file)
index 0000000..ce5418d
--- /dev/null
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Counter.h
+ *
+*/
+#ifndef ZYPP_BASE_COUNTER_H
+#define ZYPP_BASE_COUNTER_H
+
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Counter
+  //
+  /** Integral type with initial value \c 0.
+  */
+  template<class _IntT>
+    class Counter
+    {
+    public:
+      Counter( _IntT value_r = _IntT(0) )
+      : _value( _IntT( value_r ) )
+      {}
+
+      operator _IntT &()
+      { return _value; }
+
+      operator const _IntT &() const
+      { return _value; }
+
+    public:
+      _IntT _value;
+    };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_COUNTER_H
diff --git a/zypp/base/Debug.h b/zypp/base/Debug.h
new file mode 100644 (file)
index 0000000..e19d94f
--- /dev/null
@@ -0,0 +1,172 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Debug.h
+ *
+ * Debuging tools which should not be used in released code.
+*/
+#warning ZYPP_BASE_DEBUG_H included
+#ifndef ZYPP_BASE_DEBUG_H
+#define ZYPP_BASE_DEBUG_H
+
+#include <iosfwd>
+//#include <sstream>
+//#include <string>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/base/ProvideNumericId.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace debug
+  { /////////////////////////////////////////////////////////////////
+
+    /** \defgroup DEBUG Debug tools
+    */
+
+#define TAG INT << __PRETTY_FUNCTION__ << std::endl
+
+    /** 'ps v' */
+    inline std::ostream & dumpMemOn( std::ostream & str, const std::string & msg = std::string() )
+    {
+      static std::string mypid( str::numstring( getpid() ) );
+      const char* argv[] =
+      {
+        "ps",
+        "v",
+        mypid.c_str(),
+        NULL
+      };
+      ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+      str << "MEMUSAGE " << msg << std::endl;
+      for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+          str << line;
+
+      prog.close();
+      return str;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    /** \defgroup DBG_TRACER Tracer
+     * \ingroup DEBUG
+    */
+    //@{
+    /** Base for a simple tracer. Provides an enum indicating which
+     * traced functions were called.
+    */
+    struct TraceCADBase
+    {
+      enum What { CTOR, COPYCTOR, ASSIGN, DTOR, PING };
+      std::string _ident;
+    };
+
+    /** \relates TraceCADBase Stream output of TraceCADBase::What. */
+    inline std::ostream & operator<<( std::ostream & str, TraceCADBase::What obj )
+    {
+      switch( obj )
+        {
+        case TraceCADBase::CTOR:     return str << "CTOR";
+        case TraceCADBase::COPYCTOR: return str << "COPYCTOR";
+        case TraceCADBase::ASSIGN:   return str << "ASSIGN";
+        case TraceCADBase::DTOR:     return str << "DTOR";
+        case TraceCADBase::PING:     return str << "PING";
+        }
+      return str;
+    }
+
+    /** A simple tracer for (copy) Construction, Assignment, and
+     * Destruction. To trace class Foo, derive public from
+     * TraceCAD<Foo>. This tracer simply calls traceCAD in each
+     * traced method, and traceCAD simply drops a line in the log.
+     *
+     * This tracer logs construction, copy construction, assignment,
+     * destruction and _PING.
+     *
+     * assignment: In case the traced class defines an operator=
+     * it must be altered to call TraceCAD::operator=, otherwise it
+     * won't be triggered.
+     *
+     * _PING: Completely up to you. Call _PING somewhere in the traced
+     * class to indicate something. In case you overload traceCAD, do
+     * whatever is appropriate on _PING. It's just an offer to perform
+     * logging or actions here, and not in the traced code.
+     *
+     * But traceCAD may be overloaded to produce more stats.
+     *
+     * \see \c Example.COW_debug.cc.
+     */
+    template<class _Tp>
+      struct TraceCAD : public base::ProvideNumericId<TraceCAD<_Tp>, unsigned long>
+                      , public TraceCADBase
+      {
+        static unsigned long & _totalTraceCAD()
+        { static unsigned long _val = 0;
+          return _val; }
+
+        TraceCAD()
+        { _ident = __PRETTY_FUNCTION__;
+          ++_totalTraceCAD();
+          traceCAD( CTOR, *this, *this ); }
+
+        TraceCAD( const TraceCAD & rhs )
+        { ++_totalTraceCAD();
+          traceCAD( COPYCTOR, *this, rhs ); }
+
+        TraceCAD & operator=( const TraceCAD & rhs )
+        { traceCAD( ASSIGN, *this, rhs ); return *this; }
+
+        virtual ~TraceCAD()
+        { --_totalTraceCAD();
+          traceCAD( DTOR, *this, *this ); }
+
+        void _PING() const
+        { traceCAD( PING, *this, *this ); }
+      };
+
+    /** \relates TraceCAD Stream output. */
+    template<class _Tp>
+      inline std::ostream & operator<<( std::ostream & str, const TraceCAD<_Tp> & obj )
+      { return str << "(ID " << obj.numericId() << ", TOTAL " << obj._totalTraceCAD()
+                   << ") [" << &obj << "] "; }
+
+    /** Drop a log line about the traced method. Overload to
+     * fit your needs.
+    */
+    template<class _Tp>
+      void traceCAD( TraceCADBase::What what_r,
+                     const TraceCAD<_Tp> & self_r,
+                     const TraceCAD<_Tp> & rhs_r )
+      {
+        switch( what_r )
+          {
+          case TraceCADBase::CTOR:
+          case TraceCADBase::PING:
+          case TraceCADBase::DTOR:
+            _DBG("DEBUG") << what_r << self_r << " (" << self_r._ident << ")" << std::endl;
+            break;
+
+          case TraceCADBase::COPYCTOR:
+          case TraceCADBase::ASSIGN:
+            _DBG("DEBUG") << what_r << self_r << "( " << rhs_r << ")" << " (" << self_r._ident << ")" << std::endl;
+            break;
+          }
+      }
+    //@}
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace debug
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_DEBUG_H
diff --git a/zypp/base/DefaultIntegral.h b/zypp/base/DefaultIntegral.h
new file mode 100644 (file)
index 0000000..4ddf774
--- /dev/null
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/DefaultIntegral.h
+ *
+*/
+#ifndef ZYPP_BASE_DEFAULTINTEGRAL_H
+#define ZYPP_BASE_DEFAULTINTEGRAL_H
+
+#include <iosfwd>
+#include <boost/static_assert.hpp>
+#include <boost/type_traits/is_integral.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : DefaultIntegral<_Tp,_Initial>
+  //
+  /** Integral type with defined initial value when default constructed.
+   *
+   * \code
+   * typedef DefaultIntegral<unsigned,0> Counter;
+   * std::map<KeyType,Counter> stats;
+   * for ( all keys  )
+   *   ++(stats[key]);
+   * \endcode
+   *
+   * \todo maybe specialize for bool, add logical and bit operators
+   * \todo let _Initial default to 0 then remove base/Counter.h
+  */
+  template<class _Tp, _Tp _Initial>
+    class DefaultIntegral
+    {
+    public:
+      typedef _Tp value_type;
+
+    public:
+      DefaultIntegral( _Tp val_r = _Initial )
+      : _val( val_r )
+      { BOOST_STATIC_ASSERT(boost::is_integral<_Tp>::value); }
+
+      /** Conversion to _Tp. */
+      //@{
+      _Tp & get()       { return _val; }
+      _Tp   get() const { return _val; }
+
+      operator _Tp &()       { return get(); }
+      operator _Tp  () const { return get(); }
+      //@}
+
+      /** Reset to the defined initial value. */
+      DefaultIntegral & reset()        { _val = _Initial; return *this; }
+
+      /** \name Arithmetic operations.
+       * \c + \c - \c * \c / are provided via conversion to _Tp.
+      */
+      //@{
+      DefaultIntegral & operator=( _Tp rhs )  {  _val = rhs; return *this; }
+      DefaultIntegral & operator+=( _Tp rhs ) { _val += rhs; return *this; }
+      DefaultIntegral & operator-=( _Tp rhs ) { _val -= rhs; return *this; }
+      DefaultIntegral & operator*=( _Tp rhs ) { _val *= rhs; return *this; }
+      DefaultIntegral & operator/=( _Tp rhs ) { _val /= rhs; return *this; }
+
+      DefaultIntegral & operator++(/*prefix*/) { ++_val; return *this; }
+      DefaultIntegral & operator--(/*prefix*/) { ++_val; return *this; }
+
+      DefaultIntegral operator++(int/*postfix*/) { return _val++; }
+      DefaultIntegral operator--(int/*postfix*/) { return _val--; }
+      //@}
+
+    private:
+      _Tp _val;
+    };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_DEFAULTINTEGRAL_H
diff --git a/zypp/base/Deprecated.h b/zypp/base/Deprecated.h
new file mode 100644 (file)
index 0000000..9c551a0
--- /dev/null
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Deprecated.h
+ *  \brief     Provides the ZYPP_DEPRECATED macro.
+ */
+
+/**
+ * The ZYPP_DEPRECATED macro can be used to trigger compile-time warnings
+ * with gcc >= 3.2 when deprecated functions are used.
+ *
+ * For non-inline functions, the macro is used at the very end of the
+ * function declaration, right before the semicolon, unless it's pure
+ * virtual:
+ *
+ * int deprecatedFunc() const ZYPP_DEPRECATED;
+ * virtual int deprecatedPureVirtualFunc() const ZYPP_DEPRECATED = 0;
+ *
+ * Functions which are implemented inline are handled differently:
+ * the ZYPP_DEPRECATED macro is used at the front, right before the
+ * return type, but after "static" or "virtual":
+ *
+ * ZYPP_DEPRECATED void deprecatedFuncA() { .. }
+ * virtual ZYPP_DEPRECATED int deprecatedFuncB() { .. }
+ * static  ZYPP_DEPRECATED bool deprecatedFuncC() { .. }
+ *
+ * You can also mark whole structs or classes as deprecated, by inserting
+ * the ZYPP_DEPRECATED macro after the struct/class keyword, but before
+ * the name of the struct/class:
+ *
+ * class ZYPP_DEPRECATED DeprecatedClass { };
+ * struct ZYPP_DEPRECATED DeprecatedStruct { };
+ *
+ * However, deprecating a struct/class doesn't create a warning for gcc
+ * versions <= 3.3 (haven't tried 3.4 yet).  If you want to deprecate a class,
+ * also deprecate all member functions as well (which will cause warnings).
+ *
+ */
+#if __GNUC__ - 0 > 3 || (__GNUC__ - 0 == 3 && __GNUC_MINOR__ - 0 >= 2)
+#ifndef ZYPP_DEPRECATED
+#define ZYPP_DEPRECATED __attribute__ ((deprecated))
+#endif
+#else
+#ifndef ZYPP_DEPRECATED
+#define ZYPP_DEPRECATED
+#endif
+#endif
+
diff --git a/zypp/base/DtorReset.h b/zypp/base/DtorReset.h
new file mode 100644 (file)
index 0000000..48e8fdf
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/DtorReset.h
+ *
+*/
+#ifndef ZYPP_BASE_DTORRESET_H
+#define ZYPP_BASE_DTORRESET_H
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : DtorReset
+  //
+  /** Assign a vaiable a certain value when going out of scope.
+   * Use it e.g. to reset/cleanup in presence of exceptions.
+   * \code
+   * struct Foo
+   * {
+   *   void consume()
+   *   {
+   *     DtorReset x(_inConsume,false);
+   *     _inConsume = true;
+   *     MIL << _inConsume << endl;
+   *   };
+   *
+   *   DefaultIntegral<bool,false> _inConsume;
+   * };
+   *
+   * Foo f;
+   * MIL << f._inConsume << endl; // 0
+   * f.consume();                 // 1
+   * MIL << f._inConsume << endl; // 0
+   * \endcode
+   * \ingroup g_RAII
+   * \todo Check if using call_traits enables 'DtorReset(std::string,"value")',
+   * as this currently would require assignment of 'char[]'.
+   */
+  class DtorReset
+  {
+  public:
+    template<class _Var>
+      DtorReset( _Var & var_r )
+      : _pimpl( new Impl<_Var,_Var>( var_r, var_r ) )
+      {}
+    template<class _Var, class _Val>
+      DtorReset( _Var & var_r, const _Val & val_r )
+      : _pimpl( new Impl<_Var,_Val>( var_r, val_r ) )
+      {}
+
+  private:
+    /** Requires _Val being copy constructible, and assignment
+     * <tt>_Var = _Val</tt> defined. */
+    template<class _Var, class _Val>
+      struct Impl
+      {
+        Impl( _Var & var_r, const _Val & val_r )
+        : _var( var_r )
+        , _val( val_r )
+        {}
+        ~Impl()
+        { _var = _val; }
+        _Var & _var;
+        _Val   _val;
+      };
+    shared_ptr<void> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_DTORRESET_H
diff --git a/zypp/base/Easy.h b/zypp/base/Easy.h
new file mode 100644 (file)
index 0000000..4a565ce
--- /dev/null
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Easy.h
+ *
+*/
+#ifndef ZYPP_BASE_EASY_H
+#define ZYPP_BASE_EASY_H
+
+/** Convenient for-loops using iterator.
+ * \code
+ *  std::set&lt;std::string&gt; _store;
+ *  for_( it, _store.begin(), _store.end() )
+ *  {
+ *    cout << *it << endl;
+ *  }
+ * \endcode
+*/
+#define for_(IT,BEG,END) for ( typeof(BEG) IT = BEG, _for_end = END; IT != _for_end; ++IT )
+#define for_each_(IT,CONT) for_( IT, CONT.begin(), CONT.end() )
+
+/** Simple C-array iterator
+ * \code
+ *  const char * defstrings[] = { "",  "a", "default", "two words" };
+ *  for_( it, arrayBegin(defstrings), arrayEnd(defstrings) )
+ *    cout << *it << endl;
+ * \endcode
+*/
+#define arrayBegin(A) (&A[0])
+#define arraySize(A)  (sizeof(A)/sizeof(*A))
+#define arrayEnd(A)   (&A[0] + arraySize(A))
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_EASY_H
diff --git a/zypp/base/Errno.h b/zypp/base/Errno.h
new file mode 100644 (file)
index 0000000..220ab64
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Errno.h
+ *
+*/
+#ifndef ZYPP_BASE_ERRNO_H
+#define ZYPP_BASE_ERRNO_H
+
+#include <cerrno>
+#include <iosfwd>
+
+#include <zypp/base/String.h>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Convenience \c errno wrapper. */
+  class Errno
+  {
+    public:
+      /** Default ctor: \c errno */
+      Errno() : _errno( errno ) {}
+
+      /** Ctor set to \c errno if error condition, else \c 0.
+       * \code
+       *  int ret = ::write( fd, buffer, size );
+       *  DBG << "write returns: " << Errno( ret != size ) << end;
+       *  // on success:    "write returns: [0-Success]"
+       *  // on error e.g.: "write returns: [11-Resource temporarily unavailable]"
+       * \endcode
+       */
+      Errno( bool error_r ) : _errno( error_r ? errno : 0 ) {}
+
+      /** Ctor taking an explicit errno value. */
+      Errno( int errno_r ) : _errno( errno_r ) {}
+
+    public:
+      /** Return the stored errno. */
+      int get() const { return _errno; }
+
+      /** Allow implicit conversion to \c int. */
+      operator int() const { return get(); }
+
+      /** Return human readable error string. */
+      std::string asString() const { return str::form( "[%d-%s]", _errno, ::strerror(_errno) ); }
+
+    private:
+      int _errno;
+  };
+
+  /** \relates Errno Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const Errno & obj )
+  { return str << obj.asString(); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_ERRNO_H
diff --git a/zypp/base/Exception.cc b/zypp/base/Exception.cc
new file mode 100644 (file)
index 0000000..86f7b51
--- /dev/null
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Exception.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Exception.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace exception_detail
+  { /////////////////////////////////////////////////////////////////
+
+    std::string CodeLocation::asString() const
+    {
+      return str::form( "%s(%s):%u",
+                        _file.c_str(),
+                        _func.c_str(),
+                        _line );
+    }
+
+    std::ostream & operator<<( std::ostream & str, const CodeLocation & obj )
+    { return str << obj.asString(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace exception_detail
+  ///////////////////////////////////////////////////////////////////
+
+  Exception::Exception()
+  {}
+
+  Exception::Exception( const std::string & msg_r )
+  : _msg( msg_r )
+  {}
+
+  Exception::Exception( const std::string & msg_r, const Exception & history_r )
+  : _msg( msg_r )
+  { remember( history_r ); }
+
+  Exception::~Exception() throw()
+  {}
+
+  std::string Exception::asString() const
+  {
+    std::ostringstream str;
+    dumpOn( str );
+    return str.str();
+  }
+
+  std::string Exception::asUserString() const
+  {
+    std::ostringstream str;
+    dumpOn( str );
+    // call gettext to translate the message. This will
+    // not work if dumpOn() uses composed messages.
+    return _(str.str().c_str());
+  }
+
+  std::string Exception::asUserHistory() const
+  {
+    if ( historyEmpty() )
+      return asUserString();
+
+    std::string ret( asUserString() );
+    if ( ret.empty() )
+      return historyAsString();
+
+    ret += '\n';
+    ret += historyAsString();
+    return ret;
+  }
+
+  void Exception::remember( const Exception & old_r )
+  {
+    if ( &old_r != this ) // no self-remember
+    {
+      History newh( old_r._history.begin(), old_r._history.end() );
+      newh.push_front( old_r.asUserString() );
+      _history.swap( newh );
+    }
+  }
+
+  void Exception::addHistory( const std::string & msg_r )
+  {
+    _history.push_front( msg_r );
+  }
+
+  std::string Exception::historyAsString() const
+  {
+    // TranslatorExplanation followed by the list of error messages that lead to this exception
+    std::string history( _("History:") );
+    std::ostringstream ret;
+    dumpRange( ret, historyBegin(), historyEnd(),
+               "", history+"\n - ", "\n - ", "\n", "" );
+    return ret.str();
+  }
+
+  std::ostream & Exception::dumpOn( std::ostream & str ) const
+  { return str << _msg; }
+
+  std::ostream & Exception::dumpError( std::ostream & str ) const
+  { return dumpOn( str << _where << ": " ); }
+
+  std::ostream & operator<<( std::ostream & str, const Exception & obj )
+  { return obj.dumpError( str ); }
+
+
+  std::string Exception::strErrno( int errno_r )
+  { return str::strerror( errno_r ); }
+
+  std::string Exception::strErrno( int errno_r, const std::string & msg_r )
+  {
+    std::string ret( msg_r );
+    ret += ": ";
+    return ret += strErrno( errno_r );
+  }
+
+  void Exception::log( const Exception & excpt_r, const CodeLocation & where_r,
+                       const char *const prefix_r )
+  {
+    INT << where_r << " " << prefix_r << " " << excpt_r.asUserHistory() << endl;
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/Exception.h b/zypp/base/Exception.h
new file mode 100644 (file)
index 0000000..4d0f5be
--- /dev/null
@@ -0,0 +1,356 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Exception.h
+ *
+*/
+#ifndef ZYPP_BASE_EXCEPTION_H
+#define ZYPP_BASE_EXCEPTION_H
+
+#include <iosfwd>
+#include <string>
+#include <list>
+#include <stdexcept>
+
+#include "zypp/base/Errno.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace exception_detail
+  { /////////////////////////////////////////////////////////////////
+
+    /** Keep _FILE_, _FUNCTION_ and _LINE_.
+     * Construct it using the \ref ZYPP_EX_CODELOCATION macro.
+    */
+    struct CodeLocation
+    {
+      friend std::ostream & operator<<( std::ostream & str, const CodeLocation & obj );
+
+      /** Ctor */
+      CodeLocation()
+      : _line( 0 )
+      {}
+
+      /** Ctor */
+      CodeLocation( const std::string & file_r,
+                    const std::string & func_r,
+                    unsigned            line_r )
+      : _file( file_r ), _func( func_r ), _line( line_r )
+      {}
+
+      /** Location as string */
+      std::string asString() const;
+
+    private:
+      std::string _file;
+      std::string _func;
+      unsigned    _line;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** Create CodeLocation object storing the current location. */
+    //#define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(__FILE__,__FUNCTION__,__LINE__)
+#define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(( *__FILE__ == '/' ? strrchr( __FILE__, '/' ) + 1 : __FILE__ ),__FUNCTION__,__LINE__)
+
+    /** \relates CodeLocation Stream output */
+    std::ostream & operator<<( std::ostream & str, const CodeLocation & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace exception_detail
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Exception
+  /** Base class for Exception.
+   *
+   * Exception offers to store a message string passed to the ctor.
+   * Derived classes may provide additional information. Overload
+   * \ref dumpOn to provide a proper error text.
+   *
+   * \li Use \ref ZYPP_THROW to throw exceptions.
+   * \li Use \ref ZYPP_CAUGHT If you caught an exceptions in order to handle it.
+   * \li Use \ref ZYPP_RETHROW to rethrow a caught exception.
+   *
+   * The use of these macros is not mandatory. but \c ZYPP_THROW and
+   * \c ZYPP_RETHROW will adjust the code location information stored in
+   * the Exception. All three macros will drop a line in the logfile.
+
+   * \code
+   *  43   try
+   *  44     {
+   *  45       try
+   *  46         {
+   *  47           ZYPP_THROW( Exception("Something bad happened.") );
+   *  48         }
+   *  49       catch ( Exception & excpt )
+   *  50         {
+   *  51           ZYPP_RETHROW( excpt );
+   *  52         }
+   *  53
+   *  54     }
+   *  55   catch ( Exception & excpt )
+   *  56     {
+   *  57       ZYPP_CAUGHT( excpt );
+   *  58     }
+   * \endcode
+   * The above produces the following log lines:
+   * \code
+   *  Main.cc(main):47 THROW:    Main.cc(main):47: Something bad happened.
+   *  Main.cc(main):51 RETHROW:  Main.cc(main):47: Something bad happened.
+   *  Main.cc(main):57 CAUGHT:   Main.cc(main):51: Something bad happened.
+   * \endcode
+   *
+   *
+   * Class Exception now offers a history list of message strings.
+   * These messages should describe what lead to the exception.
+   *
+   * The Exceptions message itself is NOT included in the history.
+   *
+   * Rethrow, remembering an old exception:
+   * \code
+   * try
+   * {
+   *   ....
+   * }
+   * catch( const Exception & olderr_r )
+   * {
+   *    ZYPP_CAUGHT( olderr_r )
+   *    HighLevelException newerr( "Something failed." );
+   *    newerr.rember( olderr_r );
+   *    ZYPP_THROW( newerr );
+   * }
+   * \endcode
+   *
+   * Print an Exception followed by it's history if available:
+   * \code
+   * Exception error;
+   * ERR << error << endl << error.historyAsString();
+   * \endcode
+   *
+   * \todo That's a draft to have a common way of throwing exceptions.
+   * Most probabely we'll finally use blocxx exceptions. Here, but not
+   * in the remaining code of zypp. If we can we should try to wrap
+   * the blocxx macros and typedef the classes in here.
+   **/
+  class Exception : public std::exception
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Exception & obj );
+
+  public:
+    typedef exception_detail::CodeLocation CodeLocation;
+    typedef std::list<std::string>         History;
+    typedef History::const_iterator        HistoryIterator;
+    typedef History::size_type             HistorySize;
+
+    /** Default ctor.
+     * Use \ref ZYPP_THROW to throw exceptions.
+    */
+    Exception();
+
+    /** Ctor taking a message.
+     * Use \ref ZYPP_THROW to throw exceptions.
+    */
+    Exception( const std::string & msg_r );
+
+    /** Ctor taking a message and an exception to remember as history
+     * \see \ref remember
+     * Use \ref ZYPP_THROW to throw exceptions.
+    */
+    Exception( const std::string & msg_r, const Exception & history_r );
+
+    /** Dtor. */
+    virtual ~Exception() throw();
+
+    /** Return CodeLocation. */
+    const CodeLocation & where() const
+    { return _where; }
+
+    /** Exchange location on rethrow. */
+    void relocate( const CodeLocation & where_r ) const
+    { _where = where_r; }
+
+    /** Return the message string provided to the ctor.
+     * \note This is not necessarily the complete error message.
+     * The whole error message is provided by \ref asString or
+     * \ref dumpOn.
+    */
+    const std::string & msg() const
+    { return _msg; }
+
+    /** Error message provided by \ref dumpOn as string. */
+    std::string asString() const;
+
+    /** Translated error message as string suitable for the user.
+     * \see \ref asUserStringHistory
+    */
+    std::string asUserString() const;
+
+  public:
+    /** \name History list of message strings.
+     * Maintain a simple list of individual error messages, that lead
+     * to this Exception. The Exceptions message itself is not included
+     * in the history. The History list stores the most recent message
+     * fist.
+     */
+    //@{
+
+    /** Store an other Exception as history. */
+    void remember( const Exception & old_r );
+
+    /** Add some message text to the history. */
+    void addHistory( const std::string & msg_r );
+
+    /** Iterator pointing to the most recent message. */
+    HistoryIterator historyBegin() const
+    { return _history.begin(); }
+
+    /** Iterator pointing behind the last message. */
+    HistoryIterator historyEnd() const
+    { return _history.end(); }
+
+    /** Whether the history list is empty. */
+    bool historyEmpty() const
+    { return _history.empty(); }
+
+    /** The size of the history list. */
+    HistorySize historySize() const
+    { return _history.size(); }
+
+    /** The history as string. Empty if \ref historyEmpty.
+     * Otherwise:
+     * \code
+     * History:
+     *  - most recent message
+     *  - 2nd message
+     * ...
+     *  - oldest message
+     * \endcode
+    */
+    std::string historyAsString() const;
+
+    /** A single (multiline) string composed of \ref asUserString  and  \ref historyAsString. */
+    std::string asUserHistory() const;
+    //@}
+
+  protected:
+
+    /** Overload this to print a proper error message. */
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+  public:
+     /** Make a string from \a errno_r. */
+    static std::string strErrno( int errno_r );
+     /** Make a string from \a errno_r and \a msg_r. */
+    static std::string strErrno( int errno_r, const std::string & msg_r );
+
+  public:
+    /** Drop a logline on throw, catch or rethrow.
+     * Used by \ref ZYPP_THROW macros.
+    */
+    static void log( const Exception & excpt_r, const CodeLocation & where_r,
+                     const char *const prefix_r );
+
+  private:
+    mutable CodeLocation _where;
+    std::string          _msg;
+    History              _history;
+
+    /** Return message string. */
+    virtual const char * what() const throw()
+    { return _msg.c_str(); }
+
+    /** Called by <tt>std::ostream & operator\<\<</tt>.
+     * Prints \ref CodeLocation and the error message provided by
+     * \ref dumpOn.
+    */
+    std::ostream & dumpError( std::ostream & str ) const;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates Exception Stream output */
+  std::ostream & operator<<( std::ostream & str, const Exception & obj );
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** Helper for \ref ZYPP_THROW. */
+  template<class _Excpt>
+    void _ZYPP_THROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r ) __attribute__((noreturn));
+  template<class _Excpt>
+    void _ZYPP_THROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    {
+      excpt_r.relocate( where_r );
+      Exception::log( excpt_r, where_r, "THROW:   " );
+      throw( excpt_r );
+    }
+
+  /** Helper for \ref ZYPP_THROW. */
+  template<class _Excpt>
+    void _ZYPP_CAUGHT( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    {
+      Exception::log( excpt_r, where_r, "CAUGHT:  " );
+    }
+
+  /** Helper for \ref ZYPP_THROW. */
+  template<class _Excpt>
+    void _ZYPP_RETHROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r ) __attribute__((noreturn));
+  template<class _Excpt>
+    void _ZYPP_RETHROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    {
+      Exception::log( excpt_r, where_r, "RETHROW: " );
+      excpt_r.relocate( where_r );
+      throw;
+    }
+
+  ///////////////////////////////////////////////////////////////////
+
+  /** \defgroup ZYPP_THROW ZYPP_THROW macros
+   * Macros for throwing Exception.
+   * \see \ref zypp::Exception for an example.
+  */
+  //@{
+  /** Drops a logline and throws the Exception. */
+#define ZYPP_THROW(EXCPT)\
+  _ZYPP_THROW( EXCPT, ZYPP_EX_CODELOCATION )
+
+  /** Drops a logline telling the Exception was caught (in order to handle it). */
+#define ZYPP_CAUGHT(EXCPT)\
+  _ZYPP_CAUGHT( EXCPT, ZYPP_EX_CODELOCATION )
+
+  /** Drops a logline and rethrows, updating the CodeLocation. */
+#define ZYPP_RETHROW(EXCPT)\
+  _ZYPP_RETHROW( EXCPT, ZYPP_EX_CODELOCATION )
+
+
+  /** Throw Exception built from a message string. */
+#define ZYPP_THROW_MSG(EXCPTTYPE, MSG)\
+  ZYPP_THROW( EXCPTTYPE( MSG ) )
+
+  /** Throw Exception built from errno. */
+#define ZYPP_THROW_ERRNO(EXCPTTYPE)\
+  ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(errno) ) )
+
+  /** Throw Exception built from errno provided as argument. */
+#define ZYPP_THROW_ERRNO1(EXCPTTYPE, ERRNO)\
+  ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(ERRNO) ) )
+
+  /** Throw Exception built from errno and a message string. */
+#define ZYPP_THROW_ERRNO_MSG(EXCPTTYPE, MSG)\
+  ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(errno,MSG) ) )
+
+  /** Throw Exception built from errno provided as argument and a message string */
+#define ZYPP_THROW_ERRNO_MSG1(EXCPTTYPE, ERRNO,MSG)\
+  ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(ERRNO,MSG) ) )
+  //@}
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_EXCEPTION_H
diff --git a/zypp/base/ExternalDataSource.cc b/zypp/base/ExternalDataSource.cc
new file mode 100644 (file)
index 0000000..d503ba3
--- /dev/null
@@ -0,0 +1,159 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/ExternalDataSource.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#define _GNU_SOURCE 1 // for ::getline
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <iostream>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sstream>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/ExternalDataSource.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace externalprogram {
+
+    ExternalDataSource::ExternalDataSource (FILE *ifile, FILE *ofile)
+      : inputfile (ifile),
+        outputfile (ofile),
+        linebuffer (0),
+        linebuffer_size (0)
+    {
+    }
+
+
+    ExternalDataSource::~ExternalDataSource ()
+    {
+      if (linebuffer)
+       free (linebuffer);
+      close ();
+    }
+
+
+    bool
+    ExternalDataSource::send (const char *buffer, size_t length)
+    {
+      if (outputfile) {
+       bool success = fwrite (buffer, length, 1, outputfile) != 0;
+       fflush (outputfile);
+       return success;
+      }
+      else
+       return false;
+    }
+
+
+    bool
+    ExternalDataSource::send (std::string s)
+    {
+      DBG << "send (" << s << ")";
+      return send(s.data(), s.length());
+    }
+
+
+    string
+    ExternalDataSource::receiveUpto (char c)
+    {
+      if (inputfile && !feof(inputfile))
+      {
+       std::ostringstream datas;
+        while ( true )
+        {
+          int readc = fgetc(inputfile);
+          if (readc == EOF) break;
+          datas << (char)readc;
+          if ((char)readc == c) break;
+        }
+        return datas.str();
+      }
+      return string();
+    }
+
+
+    size_t
+    ExternalDataSource::receive (char *buffer, size_t length)
+    {
+      if (inputfile)
+       return fread (buffer, 1, length, inputfile);
+      else
+       return 0;
+    }
+
+    void ExternalDataSource::setBlocking(bool mode)
+    {
+      if(!inputfile) return;
+
+      int fd = ::fileno(inputfile);
+
+      if(fd == -1)
+       { ERR << strerror(errno) << endl; return; }
+
+      int flags = ::fcntl(fd,F_GETFL);
+
+      if(flags == -1)
+       { ERR << strerror(errno) << endl; return; }
+
+      if(!mode)
+       flags = flags | O_NONBLOCK;
+      else if(flags & O_NONBLOCK)
+       flags = flags ^ O_NONBLOCK;
+
+      flags = ::fcntl(fd,F_SETFL,flags);
+
+      if(flags == -1)
+       { ERR << strerror(errno) << endl; return; }
+    }
+
+    string
+    ExternalDataSource::receiveLine()
+    {
+      if (inputfile)
+      {
+       ssize_t nread = getline (&linebuffer, &linebuffer_size, inputfile);
+       if (nread == -1)
+           return "";
+       else
+           return string (linebuffer, nread);
+      }
+      else
+       return "";
+    }
+
+
+    int
+    ExternalDataSource::close ()
+    {
+      if (inputfile && inputfile != outputfile)
+       fclose (inputfile);
+      if (outputfile)
+       fclose (outputfile);
+      inputfile = 0;
+      outputfile = 0;
+      return 0;
+    }
+
+
+  } // namespace externalprogram
+} // namespace zypp
+
diff --git a/zypp/base/ExternalDataSource.h b/zypp/base/ExternalDataSource.h
new file mode 100644 (file)
index 0000000..ffdf502
--- /dev/null
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/ExternalDataSource.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#ifndef ZYPP_EXTERNALDATASOURCE_H
+#define ZYPP_EXTERNALDATASOURCE_H
+
+#include <stdio.h>
+
+#include <string>
+
+namespace zypp {
+  namespace externalprogram {
+
+    /**
+     * @short Bidirectional stream to external data
+     */
+    class ExternalDataSource
+    {
+    protected:
+      FILE *inputfile;
+      FILE *outputfile;
+    
+    private:
+      char *linebuffer;
+      size_t linebuffer_size;
+    
+    public:
+      /**
+       * Create a new instance.
+       * @param inputfile The stream for reading
+       * @param outputfile The stream for writing
+       * Either can be NULL if no reading/writing is allowed.
+       */
+      ExternalDataSource(FILE *inputfile = 0, FILE *outputfile = 0);
+    
+      /**
+       * Implicitly close the connection.
+       */
+      virtual ~ExternalDataSource();
+    
+      /**
+       * Send some data to the output stream.
+       * @param buffer The data to send
+       * @param length The size of it
+       */
+      bool send (const char *buffer, size_t length);
+    
+      /**
+       * Send some data down the stream.
+       * @param string The data to send
+       */
+      bool send (std::string s);
+    
+      /**
+       * Read some data from the input stream.
+       * @param buffer Where to put the data
+       * @param length How much to read at most
+       * Returns the amount actually received
+       */
+      size_t receive(char *buffer, size_t length);
+    
+      /**
+       * Read one line from the input stream.
+       * Returns the line read, including the terminator.
+       */
+      std::string receiveLine();
+    
+      /**
+       * Read characters into a string until character c is
+       * read. C is put at the end of the string.
+       */
+      std::string receiveUpto(char c);
+      /**
+       * Set the blocking mode of the input stream.
+       * @param mode True if the reader should be blocked waiting for input.
+       * This is the initial default.
+       */
+      void setBlocking(bool mode);
+    
+      /**
+       * Close the input and output streams.
+       */
+      virtual int close();
+    
+      /**
+       * Return the input stream.
+       */
+      FILE *inputFile() const  { return inputfile; }
+    
+      /**
+       * Return the output stream.
+       */
+      FILE *outputFile() const { return outputfile; }
+    };
+
+  } // namespace externalprogram
+} // namespace zypp
+
+#endif // ZYPP_EXTERNALDATASOURCE_H
+
diff --git a/zypp/base/Fd.cc b/zypp/base/Fd.cc
new file mode 100644 (file)
index 0000000..29887ea
--- /dev/null
@@ -0,0 +1,63 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Fd.cc
+ *
+*/
+extern "C"
+{
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+}
+
+#include <iostream>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/Fd.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Fd::Fd
+    // METHOD TYPE : Ctor
+    //
+    Fd::Fd( const Pathname & file_r, int open_flags, mode_t mode )
+    : m_fd( -1 )
+    {
+      m_fd = open( file_r.asString().c_str(), open_flags, mode );
+      if ( m_fd == -1 )
+        ZYPP_THROW_ERRNO_MSG( Exception, std::string("open ")+file_r.asString() );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Fd::close
+    // METHOD TYPE : void
+    //
+    void Fd::close()
+    {
+      if ( m_fd != -1 )
+        {
+          ::close( m_fd );
+          m_fd = -1;
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/Fd.h b/zypp/base/Fd.h
new file mode 100644 (file)
index 0000000..0cca746
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Fd.h
+ *
+*/
+#ifndef ZYPP_BASE_FD_H
+#define ZYPP_BASE_FD_H
+
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Fd
+    //
+    /** Assert \c close called on open filedescriptor.
+     * \code
+     * ...
+     * scoped_ptr<Fd> fd; // calls close when going out of scope
+     * try {
+     *   fd.reset( new Fd( "/some/file" ) );
+     * } catch ( ... ) {
+     *   // open failed.
+     * }
+     * read( fd->fd(), ... ),
+     * \endcode
+     *
+     * \ingroup g_RAII
+     * \todo It's dumb. Openflags and more related functions (read/write..)
+     * could be added.
+    */
+    class Fd
+    {
+    public:
+      /** Ctor opens file.
+       * \throw EXCEPTION If open fails.
+      */
+      Fd( const Pathname & file_r, int open_flags, mode_t mode = 0 );
+
+      /** Dtor closes file. */
+      ~Fd()
+      { close(); }
+
+      /** Explicitly close the file. */
+      void close();
+
+      /** Test for valid filedescriptor. */
+      bool isOpen() const
+      { return m_fd != -1; }
+
+      /** Return the filedescriptor. */
+      int fd() const
+      { return m_fd; }
+
+    private:
+      /** The filedescriptor. */
+      int m_fd;
+      /** No copy. */
+      Fd( const Fd & );
+      /** No assign. */
+      Fd & operator=( const Fd & );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_FD_H
diff --git a/zypp/base/Flags.h b/zypp/base/Flags.h
new file mode 100644 (file)
index 0000000..6547d6a
--- /dev/null
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Flags.h
+ *
+*/
+#ifndef ZYPP_BASE_FLAGS_H
+#define ZYPP_BASE_FLAGS_H
+
+#include "zypp/base/String.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Flags<Enum>
+    //
+    /** A type-safe way of storing OR-combinations of enum values (like QTs QFlags).
+     * \see <a href="http://doc.trolltech.com/4.1/qflags.html">QFlags Class Reference</a>
+     * \code
+     *  class RpmDb
+     *  {
+     *    public:
+     *      enum DbStateInfoBits {
+     *        DbSI_NO_INIT     = 0x0000,
+     *        DbSI_HAVE_V4     = 0x0001,
+     *        DbSI_MADE_V4     = 0x0002,
+     *        DbSI_MODIFIED_V4 = 0x0004,
+     *        DbSI_HAVE_V3     = 0x0008,
+     *        DbSI_HAVE_V3TOV4 = 0x0010,
+     *        DbSI_MADE_V3TOV4 = 0x0020
+     *      };
+     *
+     *      ZYPP_DECLARE_FLAGS(DbStateInfo,DbStateInfoBits);
+     *  };
+     *  ZYPP_DECLARE_OPERATORS_FOR_FLAGS(RpmDb::DbStateInfo);
+     *
+     *  ...
+     *  enum Other { OTHERVAL = 13 };
+     *  {
+     *    XRpmDb::DbStateInfo s;
+     *    s = XRpmDb::DbSI_MODIFIED_V4|XRpmDb::DbSI_HAVE_V4;
+     *    // s |= OTHERVAL; // As desired: it does not compile
+     *  }
+     * \endcode
+     */
+    template<typename Enum>
+    class Flags
+    {
+      public:
+        typedef Enum enum_type;
+
+      public:
+        Flags()                               : _val( 0 ) {}
+        Flags( Enum flag_r )                  : _val( flag_r ) {}
+        Flags( unsigned flag_r )              : _val( flag_r ) {}
+
+        Flags & operator&=( Flags rhs )       { _val &= rhs._val; return *this; }
+        Flags & operator&=( Enum rhs )        { _val &= rhs;      return *this; }
+
+        Flags & operator|=( Flags rhs )       { _val |= rhs._val; return *this; }
+        Flags & operator|=( Enum rhs )        { _val |= rhs;      return *this; }
+
+        Flags & operator^=( Flags rhs )       { _val ^= rhs._val; return *this; }
+        Flags & operator^=( Enum rhs )        { _val ^= rhs;      return *this; }
+
+      public:
+        operator unsigned() const             { return _val; }
+
+        Flags operator&( Flags rhs ) const    { return Flags( *this ) &= rhs; }
+        Flags operator&( Enum rhs ) const     { return Flags( *this ) &= rhs; }
+
+        Flags operator|( Flags rhs ) const    { return Flags( *this ) |= rhs; }
+        Flags operator|( Enum rhs ) const     { return Flags( *this ) |= rhs; }
+
+        Flags operator^( Flags rhs ) const    { return Flags( *this ) ^= rhs; }
+        Flags operator^( Enum rhs ) const     { return Flags( *this ) ^= rhs; }
+
+        Flags operator~() const               { return Flags( ~_val ); }
+
+      public:
+        Flags & setFlag( Enum flag_r, bool newval_r )
+        { return( newval_r ? setFlag(flag_r) : unsetFlag(flag_r) ); }
+
+        Flags & setFlag( Enum flag_r )           { _val |= flag_r; return *this; }
+        Flags & unsetFlag( Enum flag_r )         { _val &= ~flag_r; return *this; }
+
+        bool testFlag( Enum flag_r ) const    { return _val & flag_r; }
+
+      private:
+        unsigned _val;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    template<typename Enum>
+    inline std::ostream & operator<<( std::ostream & str, const Flags<Enum> & obj )
+    { return str << str::hexstring(obj); }
+
+    /** \relates Flags */
+#define ZYPP_DECLARE_FLAGS(Name,Enum) typedef zypp::base::Flags<Enum> Name
+
+    /** \relates Flags */
+#define ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Name) \
+inline Name operator|( Name::enum_type lhs, Name::enum_type rhs )    { return Name( lhs ) |= rhs; } \
+inline Name operator|( Name::enum_type lhs, Name rhs )               { return rhs |= lhs; }
+
+    /** \relates Flags */
+#define ZYPP_DECLARE_FLAGS_AND_OPERATORS(Name,Enum) \
+    ZYPP_DECLARE_FLAGS(Name,Enum); \
+    ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Name)
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_FLAGS_H
diff --git a/zypp/base/Function.h b/zypp/base/Function.h
new file mode 100644 (file)
index 0000000..95b1fe7
--- /dev/null
@@ -0,0 +1,78 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Function.h
+ *
+*/
+#ifndef ZYPP_BASE_FUNCTION_H
+#define ZYPP_BASE_FUNCTION_H
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/ref.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /* http://www.boost.org/doc/html/function.html
+
+   The Boost.Function library contains a family of class templates
+   that are function object wrappers. The notion is similar to a
+   generalized callback. It shares features with function pointers
+   in that both define a call interface (e.g., a function taking
+   two integer arguments and returning a floating-point value)
+   through which some implementation can be called, and the
+   implementation that is invoked may change throughout the
+   course of the program.
+
+   Generally, any place in which a function pointer would be used
+   to defer a call or make a callback, Boost.Function can be used
+   instead to allow the user greater flexibility in the implementation
+   of the target. Targets can be any 'compatible' function object
+   (or function pointer), meaning that the arguments to the interface
+   designated by Boost.Function can be converted to the arguments of
+   the target function object.
+  */
+  using boost::function;
+
+  /* http://www.boost.org/libs/bind/bind.html
+
+   boost::bind is a generalization of the standard functions std::bind1st
+   and std::bind2nd. It supports arbitrary function objects, functions,
+   function pointers, and member function pointers, and is able to bind
+   any argument to a specific value or route input arguments into arbitrary
+   positions. bind  does not place any requirements on the function object;
+   in particular, it does not need the result_type, first_argument_type and
+   second_argument_type  standard typedefs.
+  */
+  using boost::bind;
+
+  /* http://www.boost.org/doc/html/ref.html
+
+   The Ref library is a small library that is useful for passing references
+   to function templates (algorithms) that would usually take copies of their
+   arguments. It defines the class template boost::reference_wrapper<T>, the
+   two functions boost::ref and boost::cref that return instances of
+   boost::reference_wrapper<T>, and the two traits classes
+   boost::is_reference_wrapper<T>  and boost::unwrap_reference<T>.
+
+   The purpose of boost::reference_wrapper<T> is to contain a reference to an
+   object of type T. It is primarily used to "feed" references to function
+   templates (algorithms) that take their parameter by value.
+
+   To support this usage, boost::reference_wrapper<T> provides an implicit
+   conversion to T&. This usually allows the function templates to work on
+   references unmodified.
+  */
+  using boost::ref;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_FUNCTION_H
diff --git a/zypp/base/Functional.h b/zypp/base/Functional.h
new file mode 100644 (file)
index 0000000..9d6de50
--- /dev/null
@@ -0,0 +1,445 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Functional.h
+ *
+*/
+#ifndef ZYPP_BASE_FUNCTIONAL_H
+#define ZYPP_BASE_FUNCTIONAL_H
+
+#include <boost/functional.hpp>
+
+#include "zypp/base/Function.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /* http://www.boost.org/libs/functional/mem_fun.html
+
+   The header functional.hpp includes improved versions of
+   the full range of member function adapters from the
+   C++ Standard Library.
+  */
+  using boost::mem_fun;
+  using boost::mem_fun_ref;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace functor
+  { /////////////////////////////////////////////////////////////////
+
+    /** An unary functor forwarding to some other <tt>_Functor &</tt>.
+     * \ingroup g_Functor
+     *
+     * Most algorithms take functor arguments by value. That's inconvenient
+     * if the functor wants to collect and return data. Creating and
+     * passing a \ref FunctorRef to the algorithm, may help you out of this.
+     *
+     * \code
+     *   // Counts invokations of operator().
+     *   template<class _Tp>
+     *     struct Counter : public std::unary_function<_Tp, void>
+     *     {
+     *       void operator()( _Tp )
+     *       { ++_value; }
+     *
+     *       Counter() : _value( 0 ) {}
+     *
+     *       unsigned _value;
+     *     };
+     *
+     *   std::set<SomeType> c;
+     *   Counter<SomeType> counter;
+     *   // Invokations of FunctorRef are forwarded to counter:
+     *   std::for_each( c.begin, c.end(),
+     *                  // currently you must specify the
+     *                  // operator() signature:
+     *                  functorRef<void,SomeType>(counter)
+     *                );
+     * \endcode
+     *
+     * \note FunctorRef must be able to deduce the signature of
+     * \c _Functor::operator(). This is currently not automated,
+     * so you must specify the operator() signature as template
+     * arguments.
+     *
+     * \note The order is <result_type, arg1_type, ...> (this
+     * differs from std::, where the result comes last).
+     *
+     * \todo drop it an use boost::ref
+    */
+
+    /////////////////////////////////////////////////////////////////
+    namespace functor_detail
+    {
+      template <class _Functor, class res_type>
+        struct FunctorRef0
+        {
+          FunctorRef0( _Functor & f_r )
+          : _f( f_r )
+          {}
+
+          res_type operator()() const
+          {
+          return _f();
+          }
+
+        private:
+          _Functor & _f;
+        };
+
+      template <class _Functor, class res_type, class arg1_type>
+        struct FunctorRef1 : public std::unary_function<arg1_type, res_type>
+        {
+          FunctorRef1( _Functor & f_r )
+          : _f( f_r )
+          {}
+
+          res_type operator()( arg1_type a1 ) const
+          {
+            return _f( a1 );
+          }
+
+        private:
+          _Functor & _f;
+        };
+
+      template <class _Functor, class res_type, class arg1_type, class arg2_type>
+        struct FunctorRef2 : public std::binary_function<arg1_type, arg2_type, res_type>
+        {
+          FunctorRef2( _Functor & f_r )
+          : _f( f_r )
+          {}
+
+          res_type operator()( arg1_type a1, arg2_type a2 ) const
+          {
+            return _f( a1, a2 );
+          }
+
+        private:
+          _Functor & _f;
+        };
+
+      struct nil
+      {};
+    }
+    /////////////////////////////////////////////////////////////////
+
+    /** A binary \ref FunctorRef.
+     * Create it using \ref functorRef convenience function.
+    */
+    template <class _Functor, class res_type, class arg1_type = functor_detail::nil,
+                                              class arg2_type = functor_detail::nil>
+      struct FunctorRef
+      : public functor_detail::FunctorRef2<_Functor, res_type, arg1_type, arg2_type>
+      {
+        FunctorRef( _Functor & f_r )
+        : functor_detail::FunctorRef2<_Functor, res_type, arg1_type, arg2_type>( f_r )
+        {}
+      };
+
+    /** A unary \ref FunctorRef.
+     * Create it using \ref functorRef convenience function.
+    */
+    template <class _Functor, class res_type, class arg1_type>
+      struct FunctorRef<_Functor, res_type, arg1_type>
+      : public functor_detail::FunctorRef1<_Functor, res_type, arg1_type>
+      {
+        FunctorRef( _Functor & f_r )
+        : functor_detail::FunctorRef1<_Functor, res_type, arg1_type>( f_r )
+        {}
+      };
+
+    /** A nullary \ref FunctorRef.
+     * Create it using \ref functorRef convenience function.
+    */
+    template <class _Functor, class res_type>
+      struct FunctorRef<_Functor, res_type>
+      : public functor_detail::FunctorRef0<_Functor, res_type>
+      {
+        FunctorRef( _Functor & f_r )
+        : functor_detail::FunctorRef0<_Functor, res_type>( f_r )
+        {}
+      };
+
+    /** Convenience function creating a binary \ref FunctorRef. */
+    template <class res_type, class arg1_type, class arg2_type, class _Functor>
+      FunctorRef<_Functor, res_type, arg1_type, arg2_type>
+      functorRef( _Functor & f_r )
+      { return FunctorRef<_Functor, res_type, arg1_type, arg2_type>( f_r ); }
+    template <class res_type, class arg1_type, class _Functor>
+      FunctorRef<_Functor, res_type, arg1_type>
+      functorRef( _Functor & f_r )
+      { return FunctorRef<_Functor, res_type, arg1_type>( f_r ); }
+    template <class res_type, class _Functor>
+      FunctorRef<_Functor, res_type>
+      functorRef( _Functor & f_r )
+      { return FunctorRef<_Functor, res_type>( f_r ); }
+
+    /////////////////////////////////////////////////////////////////
+
+    /** \defgroup LOGICALFILTERS Functors for building compex queries.
+     * \ingroup g_Functor
+     *
+     * Some logical functors to build more complex queries:
+     *
+     * \li \ref True and \ref False. No supprise, they always return
+     *     \c true or \c false.
+     * \li \ref Not\<_Condition\>. _Condition is a functor, and
+     *     it's result is inverted.
+     * \li \ref Chain\<_ACondition,_BCondition\>. \c _ACondition and \c _BCondition
+     *     are functors, and Chain evaluates <tt>_ACondition && _BCondition</tt>.
+     *
+     * As it's no fun to get and write the correct template arguments,
+     * convenience functions creating the correct functor are provided.
+     *
+     * \li \c true_c and \c false_c. (provided just to match the schema)
+     * \li \c not_c. Takes a functor as argument and returns the appropriate
+     *     \ref Not functor.
+     * \li \c chain. Takes two functors and returns the appropriate
+     *     \ref Cain functor.
+     *
+     * \code
+     *  struct Print; // functor printing elements
+     *  struct Count; // functor counting number of elements
+     *
+     *  std::for_each( c.begin(), c.end(),
+     *                 chain( Print(), Count() ) );
+     * \endcode
+    */
+    //@{
+
+    /* functor that always returns a copied
+       value */
+    template<class T>
+    struct Constant
+    {
+      Constant( const T &value )
+        : _value(value)
+      {}
+
+      template<class _Tp>
+      T operator()( _Tp ) const
+      { return _value; }
+
+      T operator()() const
+      { return _value; }
+
+      T _value;
+    };
+
+    template<class T>
+    inline Constant<T> constant( const T &value )
+    { return Constant<T>(value); }
+
+    /** Logical functor always \c true.
+    */
+    struct True
+    {
+      template<class _Tp>
+        bool operator()( _Tp ) const
+        {
+          return true;
+        }
+    };
+
+    /** Convenience function for creating a True. */
+    inline True true_c()
+    { return True(); }
+
+    /** Logical functor always \c false.
+    */
+    struct False
+    {
+      template<class _Tp>
+        bool operator()( _Tp ) const
+        {
+          return false;
+        }
+    };
+
+    /** Convenience function for creating a False. */
+    inline False false_c()
+    { return False(); }
+
+    /** Logical functor inverting \a _Condition.
+    */
+    template<class _Condition>
+      struct Not
+      {
+        Not( _Condition cond_r )
+        : _cond( cond_r )
+        {}
+
+        template<class _Tp>
+          bool operator()( _Tp t ) const
+          {
+            return ! _cond( t );
+          }
+
+        _Condition _cond;
+      };
+
+    /** Convenience function for creating a Not from \a _Condition. */
+    template<class _Condition>
+      inline Not<_Condition> not_c( _Condition cond_r )
+      {
+        return Not<_Condition>( cond_r );
+      }
+
+    /** Logical functor chaining \a _ACondition \c OR \a _BCondition.
+    */
+    template<class _ACondition, class _BCondition>
+      struct Or
+      {
+        Or( _ACondition conda_r, _BCondition condb_r )
+        : _conda( conda_r )
+        , _condb( condb_r )
+        {}
+
+        template<class _Tp>
+          bool operator()( _Tp t ) const
+          {
+            return _conda( t ) || _condb( t );
+          }
+
+        _ACondition _conda;
+        _BCondition _condb;
+      };
+
+    /** Convenience function for creating a Or from two conditions
+     *  \a conda_r OR \a condb_r.
+    */
+    template<class _ACondition, class _BCondition>
+      inline Or<_ACondition, _BCondition> or_c( _ACondition conda_r, _BCondition condb_r )
+      {
+        return Or<_ACondition, _BCondition>( conda_r, condb_r );
+      }
+
+    /** Logical functor chaining \a _ACondition \c AND \a _BCondition.
+    */
+    template<class _ACondition, class _BCondition>
+      struct Chain
+      {
+        Chain( _ACondition conda_r, _BCondition condb_r )
+        : _conda( conda_r )
+        , _condb( condb_r )
+        {}
+
+        template<class _Tp>
+          bool operator()( _Tp t ) const
+          {
+            return _conda( t ) && _condb( t );
+          }
+
+        _ACondition _conda;
+        _BCondition _condb;
+      };
+
+    /** Convenience function for creating a Chain from two conditions
+     *  \a conda_r and \a condb_r.
+    */
+    template<class _ACondition, class _BCondition>
+      inline Chain<_ACondition, _BCondition> chain( _ACondition conda_r, _BCondition condb_r )
+      {
+        return Chain<_ACondition, _BCondition>( conda_r, condb_r );
+      }
+
+    //@}
+    ///////////////////////////////////////////////////////////////////
+
+    /** \defgroup ACTIONFUNCTOR
+     * \ingroup g_Functor
+     */
+    //@{
+
+    /** Strore the 1st result found in the variable passed to the ctor.
+     * \code
+     *   PoolItem result;
+     *   invokeOnEach( pool.byIdentBegin(installed), pool.byIdentEnd(installed),
+     *                 filter::SameItem( installed ),
+     *                 getFirst( result ) );
+     * \endcode
+     */
+    template<class _Tp>
+    struct GetFirst
+    {
+      GetFirst( _Tp & result_r )
+        : _result( &result_r )
+      {}
+      bool operator()( const _Tp & val_r )
+      { *_result = val_r; return false; }
+
+      private:
+        _Tp * _result;
+    };
+
+    /** Convenience function for creating \ref GetFirst. */
+    template<class _Tp>
+    GetFirst<_Tp> getFirst( _Tp & result_r )
+    { return GetFirst<_Tp>( result_r ); }
+
+
+    /** Strore the last result found in the variable passed to the ctor.
+     */
+    template<class _Tp>
+    struct GetLast
+    {
+      GetLast( _Tp & result_r )
+        : _result( &result_r )
+      {}
+      bool operator()( const _Tp & val_r )
+      { *_result = val_r; return true; }
+
+      private:
+        _Tp * _result;
+    };
+
+    /** Convenience function for creating \ref GetLast. */
+    template<class _Tp>
+    GetLast<_Tp> getLast( _Tp & result_r )
+    { return GetLast<_Tp>( result_r ); }
+
+
+    /** Store all results found to some output_iterator.
+     * \code
+     * std::vector<parser::ProductFileData> result;
+     * parser::ProductFileReader::scanDir( functor::getAll( std::back_inserter( result ) ),
+                                           sysRoot / "etc/products.d" );
+     * \endcode
+     */
+    template<class _OutputIterator>
+    struct GetAll
+    {
+      GetAll( _OutputIterator result_r )
+        : _result( result_r )
+      {}
+
+      template<class _Tp>
+      bool operator()(  const _Tp & val_r ) const
+      { *(_result++) = val_r; return true; }
+
+      private:
+        mutable _OutputIterator _result;
+    };
+
+    /** Convenience function for creating \ref GetAll. */
+    template<class _OutputIterator>
+    GetAll<_OutputIterator> getAll( _OutputIterator result_r )
+    { return GetAll<_OutputIterator>( result_r ); }
+
+    //@}
+    ///////////////////////////////////////////////////////////////////
+
+   /////////////////////////////////////////////////////////////////
+  } // namespace functor
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_FUNCTIONAL_H
diff --git a/zypp/base/Gettext.cc b/zypp/base/Gettext.cc
new file mode 100644 (file)
index 0000000..f90ec91
--- /dev/null
@@ -0,0 +1,63 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Gettext.cc
+ *
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+extern "C" {
+#include <libintl.h>
+}
+
+#include "zypp/base/Gettext.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace gettext
+  { /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    // TEXTDOMAIN and LOCALEDIR must be provided via config.h
+    // or at compile time using -D.
+    /////////////////////////////////////////////////////////////////
+
+    inline void assertInit()
+    {
+      static bool initialized = false;
+      if ( ! initialized )
+        {
+          ::bindtextdomain( TEXTDOMAIN, LOCALEDIR );
+          ::bind_textdomain_codeset( TEXTDOMAIN, "UTF-8" );
+          initialized = true;
+        }
+    }
+
+    const char * dgettext( const char * msgid )
+    {
+      assertInit();
+      return ::dgettext( TEXTDOMAIN, msgid );
+    }
+
+    const char * dngettext( const char * msgid1, const char * msgid2,
+                            unsigned long n )
+    {
+      assertInit();
+      return ::dngettext( TEXTDOMAIN, msgid1, msgid2, n );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace gettext
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/Gettext.h b/zypp/base/Gettext.h
new file mode 100644 (file)
index 0000000..fb9fe8a
--- /dev/null
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Gettext.h
+ *
+ * Interface to gettext.
+ *
+*/
+#ifndef ZYPP_BASE_GETTEXT_H
+#define ZYPP_BASE_GETTEXT_H
+
+/** Just tag text for translation. */
+#define N_(MSG) MSG
+
+/** Return translated text. */
+#define _(MSG) ::zypp::gettext::dgettext( MSG )
+
+/** Return translated text (plural form). */
+#define _PL(MSG1,MSG2,N) ::zypp::gettext::dngettext( MSG1, MSG2, N )
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace gettext
+  { /////////////////////////////////////////////////////////////////
+
+    /** Return translated text. */
+    const char * dgettext( const char * msgid );
+
+    /** Return translated text (plural form). */
+    const char * dngettext( const char * msgid1, const char * msgid2,
+                            unsigned long n );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace gettext
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_GETTEXT_H
diff --git a/zypp/base/GzStream.cc b/zypp/base/GzStream.cc
new file mode 100644 (file)
index 0000000..d4d5cf0
--- /dev/null
@@ -0,0 +1,365 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                               core system                            |
+|                                         (C) SuSE Linux Products GmbH |
+\----------------------------------------------------------------------/
+
+  File:       GzStream.cc
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose: Streams reading and writing gzip files.
+
+/-*/
+
+#include <cerrno>
+#include <iostream>
+#include <zypp/base/LogControl.h>
+#include <zypp/base/LogTools.h>
+using std::endl;
+
+#include "zypp/base/GzStream.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace gzstream_detail
+  { /////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ZlibError
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ZlibError::strerror
+    // METHOD TYPE : std::string
+    //
+    std::string
+    ZlibError::strerror() const
+    {
+      std::string ret = ( _zError ? ::zError( _zError ) : "OK" );
+      if ( _zError == Z_ERRNO )
+        ret += std::string("(") + ::strerror( _errno ) + ")";
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : fgzstreambuf
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::open
+    // METHOD TYPE : fgzstreambuf *
+    //
+    fgzstreambuf *
+    fgzstreambuf::open( const char * name_r, std::ios_base::openmode mode_r )
+    {
+      fgzstreambuf * ret = NULL;
+      if ( ! isOpen() )
+        {
+         // we expect gzdopen to handle errors of ::open
+          if ( mode_r == std::ios_base::in )
+         {
+            _fd = ::open( name_r, O_RDONLY );
+            _file = gzdopen( _fd, "rb" );
+         }
+          else if ( mode_r == std::ios_base::out )
+         {
+            _fd = ::open( name_r, O_WRONLY|O_CREAT, 0666 );
+            _file = gzdopen( _fd, "wb" );
+         }
+          // else: not supported
+
+          if ( isOpen() )
+            {
+              // Store mode and initialize the internal buffer.
+              _mode = mode_r;
+              if ( inReadMode() )
+                {
+                  setp( NULL, NULL );
+                  setg( &(_buffer[0]), &(_buffer[0]), &(_buffer[0]) );
+                }
+              else
+                {
+                  setp( &(_buffer[0]), &(_buffer[_buffer.size()-1]) );
+                  setg( NULL, NULL, NULL );
+                }
+              ret = this;
+            }
+          else
+            setZError();
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::close
+    // METHOD TYPE : fgzstreambuf *
+    //
+    fgzstreambuf *
+    fgzstreambuf::close()
+    {
+      fgzstreambuf * ret = NULL;
+      if ( isOpen() )
+        {
+          bool failed = false;
+          if ( sync() != 0 )
+            failed = true;
+         // it also closes _fd, fine
+          int r = gzclose( _file );
+          if ( r != Z_OK )
+            {
+              failed = true;
+              // DONT call setZError() here, as _file is no longer valid
+              _error._zError = r;
+              _error._errno = errno;
+            }
+
+          // Reset everything
+         _fd = -1;
+          _file = NULL;
+          _mode = std::ios_base::openmode(0);
+          setp( NULL, NULL );
+          setg( NULL, NULL, NULL );
+          if ( ! failed )
+            ret = this;
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::sync
+    // METHOD TYPE : int
+    //
+    int
+    fgzstreambuf::sync()
+    {
+      int ret = 0;
+      if ( pbase() < pptr() ) {
+        const int_type res = overflow();
+        if ( traits_type::eq_int_type( res, traits_type::eof() ) )
+          ret = -1;
+      }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::overflow
+    // METHOD TYPE : fgzstreambuf::int_type
+    //
+    fgzstreambuf::int_type
+    fgzstreambuf::overflow( int_type c )
+    {
+      int_type ret = traits_type::eof();
+      if ( inWriteMode() )
+        {
+          if ( ! traits_type::eq_int_type( c, traits_type::eof() ) )
+            {
+              *pptr() = traits_type::to_char_type( c );
+              pbump(1);
+            }
+          if ( pbase() <= pptr() )
+            {
+              if ( zWriteFrom( pbase(), pptr() - pbase() ) )
+                {
+                  setp( &(_buffer[0]), &(_buffer[_buffer.size()-1]) );
+                  ret = traits_type::not_eof( c );
+                }
+              // else: error writing the file
+            }
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::underflow
+    // METHOD TYPE : fgzstreambuf::int_type
+    //
+    fgzstreambuf::int_type
+    fgzstreambuf::underflow()
+    {
+      int_type ret = traits_type::eof();
+      if ( inReadMode() )
+        {
+          if ( gptr() < egptr() )
+            return traits_type::to_int_type( *gptr() );
+
+          const std::streamsize got = zReadTo( &(_buffer[0]), _buffer.size() );
+          if ( got > 0 )
+            {
+              setg( &(_buffer[0]), &(_buffer[0]), &(_buffer[got]) );
+              ret = traits_type::to_int_type( *gptr() );
+            }
+          else if ( got == 0 )
+            {
+              // EOF
+              setg( &(_buffer[0]), &(_buffer[0]), &(_buffer[0]) );
+            }
+          // else: error reading the file
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::zReadTo
+    // METHOD TYPE : std::streamsize
+    //
+    std::streamsize
+    fgzstreambuf::zReadTo( char * buffer_r, std::streamsize maxcount_r )
+    {
+      int read = gzread( _file, buffer_r, maxcount_r );
+      if ( read < 0 )
+        setZError();
+      return read;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::zWriteFrom
+    // METHOD TYPE : bool
+    //
+    bool
+    fgzstreambuf::zWriteFrom( const char * buffer_r, std::streamsize count_r )
+    {
+      int written = 0;
+      if ( count_r )
+        {
+          if ( (written = gzwrite( _file, buffer_r, count_r )) == 0 )
+            setZError();
+        }
+      return( written == count_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::zSeekTo
+    // METHOD TYPE : fgzstreambuf::pos_type
+    //
+    fgzstreambuf::pos_type
+    fgzstreambuf::zSeekTo( off_type off_r, std::ios_base::seekdir way_r )
+    {
+      z_off_t ret = gzseek( _file, off_r, way_r );
+      if ( ret == -1 )
+        setZError();
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::zTell
+    // METHOD TYPE : fgzstreambuf::pos_type
+    //
+    fgzstreambuf::pos_type
+    fgzstreambuf::zTell()
+    {
+      z_off_t ret = gztell( _file );
+      if ( ret == -1 )
+        setZError();
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : fgzstreambuf::seekTo
+    // METHOD TYPE : fgzstreambuf::pos_type
+    //
+    fgzstreambuf::pos_type
+    fgzstreambuf::seekTo( off_type off_r, std::ios_base::seekdir way_r )
+    {
+      pos_type ret = pos_type(off_type(-1));
+      if ( isOpen() )
+        {
+          if ( inWriteMode() )
+            {
+              if ( sync() == 0 )
+                ret = zSeekTo( off_r, way_r );
+            }
+          else
+            {
+              off_type zegptr = zTell();
+              if ( zegptr != off_type(-1) )
+                {
+                  if ( way_r == std::ios_base::end )
+                    {
+                      // Invalidate buffer and seek.
+                      // XXX improve by transformation into ios_base::beg
+                      // to see whether we stay inside the buffer.
+                      setg( &(_buffer[0]), &(_buffer[0]), &(_buffer[0]) );
+                      ret = zSeekTo( off_r, way_r );
+                    }
+                  else
+                    {
+                      // Transform into ios_base::beg and seek.
+                      off_type zeback = zegptr - ( egptr() - eback() );
+                      off_type zgptr  = zegptr - ( egptr() - gptr() );
+                      off_type zngptr = off_r;
+                      if ( way_r == std::ios_base::cur )
+                        {
+                          zngptr += zgptr;
+                          way_r = std::ios_base::beg;
+                        }
+
+                      if ( way_r == std::ios_base::beg )
+                        {
+                          if ( zeback <= zngptr && zngptr <= zegptr )
+                            {
+                              // Still inside buffer, adjust gptr and
+                              // calculate new position.
+                              setg( eback(),
+                                    eback() + (zngptr-zeback),
+                                    egptr() );
+                              ret = pos_type(zngptr);
+                            }
+                          else
+                            {
+                              // Invalidate buffer and seek.
+                              setg( &(_buffer[0]), &(_buffer[0]), &(_buffer[0]) );
+                              ret = zSeekTo( off_r, way_r );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+      return ret;
+    }
+
+    fgzstreambuf::pos_type
+    fgzstreambuf::compressed_tell() const
+    {
+       off_t pos = lseek (_fd, 0, SEEK_CUR);
+       // hopefully the conversion is ok
+       return pos;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace gzstream_detail
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/base/GzStream.h b/zypp/base/GzStream.h
new file mode 100644 (file)
index 0000000..eaadd83
--- /dev/null
@@ -0,0 +1,286 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                               core system                            |
+|                                         (C) SuSE Linux Products GmbH |
+\----------------------------------------------------------------------/
+
+  File:       GzStream.h
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose: Streams reading and writing gzip files.
+
+/-*/
+#ifndef ZYPP_BASE_GZSTREAM_H
+#define ZYPP_BASE_GZSTREAM_H
+
+#include <iosfwd>
+#include <streambuf>
+#include <vector>
+#include <zlib.h>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace gzstream_detail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ZlibError
+    /**
+     * @short Helper class to ship zlib errors.
+     **/
+    struct ZlibError
+    {
+      /**
+       * The zlib error code
+       **/
+      int _zError;
+
+      /**
+       * errno, valid if zError is Z_ERRNO
+       **/
+      int _errno;
+
+      ZlibError()
+      : _zError( 0 ), _errno( 0 )
+      {}
+
+      /**
+       * Return string describing the zlib error code
+       **/
+      std::string
+      strerror() const;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ZlibError Stream output. */
+    inline std::ostream & operator<<( std::ostream & str, const ZlibError & obj )
+    { return str << obj.strerror(); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : fgzstreambuf
+    /**
+     * @short Streambuffer reading or writing gzip files.
+     *
+     * Read and write mode are mutual exclusive. Seek is supported,
+     * but zlib restrictions appy (only forward seek in write mode;
+     * backward seek in read mode might be expensive).Putback is not
+     * supported.
+     *
+     * Reading plain (no gziped) files is possible as well.
+     *
+     * This streambuf is used in @ref ifgzstream and  @ref ofgzstream.
+     **/
+    class fgzstreambuf : public std::streambuf {
+
+    public:
+
+      fgzstreambuf( unsigned bufferSize_r = 512 )
+      : _fd( -1 )
+      ,_file( NULL )
+      , _mode( std::ios_base::openmode(0) )
+      , _buffer( (bufferSize_r?bufferSize_r:1), 0 )
+      {}
+
+      virtual
+      ~fgzstreambuf()
+      { close(); }
+
+      bool
+      isOpen() const
+      { return _file; }
+
+      bool
+      inReadMode() const
+      { return( _mode == std::ios_base::in ); }
+
+      bool
+      inWriteMode() const
+      { return( _mode == std::ios_base::out ); }
+
+      fgzstreambuf *
+      open( const char * name_r, std::ios_base::openmode mode_r = std::ios_base::in );
+
+        fgzstreambuf *
+        close();
+
+       //! Tell the file position in the compressed file.
+       //! Analogous to tell(2), complementary to gztell.
+       pos_type compressed_tell() const;
+
+        /**
+         * The last error returned fron zlib.
+         **/
+        ZlibError
+        zError() const
+        { return _error; }
+
+    protected:
+
+      virtual int
+      sync();
+
+      virtual int_type
+      overflow( int_type c = traits_type::eof() );
+
+      virtual int_type
+      underflow();
+
+      virtual pos_type
+      seekoff( off_type off_r, std::ios_base::seekdir way_r, std::ios_base::openmode /* ignored */ )
+      { return seekTo( off_r, way_r ); }
+
+      virtual pos_type
+      seekpos( pos_type pos_r, std::ios_base::openmode /* ignored */ )
+      { return seekTo( off_type(pos_r), std::ios_base::beg ); }
+
+    private:
+
+      typedef std::vector<char> buffer_type;
+
+      //! file descriptor of the compressed file
+      int                     _fd;
+
+      gzFile                   _file;
+
+      std::ios_base::openmode  _mode;
+
+      buffer_type              _buffer;
+
+      ZlibError                _error;
+
+    private:
+
+      void
+      setZError()
+      { gzerror( _file, &_error._zError ); }
+
+      std::streamsize
+      zReadTo( char * buffer_r, std::streamsize maxcount_r );
+
+      bool
+      zWriteFrom( const char * buffer_r, std::streamsize count_r );
+
+      pos_type
+      zSeekTo( off_type off_r, std::ios_base::seekdir way_r );
+
+      pos_type
+      zTell();
+
+      pos_type
+      seekTo( off_type off_r, std::ios_base::seekdir way_r );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : fXstream<class _BStr,class _SBuf>
+    /**
+     * @short Common template to define ifgzstream/ofgzstream
+     * reading/writing gzip files.
+     *
+     * Don't use fXstream directly, but @ref ifgzstream or
+     * @ref ofgzstream. fXstream is just to avoid almost
+     * duplicate code.
+     **/
+    template<class _BStream,class _StreamBuf>
+      class fXstream : public _BStream
+      {
+      public:
+
+        typedef gzstream_detail::ZlibError ZlibError;
+        typedef _BStream                   stream_type;
+        typedef _StreamBuf                 streambuf_type;
+
+        fXstream()
+        : stream_type( NULL )
+        { this->init( &_streambuf ); }
+
+        explicit
+        fXstream( const char * file_r )
+        : stream_type( NULL )
+        { this->init( &_streambuf ); this->open( file_r ); }
+
+        virtual
+        ~fXstream()
+        {}
+
+        bool
+        is_open() const
+        { return _streambuf.isOpen(); }
+
+        void
+        open( const char * file_r )
+        {
+          if ( !_streambuf.open( file_r, defMode(*this) ) )
+            this->setstate(std::ios_base::failbit);
+          else
+            this->clear();
+        }
+
+        void
+        close()
+        {
+          if ( !_streambuf.close() )
+            this->setstate(std::ios_base::failbit);
+        }
+
+        /**
+         * The last error returned retuned fron zlib.
+         **/
+        ZlibError
+        zError() const
+        { return _streambuf.zError(); }
+
+       //! Similar to ios::rdbuf.
+       //! But it returns our specific type, not the generic streambuf *.
+       const streambuf_type&
+        getbuf() const
+        { return _streambuf; }
+
+      private:
+
+        streambuf_type _streambuf;
+
+        std::ios_base::openmode
+        defMode( const std::istream & str_r )
+        { return std::ios_base::in; }
+
+        std::ios_base::openmode
+        defMode( const std::ostream & str_r )
+        { return std::ios_base::out; }
+
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace gzstream_detail
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * istream reading gzip files as well as plain files.
+   **/
+  typedef gzstream_detail::fXstream<std::istream,gzstream_detail::fgzstreambuf> ifgzstream;
+
+  /**
+   * ostream writing gzip files.
+   **/
+  typedef gzstream_detail::fXstream<std::ostream,gzstream_detail::fgzstreambuf> ofgzstream;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_BASE_GZSTREAM_H
diff --git a/zypp/base/IOStream.cc b/zypp/base/IOStream.cc
new file mode 100644 (file)
index 0000000..0bb5b77
--- /dev/null
@@ -0,0 +1,99 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/IOStream.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/IOStream.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace iostr
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : getline
+     **        FUNCTION TYPE : std::string
+     */
+    std::string getline( std::istream & str )
+    {
+      static const unsigned tmpBuffLen = 1024;
+      static char           tmpBuff[tmpBuffLen];
+      std::string ret;
+      do {
+        str.clear();
+        str.getline( tmpBuff, tmpBuffLen ); // always writes '\0' terminated
+        ret += tmpBuff;
+      } while( str.rdstate() == std::ios::failbit );
+
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : EachLine
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : EachLine::EachLine
+    // METHOD TYPE : Ctor
+    //
+    EachLine::EachLine( std::istream & str_r, unsigned lineNo_r )
+      : _str( str_r )
+      , _lineStart( -1 )
+      , _lineNo( lineNo_r )
+      , _valid( true )
+    {
+      next();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : EachLine::next
+    // METHOD TYPE : bool
+    //
+    bool EachLine::next()
+    {
+      if ( ! _valid )
+      {
+       return false;
+      }
+
+      if ( ! _str ) // usg: saw EOF in previous read
+      {
+       _line.clear();
+       return(_valid = false);
+      }
+
+      _lineStart = _str.tellg();
+      _line = iostr::getline( _str );
+      ++_lineNo;
+      if ( _str.fail() || _str.bad() )
+      {
+       _line.clear();
+       return(_valid = false);
+      }
+      return(_valid = true);
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace iostr
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/IOStream.h b/zypp/base/IOStream.h
new file mode 100644 (file)
index 0000000..d7b5749
--- /dev/null
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/IOStream.h
+ *
+*/
+#ifndef ZYPP_BASE_IOSTREAM_H
+#define ZYPP_BASE_IOSTREAM_H
+
+#include <iosfwd>
+#include <boost/io/ios_state.hpp>
+
+#include "zypp/base/PtrTypes.h"
+#include <zypp/base/SafeBool.h>
+#include <zypp/base/NonCopyable.h>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  /** Iostream related utilities.
+  */
+  namespace iostr
+  { /////////////////////////////////////////////////////////////////
+
+    /** Save and restore streams \c width, \c precision
+     * and \c fmtflags.
+     */
+    typedef boost::io::ios_base_all_saver IosFmtFlagsSaver;
+
+
+    /** Read one line from stream.
+     *
+     * Reads everything up to the next newline or EOF. newline
+     * is read but not returned.
+     *
+     * \see \ref forEachLine
+     */
+    std::string getline( std::istream & str );
+
+    /** Copy istream to ostream.
+     * \return reference to the ostream.
+     */
+    inline std::ostream & copy( std::istream & from_r, std::ostream & to_r )
+    {
+      if ( from_r && to_r )
+      {
+        char ch;
+        while ( from_r && from_r.get( ch ) )
+          to_r.put( ch );
+      }
+      return to_r;
+    }
+
+    /** Copy istream to ostream, prefixing each line with \a indent_r (default <tt>"> "</tt> ).
+     * \return reference to the ostream.
+     */
+    inline std::ostream & copyIndent( std::istream & from_r, std::ostream & to_r, const std::string & indent_r = "> " )
+    {
+      if ( from_r && to_r )
+      {
+        char ch;
+        bool indent = true;
+        while ( from_r && from_r.get( ch ) )
+        {
+          if ( indent )
+            to_r << indent_r;
+          indent = ( ch == '\n' );
+          to_r.put( ch );
+        }
+      }
+      return to_r;
+    }
+
+    /** Copy istream to ostream, prefixing each line with \a indent_r (default <tt>"> "</tt> ).
+     * \return reference to the ostream.
+     */
+    inline void tee( std::istream & from_r, std::ostream & to1_r, std::ostream & to2_r )
+    {
+      if ( from_r && ( to1_r ||to2_r ) )
+      {
+        char ch;
+        while ( from_r && from_r.get( ch ) )
+        {
+          to1_r.put( ch );
+          to2_r.put( ch );
+        }
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : EachLine
+    //
+    /** Simple lineparser: Traverse each line in a file.
+     *
+     * \code
+     * std::ifstream infile( "somefile" );
+     * for( iostr::EachLine in( infile ); in; in.next() )
+     * {
+     *   DBG << *in << endl;
+     * }
+     * \endcode
+     */
+    class EachLine : private base::SafeBool<EachLine>, private base::NonCopyable
+    {
+      typedef base::SafeBool<EachLine> SafeBool;
+
+      public:
+       /** Ctor taking a stream and reading the 1st line from it. */
+       EachLine( std::istream & str_r, unsigned lineNo_r = 0 );
+
+       /** Evaluate class in a boolean context. */
+       using SafeBool::operator bool_type;
+
+       /** Whether \c this contains a valid line to consume. */
+       bool valid() const
+       { return boolTest(); }
+
+       /** Return the current line number. */
+       unsigned lineNo() const
+       { return _lineNo; }
+
+       std::streamoff lineStart() const
+       { return _lineStart; };
+
+       /** Set current line number. */
+       void setLineNo( unsigned lineNo_r )
+       { _lineNo = lineNo_r; }
+
+       /** Access the current line. */
+       const std::string & operator*() const
+       { return _line; }
+       /** \overload non const access */
+       std::string & operator*()
+       { return _line; }
+
+       /** Access the current line. */
+       const std::string * operator->() const
+       { return &_line; }
+
+       /** Advance to next line. */
+       bool next();
+
+       /** Advance \a num_r lines. */
+       bool next( unsigned num_r )
+       {
+         while ( num_r-- && next() )
+           ; /* EMPTY */
+         return valid();
+       }
+
+      private:
+       friend SafeBool::operator bool_type() const;
+       bool boolTest() const
+       { return _valid; }
+
+      private:
+       std::istream & _str;
+       std::string    _line;
+       std::streamoff _lineStart;
+       unsigned       _lineNo;
+       bool           _valid;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** Simple lineparser: Call functor \a consume_r for each line.
+     *
+     * \param str_r The istream to read from.
+     * \param consume_r A reference to a function or functor. The loop is
+     * aborted if the function returns \c false.
+     * \code
+     * bool consume( const std::string & )
+     * { ... }
+     *
+     * struct Consume : public std::unary_function<const std::string &, bool>
+     * {
+     *   bool operator()( const std::string & line_r )
+     *   { ... }
+     * };
+     * \endcode
+     *
+     * \return A reference to \a consume_r.
+     *
+     * \todo Should be templated and specialized according to the
+     * functors return type, to allow \c void consumer.
+     */
+    template<class _Function>
+      _Function & forEachLine( std::istream & str_r, _Function & consume_r )
+      {
+        while ( str_r )
+          {
+            std::string l = getline( str_r );
+            if ( ! (str_r.fail() || str_r.bad()) )
+              {
+                // l contains valid data to be consumed.
+                if ( ! consume_r( l ) )
+                  break;
+              }
+          }
+        return consume_r;
+      }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace iostr
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_IOSTREAM_H
diff --git a/zypp/base/InputStream.cc b/zypp/base/InputStream.cc
new file mode 100644 (file)
index 0000000..81c138b
--- /dev/null
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/InputStream.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/base/InputStream.h"
+#include "zypp/base/GzStream.h"
+
+#include "zypp/PathInfo.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    inline std::streamoff _heplerInitSize( const Pathname & file_r )
+    {
+      PathInfo p( file_r );
+      if ( p.isFile() && filesystem::zipType( file_r ) == filesystem::ZT_NONE )
+       return p.size();
+      return -1;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream()
+  : _stream( &std::cin, NullDeleter() )
+  , _name( "STDIN" )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( std::istream & stream_r,
+                            const std::string & name_r )
+  : _stream( &stream_r, NullDeleter() )
+  , _name( name_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const Pathname & file_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( _path.asString() )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const Pathname & file_r,
+                            const std::string & name_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( name_r )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const std::string & file_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( _path.asString() )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const std::string & file_r,
+                            const std::string & name_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( name_r )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const char * file_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( _path.asString() )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::InputStream
+  //   METHOD TYPE : Constructor
+  //
+  InputStream::InputStream( const char * file_r,
+                            const std::string & name_r )
+  : _path( file_r )
+  , _stream( new ifgzstream( _path.asString().c_str() ) )
+  , _name( name_r )
+  , _size( _heplerInitSize( _path ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : InputStream::~InputStream
+  //   METHOD TYPE : Destructor
+  //
+  InputStream::~InputStream()
+  {}
+
+  /******************************************************************
+   **
+   **  FUNCTION NAME : operator<<
+   **  FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const InputStream & obj )
+  {
+    return str << obj.name() << obj.stream();
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/InputStream.h b/zypp/base/InputStream.h
new file mode 100644 (file)
index 0000000..cbf12d4
--- /dev/null
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/InputStream.h
+ *
+*/
+#ifndef ZYPP_BASE_INPUTSTREAM_H
+#define ZYPP_BASE_INPUTSTREAM_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : InputStream
+  //
+  /** Helper to create and pass std::istream.
+   * The provided std::istream may either be std::cin,
+   * sone (gziped) file or an aleady existig \c std::istream.
+   *
+   * An optional \c name arument may be passed to the ctor,
+   * to identify the stream in log messages, even if it is
+   * not a file.
+   *
+   * Per default the name is "STDIN", the path to an input file
+   * or empty.
+   *
+   * \code
+   * void parse( const InputStream & input = InputStream() )
+   * {
+   *   // process input.stream() and refer to input.name()
+   *   // in log messages.
+   * }
+   *
+   * parse();                  // std::cin
+   * parse( "/some/file" );    // file
+   * parse( "/some/file.gz" ); // gziped file
+   * std::istream & mystream;
+   * parse( mystream );        // some existing stream
+   * parse( InputStream( mystream,
+   *                     "my stream's name" ) );
+   * \endcode
+  */
+  class InputStream
+  {
+  public:
+    /** Default ctor providing \c std::cin. */
+    InputStream();
+
+    /** Ctor providing an aleady existig \c std::istream. */
+    InputStream( std::istream & stream_r,
+                 const std::string & name_r = std::string() );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const Pathname & file_r );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const Pathname & file_r,
+                 const std::string & name_r );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const std::string & file_r );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const std::string & file_r,
+                 const std::string & name_r );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const char * file_r );
+
+    /** Ctor for reading a (gziped) file. */
+    InputStream( const char * file_r,
+                 const std::string & name_r );
+
+    /** Dtor. */
+    ~InputStream();
+
+    /** The std::istream.
+     * \note The provided std::istream is never \c const.
+    */
+    std::istream & stream() const
+    { return *_stream; }
+
+    /** Allow implicit conversion to std::istream.*/
+    operator std::istream &() const
+    { return *_stream; }
+
+    /** Name of the std::istream.
+     * Per default this is "STDIN", the path to an input file or
+     * empty. A custom string may be provided to the ctor.
+     *
+     * This may be used in log messages to identify the stream even
+     * even if it is not a file.
+    */
+    const std::string & name() const
+    { return _name; }
+
+    /** Path to the input file or empty if no file. */
+    const Pathname & path() const
+    { return _path; }
+
+    /** Size of the input stream (informal).
+     * If constructed from an uncompressed file, the file size.
+     * Otherwise \c -1. See \ref setSize;
+    */
+    std::streamoff size() const
+    { return _size; }
+
+    /** Set the size of the input stream.
+     * You may set it to whatever vaule is appropriate. E.g.
+     * <tt>*=10</tt> to compensate gzip comression. or the
+     * number of items, lines, ... The value is not used here,
+     * just provided.
+    */
+    void setSize( std::streamoff val_r )
+    { _size = val_r; }
+
+  private:
+    Pathname                 _path;
+    shared_ptr<std::istream> _stream;
+    std::string              _name;
+    DefaultIntegral<std::streamoff,-1> _size;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates InputStream Stream output */
+  std::ostream & operator<<( std::ostream & str, const InputStream & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_INPUTSTREAM_H
diff --git a/zypp/base/InterProcessMutex.cc b/zypp/base/InterProcessMutex.cc
new file mode 100644 (file)
index 0000000..a9c9e69
--- /dev/null
@@ -0,0 +1,348 @@
+
+extern "C"
+{
+#include <sys/file.h>
+}
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/InterProcessMutex.h"
+#include "zypp/base/String.h"
+
+#include "zypp/TmpPath.h"
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+#define LMIL MIL << "LOCK [" << _options.name << "] "
+
+using namespace std;
+
+namespace zypp
+{
+namespace base
+{
+
+ZYppLockedException::ZYppLockedException( const std::string & msg_r,
+                                          const std::string &name,
+                                          pid_t locker_pid )
+    : Exception(msg_r)
+    , _locker_pid (locker_pid)
+    , _name(name)
+{}
+
+ZYppLockedException::~ZYppLockedException() throw()
+{}
+
+InterProcessMutex::Options::Options( ConsumerType ptype,
+                                     const std::string &pname,
+                                     int ptimeout )
+    : name(pname) 
+    , timeout(ptimeout)
+    , type(ptype)
+{
+    if ( geteuid() == 0 )
+        base = "/var/run";
+    else
+        base = filesystem::TmpPath::defaultLocation() + ( string("zypp-") + getenv("USER") );
+}
+    
+
+   
+InterProcessMutex::InterProcessMutex( const Options &poptions )
+    : _options(poptions)
+{
+    // get the current pid
+    pid_t curr_pid = getpid();
+    Pathname lock_file = lockFilePath();
+    int totalslept = 0;
+    int k = 0;
+    
+    while (1)
+    {
+        k++;
+        
+        // try to create the lock file atomically, this will fail if
+        // the lock exists
+       try {
+         _fd.reset( new Fd( lock_file, O_RDWR | O_CREAT | O_EXCL, 0666) );
+       } catch (...) {
+         _fd.reset();
+       }
+        if ( !_fd || !_fd->isOpen() )
+        {
+            struct flock lock;
+            
+            // the file exists, lets see if someone has it locked exclusively
+            _fd.reset( new Fd( lock_file, O_RDWR ) );
+            if ( !_fd->isOpen() )
+            {
+                ZYPP_THROW(Exception(str::form(_("Can't open lock file: %s"), strerror(errno))));
+            }
+            
+            memset(&lock, 0, sizeof(struct flock));
+            lock.l_whence = SEEK_SET;
+
+            // GETLK tells you the conflicting lock as if the lock you pass
+            // would have been set. So set the lock type depending on whether
+            // we are a writer or a reader.
+            lock.l_type = ( ( _options.type == Writer ) ? F_WRLCK : F_RDLCK );
+            
+
+            // get lock information
+            if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
+            {
+                ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
+            }
+
+            
+            MIL << lock_file << " : ";
+            switch ( lock.l_type )
+            {
+                case F_WRLCK:
+                    MIL << " Write-Lock conflicts" << endl;
+                    break;
+                case F_RDLCK:
+                    MIL << " Read-Lock conflicts" << endl;                    
+                    break;
+                case F_UNLCK:
+                    MIL << " No lock conflicts" << endl;
+                    break;
+                default:
+                    break;
+                    
+            }
+            
+            // F_GETLK is confusing
+            // http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2005-09/123fae2c774bceed?rnum=61&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2005-09%3F
+            // new table of access
+            // F_WRLCK   Reader  Wait or abort
+            // F_WRLCK   Writer  Wait or abort
+            // F_RDLCK   Writer  Wait or abort
+            // F_RDLCK   Reader  Can't happen, anyway, wait or abort            
+            // F_UNLCK   Reader  Take reader lock
+            // F_UNLCK   Writer  Take writer lock
+            
+            
+
+            // wait or abort
+            if (  lock.l_type != F_UNLCK )
+            {
+                // some lock conflicts with us.
+                LMIL << "pid " << lock.l_pid << " is running and has a lock that conflicts with us." << std::endl;
+                
+                // abort if we have slept more or equal than the timeout, but
+                // not for the case where timeout is negative which means no
+                // timeout and therefore we never abort.
+                if ( (totalslept >= _options.timeout) && (_options.timeout >= 0 ) )
+                {
+                    ZYPP_THROW(ZYppLockedException(                                       
+                                   _("This action is being run by another program already."),
+                                   _options.name, lock.l_pid));
+                }
+                        
+                // if not, let sleep one second and count it
+                LMIL << "waiting 1 second..." << endl;
+                sleep(1);
+                ++totalslept;
+                continue;
+            }
+            else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Reader ) )
+            {
+                // either there is no lock or a reader has it so we just
+                // acquire a reader lock.
+
+                // try to get more lock info
+                lock.l_type = F_WRLCK;
+                if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
+                {
+                    ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
+                }
+                
+                if ( lock.l_type == F_UNLCK )
+                {
+                    LMIL << "no previous readers, unlinking lock file and retrying." << endl;
+                    
+                    // actually there are no readers
+                    // lets delete it and break, so the next loop will
+                    // probably succeed in creating it. The worst thing that can
+                    // happen is that another process will take it first, but
+                    // we are not aiming at such level of correctness. Otherwise
+                    // the code path will complicate too much.
+                    memset(&lock, 0, sizeof(struct flock));
+                    lock.l_type = F_WRLCK;
+                    lock.l_whence = SEEK_SET;
+                    lock.l_pid = getpid();
+
+                    if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
+                    {
+                        ZYPP_THROW (Exception( "Can't lock file to unlink it."));
+                    }
+                    filesystem::unlink(lock_file.c_str());
+                    continue;
+                }
+                else if ( lock.l_type == F_RDLCK )
+                {
+                    // there is another reader.
+                    LMIL << "previous readers on lock file. taking lock as a reader." << std::endl;
+                    memset(&lock, 0, sizeof(struct flock));
+                    lock.l_type = F_RDLCK;
+                    lock.l_whence = SEEK_SET;
+                    lock.l_pid = getpid();
+
+                    if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
+                    {
+                        ZYPP_THROW (Exception( "Can't lock file for reader"));
+                    }
+                    // and keep the lock open.
+                    break;
+                }
+                else
+                {
+                    // cant happen!
+                    ERR << "impossible condition" << endl;
+                    
+                    break;
+                }
+            }
+            else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Writer ) )
+            {
+                LMIL << "stale lock found" << endl;
+                // Nobody conflicts with a writer lock so nobody is actually
+                // locking.
+                // lets delete it and break, so the next loop will
+                // probably succeed in creating it. The worst thing that can
+                // happen is that another process will take it first, but
+                // we are not aiming at such level of correctness. Otherwise
+                // the code path will complicate too much.
+                memset(&lock, 0, sizeof(struct flock));
+                lock.l_type = F_WRLCK;
+                lock.l_whence = SEEK_SET;
+                lock.l_pid = getpid();
+
+                if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
+                {
+                    ZYPP_THROW (Exception( "Can't lock file to unlink it."));
+                }
+                filesystem::unlink(lock_file.c_str());
+                continue;
+            } 
+            else 
+            {
+                // undefined case, just get out of the loop
+                LMIL << "undefined case!" << endl;
+                
+                break;
+            }
+            
+        }
+        else 
+        {
+            // exclusive file creation succeeded. So may be we are the
+            // first writer or first reader
+            
+            // try to lock it exclusively
+            // if it fails, someone won us, so we just go for another try
+            // or just abort
+            LMIL << "no lock found, taking ownership of it as a " << ( (_options.type == Reader ) ? "reader" : "writer" ) << endl;
+            struct flock lock;
+            memset(&lock, 0, sizeof(struct flock));
+            lock.l_whence = SEEK_SET;
+            lock.l_type = F_WRLCK;
+            lock.l_pid = getpid();
+
+            if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
+                ZYPP_THROW (Exception( "Can't lock file to write pid."));
+            
+            char buffer[100];
+            sprintf( buffer, "%d\n", curr_pid);
+            write( _fd->fd(), buffer, strlen(buffer));
+            
+            // by now the pid is written and the file locked.
+            // If we are a reader, just downgrade the lock to
+            // read shared lock.
+            if ( _options.type == Reader )
+            {
+                lock.l_type = F_RDLCK;
+               
+                if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
+                    ZYPP_THROW (Exception( "Can't set lock file to shared"));
+            }
+            
+            break;
+        }           
+    } // end loop       
+
+    LMIL << "Lock intialized" << endl;
+    
+}
+
+InterProcessMutex::~InterProcessMutex()
+{
+    try
+    {
+        Pathname lock_file = lockFilePath();
+        LMIL << "dropping " 
+             << ( (_options.type == Reader ) ? "reader" : "writer" ) 
+             << " lock on " << lock_file << endl;
+        
+        switch ( _options.type )
+        {
+            case Reader:
+                
+                break;
+                
+            case Writer:
+                // we are the only writer, so unlink the file
+                filesystem::unlink(lock_file.c_str());
+                break;
+                
+        }
+        // and finally close the file and release the lock
+        // (happens automatically)
+    }
+    catch(...) {} // let no exception escape.
+}
+
+
+Pathname InterProcessMutex::lockFilePath() const
+{
+    filesystem::assert_dir(_options.base);
+    return _options.base + ("zypp-" + _options.name + ".lock");
+}    
+
+bool InterProcessMutex::isProcessRunning(pid_t pid_r)
+{
+    // it is another program, not me, see if it is still running
+    Pathname procdir( Pathname("/proc") / str::numstring(pid_r) );
+
+    PathInfo status( procdir/"status" );
+    XXX << "Checking " <<  status << endl;
+    bool still_running = status.isExist();
+
+    if ( still_running )
+    {
+        Pathname p( procdir/"exe" );
+        XXX << p << " -> " << filesystem::readlink( p ) << endl;
+
+        p = procdir/"cmdline";
+        XXX << p << ": ";
+        std::ifstream infile( p.c_str() );
+        for( iostr::EachLine in( infile ); in; in.next() )
+        {
+          XXX << *in << endl;
+        }
+     }
+
+     return still_running;
+}
+
+
+}
+}
+
+
diff --git a/zypp/base/InterProcessMutex.h b/zypp/base/InterProcessMutex.h
new file mode 100644 (file)
index 0000000..fa6eaaa
--- /dev/null
@@ -0,0 +1,129 @@
+
+#ifndef ZYPP_BASE_INTER_PROCESS_MUTEX_H
+#define ZYPP_BASE_INTER_PROCESS_MUTEX_H
+
+#include <string>
+#include "zypp/base/Fd.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/Pathname.h"
+
+namespace zypp
+{
+namespace base
+{
+
+class ZYppLockedException : public Exception
+{
+public:
+    ZYppLockedException( const std::string & msg_r,
+                         const std::string &name,
+                         pid_t locker_pid );
+    virtual ~ZYppLockedException() throw();
+    pid_t locker_pid() const { return _locker_pid; }
+    std::string name() const { return _name; }
+private:
+    pid_t _locker_pid;
+    std::string _name;
+};
+
+/**
+ *
+ * Inter process scoped lock implementation
+ *
+ * This mutex will allow only one writer process to
+ * reach a critical region protected by a mutex
+ * of the same name, if there are no readers
+ * at the same time.
+ *
+ * Multiple readers are allowed if there is no
+ * currently a writer.
+ *
+ */
+class InterProcessMutex : private base::NonCopyable
+{
+public:
+   /**
+    * Processes can be of two types
+    * Reader or Writer
+    */
+    enum ConsumerType
+    {
+        Reader,
+        Writer
+    };
+
+   /**
+    * options to alter the mutex behavor
+    */
+   class Options
+   {
+   public:
+       /**
+        * Options for a mutex of type \ref ptype
+        * with a given name and timeout.
+        * Default is name "zypp" and no timeout
+        * (wait till resource is free)
+        *
+        * The mutex type, Writer or Reader must be
+        * given explictly.
+        *
+        * The mutex will be handled using a lock file
+        * located on default library path if the
+        * library is running as root, and in users home
+        * directory if not.
+        *
+        */
+       Options( ConsumerType ptype,
+                const std::string &pname = "zypp",
+                int ptimeout = -1 );
+
+       /**
+        * set the path where the lockfile is
+        * created.
+        */
+       void setPath( const Pathname &base );
+
+       std::string name;
+       int timeout;
+       ConsumerType type;
+       Pathname base;
+   };
+    
+   /**
+    * Creates a mutex with a name and a timeout.
+    *
+    * default timeout is -1 which means no timeout
+    * at all, and the mutex will wait forever if
+    * other process is accessing the critical region
+    * for a mutex in with the same name.
+    *
+    * If the timeout is 0, then if the lock is acquired
+    * an exception will be thrown inmediately.
+    *
+    * Otherwise, the timeout exception will come after
+    * the timeout is reached.
+    *
+    */
+    InterProcessMutex( const Options &poptions );
+
+    /**
+     * Destructor, gives up the lock on the named
+     * resource.
+     */
+    ~InterProcessMutex();
+
+private:
+    bool isProcessRunning(pid_t pid_r);
+    Pathname lockFilePath() const;
+private:
+    shared_ptr<Fd> _fd;
+    Options _options;
+};
+
+
+} }
+
+
+#endif
+
diff --git a/zypp/base/Iterator.h b/zypp/base/Iterator.h
new file mode 100644 (file)
index 0000000..1c21b02
--- /dev/null
@@ -0,0 +1,285 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Iterator.h
+ *
+*/
+#ifndef ZYPP_BASE_ITERATOR_H
+#define ZYPP_BASE_ITERATOR_H
+
+#include <iterator>
+#include <utility>
+
+#include <boost/functional.hpp>
+#include <boost/iterator/filter_iterator.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/function_output_iterator.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** \defgroup ITERATOR Boost.Iterator Library
+   *
+   * \see http://www.boost.org/libs/iterator/doc/index.html
+   *
+   * \li \b counting_iterator: an iterator over a sequence of
+   *        consecutive values. Implements a "lazy sequence"
+   * \li \b filter_iterator: an iterator over the subset of elements
+   *        of some sequence which satisfy a given predicate
+   * \li \b function_output_iterator: an output iterator wrapping a
+   *        unary function object; each time an element is written into
+   *        the dereferenced iterator, it is passed as a parameter to
+   *        the function object.
+   * \li \b indirect_iterator: an iterator over the objects pointed-to
+   *        by the elements of some sequence.
+   * \li \b permutation_iterator: an iterator over the elements of
+   *        some random-access sequence, rearranged according to some
+   *        sequence of integer indices.
+   * \li \b reverse_iterator: an iterator which traverses the elements
+   *        of some bidirectional sequence in reverse. Corrects many of the shortcomings of C++98's std::reverse_iterator.
+   * \li \b shared_container_iterator: an iterator over elements of
+   *        a container whose lifetime is maintained by a shared_ptr
+   *        stored in the iterator.
+   * \li \b transform_iterator: an iterator over elements which are
+   *        the result of applying some functional transformation to
+   *        the elements of an underlying sequence. This component
+   *        also replaces the old projection_iterator_adaptor.
+   * \li \b zip_iterator: an iterator over tuples of the elements
+   *        at corresponding positions of heterogeneous underlying
+   *        iterators.
+   *
+   * There are in fact more interesting iterator concepts
+   * available than the ones listed above. Have a look at them.
+   *
+   * Some of the iterator types are already dragged into namespace
+   * zypp. Feel free to add what's missing.
+   *
+   * \todo Separate them into individual zypp header files.
+  */
+  //@{
+
+  /** \class filter_iterator
+   * An iterator over the subset of elements of some sequence
+   * which satisfy a given predicate.
+   *
+   * Provides boost::filter_iterator and boost::make_filter_iterator
+   * convenience function.
+   * \see http://www.boost.org/libs/iterator/doc/filter_iterator.html
+   * \code
+   * template <class Predicate, class Iterator>
+   *   filter_iterator<Predicate,Iterator>
+   *   make_filter_iterator(Predicate f, Iterator x, Iterator end = Iterator());
+   *
+   * template <class Predicate, class Iterator>
+   *   filter_iterator<Predicate,Iterator>
+   *   make_filter_iterator(Iterator x, Iterator end = Iterator());
+   * \endcode
+   * Remember the deduction rules for template arguments.
+   * \code
+   * struct MyDefaultConstructibleFilter;
+   * make_filter_iterator<MyDefaultConstructibleFilter>( c.begin(), c.end() );
+   * make_filter_iterator( MyDefaultConstructibleFilter(), c.begin(), c.end() );
+   * ...
+   * make_filter_iterator( resfilter::ByName("foo"), c.begin(), c.end() );
+   *
+   * \endcode
+  */
+  using boost::filter_iterator;
+  using boost::make_filter_iterator;
+
+  /** Convenience to create filter_iterator from container::begin(). */
+  template<class _Filter, class _Container>
+    filter_iterator<_Filter, typename _Container::const_iterator>
+    make_filter_begin( _Filter f, const _Container & c )
+    {
+      return make_filter_iterator( f, c.begin(), c.end() );
+    }
+
+  /** Convenience to create filter_iterator from container::begin(). */
+  template<class _Filter, class _Container>
+    filter_iterator<_Filter, typename _Container::const_iterator>
+    make_filter_begin( const _Container & c )
+    {
+      return make_filter_iterator( _Filter(), c.begin(), c.end() );
+    }
+
+  /** Convenience to create filter_iterator from container::end(). */
+  template<class _Filter, class _Container>
+    filter_iterator<_Filter, typename _Container::const_iterator>
+    make_filter_end( _Filter f, const _Container & c )
+    {
+      return make_filter_iterator( f, c.end(), c.end() );
+    }
+
+  /** Convenience to create filter_iterator from container::end(). */
+  template<class _Filter, class _Container>
+    filter_iterator<_Filter, typename _Container::const_iterator>
+    make_filter_end( const _Container & c )
+    {
+      return make_filter_iterator( _Filter(), c.end(), c.end() );
+    }
+
+  /** \class transform_iterator
+   * An iterator over elements which are the result of applying
+   * some functional transformation to the elements of an underlying
+   * sequence.
+   *
+   * Provides boost::transform_iterator and boost::make_transform_iterator
+   * convenience function.
+   * \see http://www.boost.org/libs/iterator/doc/transform_iterator.html
+   * \code
+   * template <class UnaryFunction, class Iterator>
+   *   transform_iterator<UnaryFunction, Iterator>
+   *   make_transform_iterator(Iterator it, UnaryFunction fun);
+   *
+   * template <class UnaryFunction, class Iterator>
+   *   transform_iterator<UnaryFunction, Iterator>
+   *   make_transform_iterator(Iterator it);
+   * \endcode
+  */
+  using boost::transform_iterator;
+  using boost::make_transform_iterator;
+
+  /** Functor taking a \c std::pair returning \c std::pair.first.
+   * \see MapKVIteratorTraits
+  */
+  template<class _Pair>
+    struct GetPairFirst : public std::unary_function<_Pair, const typename _Pair::first_type &>
+    {
+      const typename _Pair::first_type & operator()( const _Pair & pair_r ) const
+      { return pair_r.first; }
+    };
+
+  /** Functor taking a \c std::pair returning \c std::pair.second .
+   * \see MapKVIteratorTraits
+  */
+  template<class _Pair>
+    struct GetPairSecond : public std::unary_function<_Pair, const typename _Pair::second_type &>
+    {
+      const typename _Pair::second_type & operator()( const _Pair & pair_r ) const
+      { return pair_r.second; }
+    };
+
+  /** Traits for std::map key and value iterators.
+   *
+   * \ref GetPairFirst and \ref GetPairSecond help building a transform_iterator
+   * that iterates over keys or values of a std::map. Class MapKVIteratorTraits
+   * provides some typedefs, you usg. do not want to write explicitly.
+   *
+   * \code
+   * // typedefs
+   * typedef std::map<K,V> MapType;
+   *
+   * // transform_iterator<GetPairFirst<MapType::value_type>,  MapType::const_iterator>
+   * typedef MapKVIteratorTraits<MapType>::Key_const_iterator MapTypeKey_iterator;
+   * // transform_iterator<GetPairSecond<MapType::value_type>, MapType::const_iterator>
+   * typedef MapKVIteratorTraits<MapType>::Value_const_iterator  MapTypeValue_iterator;
+   *
+   * // usage
+   * MapType mymap;
+   *
+   * MapTypeKey_const_iterator keyBegin( make_map_key_begin( mymap ) );
+   * MapTypeKey_const_iterator keyEnd  ( make_map_key_end( mymap ) );
+   *
+   * MapTypeValue_const_iterator valBegin( make_map_value_begin( mymap ) );
+   * MapTypeValue_const_iterator valEnd  ( make_map_value_end( mymap ) );
+   *
+   * std::for_each( keyBegin, keyEnd, DoSomething() );
+   * std::for_each( valBegin, valEnd, DoSomething() );
+   * \endcode
+   *
+   * Or short:
+   *
+   * \code
+   * typedef std::map<K,V> MapType;
+   * MapType mymap;
+   *
+   * std::for_each( make_map_key_begin( mymap ),   make_map_key_end( mymap ),   DoSomething() );
+   * std::for_each( make_map_value_begin( mymap ), make_map_value_end( mymap ), DoSomething() );
+   * \endcode
+   */
+  template<class _Map>
+    struct MapKVIteratorTraits
+    {
+      /** The map type */
+      typedef _Map                       MapType;
+      /** The maps key type */
+      typedef typename _Map::key_type    KeyType;
+      /** The key iterator type */
+      typedef transform_iterator<GetPairFirst<typename MapType::value_type>,
+                                 typename MapType::const_iterator> Key_const_iterator;
+      /** The maps value (mapped) type */
+      typedef typename _Map::mapped_type ValueType;
+      /** The value iterator type */
+      typedef transform_iterator<GetPairSecond<typename MapType::value_type>,
+                                 typename MapType::const_iterator> Value_const_iterator;
+    };
+
+  /** Convenience to create the key iterator from container::begin() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Key_const_iterator make_map_key_begin( const _Map & map_r )
+    { return make_transform_iterator( map_r.begin(), GetPairFirst<typename _Map::value_type>() ); }
+
+  /** Convenience to create the key iterator from container::end() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Key_const_iterator make_map_key_end( const _Map & map_r )
+    { return make_transform_iterator( map_r.end(), GetPairFirst<typename _Map::value_type>() ); }
+
+  /** Convenience to create the value iterator from container::begin() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Value_const_iterator make_map_value_begin( const _Map & map_r )
+    { return make_transform_iterator( map_r.begin(), GetPairSecond<typename _Map::value_type>() ); }
+
+  /** Convenience to create the value iterator from container::end() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Value_const_iterator make_map_value_end( const _Map & map_r )
+    { return make_transform_iterator( map_r.end(), GetPairSecond<typename _Map::value_type>() ); }
+
+  /** Convenience to create the key iterator from container::lower_bound() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Key_const_iterator make_map_key_lower_bound( const _Map & map_r, const typename _Map::key_type & key_r )
+    { return make_transform_iterator( map_r.lower_bound( key_r ), GetPairFirst<typename _Map::value_type>() ); }
+
+  /** Convenience to create the key iterator from container::upper_bound() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Key_const_iterator make_map_key_upper_bound( const _Map & map_r, const typename _Map::key_type & key_r )
+    { return make_transform_iterator( map_r.upper_bound( key_r ), GetPairFirst<typename _Map::value_type>() ); }
+
+  /** Convenience to create the value iterator from container::lower_bound() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Value_const_iterator make_map_value_lower_bound( const _Map & map_r, const typename _Map::key_type & key_r )
+    { return make_transform_iterator( map_r.lower_bound( key_r ), GetPairSecond<typename _Map::value_type>() ); }
+
+  /** Convenience to create the value iterator from container::upper_bound() */
+  template<class _Map>
+    inline typename MapKVIteratorTraits<_Map>::Value_const_iterator make_map_value_upper_bound( const _Map & map_r, const typename _Map::key_type & key_r )
+    { return make_transform_iterator( map_r.upper_bound( key_r ), GetPairSecond<typename _Map::value_type>() ); }
+
+  /** \class function_output_iterator
+   * An output iterator wrapping a unary function object; each time an
+   * element is written into the dereferenced iterator, it is passed as
+   * a parameter to the function object.
+   *
+   * Provides boost::function_output_iterator and boost::make_function_output_iterator
+   * convenience function.
+   * \see http://www.boost.org/libs/iterator/doc/function_output_iterator.html
+   * \code
+   * template <class UnaryFunction>
+   *   function_output_iterator<UnaryFunction>
+   *   make_function_output_iterator(const UnaryFunction& f = UnaryFunction());
+   * \endcode
+  */
+  using boost::function_output_iterator;
+  using boost::make_function_output_iterator;
+
+  //@}
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_ITERATOR_H
diff --git a/zypp/base/LogControl.cc b/zypp/base/LogControl.cc
new file mode 100644 (file)
index 0000000..7732634
--- /dev/null
@@ -0,0 +1,446 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/LogControl.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/ProfilingFormater.h"
+#include "zypp/base/String.h"
+#include "zypp/Date.h"
+#include "zypp/PathInfo.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace log
+  { /////////////////////////////////////////////////////////////////
+
+    StdoutLineWriter::StdoutLineWriter()
+      : StreamLineWriter( std::cout )
+    {}
+
+    StderrLineWriter::StderrLineWriter()
+      : StreamLineWriter( std::cerr )
+    {}
+
+    FileLineWriter::FileLineWriter( const Pathname & file_r, mode_t mode_r )
+    {
+      if ( file_r == Pathname("-") )
+      {
+        _str = &std::cerr;
+      }
+      else
+      {
+        // set unbuffered write
+        std::ofstream * fstr = 0;
+        _outs.reset( (fstr = new std::ofstream( file_r.asString().c_str(), std::ios_base::app )) );
+        fstr->rdbuf()->pubsetbuf(0,0);
+        _str = &(*fstr);
+        if ( mode_r )
+        {
+          // not filesystem::chmod, as filesystem:: functions log,
+          // and this FileWriter is not yet in place.
+          ::chmod( file_r.asString().c_str(), mode_r );
+        }
+      }
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace log
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    // LineFormater
+    ///////////////////////////////////////////////////////////////////
+    std::string LogControl::LineFormater::format( const std::string & group_r,
+                                                  logger::LogLevel    level_r,
+                                                  const char *        file_r,
+                                                  const char *        func_r,
+                                                  int                 line_r,
+                                                  const std::string & message_r )
+    {
+      static char hostname[1024];
+      static char nohostname[] = "unknown";
+      std::string now( Date::now().form( "%Y-%m-%d %H:%M:%S" ) );
+      return str::form( "%s <%d> %s(%d) [%s] %s(%s):%d %s",
+                        now.c_str(), level_r,
+                        ( gethostname( hostname, 1024 ) ? nohostname : hostname ),
+                        getpid(),
+                        group_r.c_str(),
+                        file_r, func_r, line_r,
+                        message_r.c_str() );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace logger
+    { /////////////////////////////////////////////////////////////////
+
+      inline void putStream( const std::string & group_r, LogLevel level_r,
+                             const char * file_r, const char * func_r, int line_r,
+                             const std::string & buffer_r );
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : Loglinebuf
+      //
+      class Loglinebuf : public std::streambuf {
+
+      public:
+        /** */
+        Loglinebuf( const std::string & group_r, LogLevel level_r )
+        : _group( group_r )
+        , _level( level_r )
+        , _file( "" )
+        , _func( "" )
+        , _line( -1 )
+        {}
+        /** */
+        ~Loglinebuf()
+        {
+          if ( !_buffer.empty() )
+            writeout( "\n", 1 );
+        }
+
+        /** */
+        void tagSet( const char * fil_r, const char * fnc_r, int lne_r )
+        {
+          _file = fil_r;
+          _func = fnc_r;
+          _line = lne_r;
+        }
+
+      private:
+        /** */
+        virtual std::streamsize xsputn( const char * s, std::streamsize n )
+        { return writeout( s, n ); }
+        /** */
+        virtual int overflow( int ch = EOF )
+        {
+          if ( ch != EOF )
+            {
+              char tmp = ch;
+              writeout( &tmp, 1 );
+            }
+          return 0;
+        }
+        /** */
+        virtual int writeout( const char* s, std::streamsize n )
+        {
+          if ( s && n )
+            {
+              const char * c = s;
+              for ( int i = 0; i < n; ++i, ++c )
+                {
+                  if ( *c == '\n' ) {
+                    _buffer += std::string( s, c-s );
+                    logger::putStream( _group, _level, _file, _func, _line, _buffer );
+                    _buffer = std::string();
+                    s = c+1;
+                  }
+                }
+              if ( s < c )
+                {
+                  _buffer += std::string( s, c-s );
+                }
+            }
+          return n;
+        }
+
+      private:
+        std::string  _group;
+        LogLevel     _level;
+        const char * _file;
+        const char * _func;
+        int          _line;
+        std::string  _buffer;
+      };
+
+      ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : Loglinestream
+      //
+      class Loglinestream {
+
+      public:
+        /** */
+        Loglinestream( const std::string & group_r, LogLevel level_r )
+        : _mybuf( group_r, level_r )
+        , _mystream( &_mybuf )
+        {}
+        /** */
+        ~Loglinestream()
+        { _mystream.flush(); }
+
+      public:
+        /** */
+        std::ostream & getStream( const char * fil_r, const char * fnc_r, int lne_r )
+        {
+          _mybuf.tagSet( fil_r, fnc_r, lne_r );
+          return _mystream;
+        }
+
+      private:
+        Loglinebuf   _mybuf;
+        std::ostream _mystream;
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : LogControlImpl
+      //
+      /** LogControl implementation (Singleton).
+       *
+       * \note There is a slight difference in using the _lineFormater and _lineWriter!
+       * \li \c _lineFormater must not be NULL (create default LogControl::LineFormater)
+       * \li \c _lineWriter is NULL if no logging is performed, this way we can pass
+       *        _no_stream as logstream to the application, and avoid unnecessary formating
+       *        of logliles, which would then be discarded when passed to some dummy
+       *        LineWriter.
+      */
+      struct LogControlImpl
+      {
+      public:
+       bool isExcessive()
+       { return _excessive; }
+
+        void excessive( bool onOff_r )
+        { _excessive = onOff_r; }
+
+        /** NULL _lineWriter indicates no loggin. */
+        void setLineWriter( const shared_ptr<LogControl::LineWriter> & writer_r )
+        { _lineWriter = writer_r; }
+
+        shared_ptr<LogControl::LineWriter> getLineWriter() const
+        { return _lineWriter; }
+
+        /** Assert \a _lineFormater is not NULL. */
+        void setLineFormater( const shared_ptr<LogControl::LineFormater> & format_r )
+        {
+          if ( format_r )
+            _lineFormater = format_r;
+          else
+            _lineFormater.reset( new LogControl::LineFormater );
+        }
+
+        void logfile( const Pathname & logfile_r, mode_t mode_r = 0640 )
+        {
+          if ( logfile_r.empty() )
+            setLineWriter( shared_ptr<LogControl::LineWriter>() );
+          else if ( logfile_r == Pathname( "-" ) )
+            setLineWriter( shared_ptr<LogControl::LineWriter>(new log::StderrLineWriter) );
+          else
+            setLineWriter( shared_ptr<LogControl::LineWriter>(new log::FileLineWriter(logfile_r, mode_r)) );
+        }
+
+      private:
+        std::ostream _no_stream;
+        bool         _excessive;
+
+        shared_ptr<LogControl::LineFormater> _lineFormater;
+        shared_ptr<LogControl::LineWriter>   _lineWriter;
+
+      public:
+        /** Provide the log stream to write (logger interface) */
+        std::ostream & getStream( const std::string & group_r,
+                                  LogLevel            level_r,
+                                  const char *        file_r,
+                                  const char *        func_r,
+                                  const int           line_r )
+        {
+          if ( ! _lineWriter )
+            return _no_stream;
+          if ( level_r == E_XXX && !_excessive )
+            return _no_stream;
+
+          if ( !_streamtable[group_r][level_r] )
+            {
+              _streamtable[group_r][level_r].reset( new Loglinestream( group_r, level_r ) );
+            }
+          return _streamtable[group_r][level_r]->getStream( file_r, func_r, line_r );
+        }
+
+        /** Format and write out a logline from Loglinebuf. */
+        void putStream( const std::string & group_r,
+                        LogLevel            level_r,
+                        const char *        file_r,
+                        const char *        func_r,
+                        int                 line_r,
+                        const std::string & message_r )
+        {
+          if ( _lineWriter )
+            _lineWriter->writeOut( _lineFormater->format( group_r, level_r,
+                                                          file_r, func_r, line_r,
+                                                          message_r ) );
+        }
+
+      private:
+        typedef shared_ptr<Loglinestream>        StreamPtr;
+        typedef std::map<LogLevel,StreamPtr>     StreamSet;
+        typedef std::map<std::string,StreamSet>  StreamTable;
+        /** one streambuffer per group and level */
+        StreamTable _streamtable;
+
+      private:
+        /** Singleton ctor.
+         * No logging per default, unless enabled via $ZYPP_LOGFILE.
+        */
+        LogControlImpl()
+        : _no_stream( NULL )
+        , _excessive( getenv("ZYPP_FULLLOG") )
+        , _lineFormater( new LogControl::LineFormater )
+        {
+          if ( getenv("ZYPP_LOGFILE") )
+            logfile( getenv("ZYPP_LOGFILE") );
+
+          if ( getenv("ZYPP_PROFILING") )
+          {
+            shared_ptr<LogControl::LineFormater> formater(new ProfilingFormater);
+            setLineFormater(formater);
+          }
+        }
+
+        ~LogControlImpl()
+        {
+          _lineWriter.reset();
+        }
+
+      public:
+        /** The LogControlImpl singleton
+         * \note As most dtors log, it is inportant that the
+         * LogControlImpl instance is the last static variable
+         * destructed. At least destucted after all statics
+         * which log from their dtor.
+        */
+        static LogControlImpl & instance();
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      // 'THE' LogControlImpl singleton
+      inline LogControlImpl & LogControlImpl::instance()
+      {
+        static LogControlImpl _instance;
+        return _instance;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+
+      /** \relates LogControlImpl Stream output */
+      inline std::ostream & operator<<( std::ostream & str, const LogControlImpl & obj )
+      {
+        return str << "LogControlImpl";
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      // Access from logger::
+      //
+      ///////////////////////////////////////////////////////////////////
+
+      std::ostream & getStream( const char * group_r,
+                                LogLevel     level_r,
+                                const char * file_r,
+                                const char * func_r,
+                                const int    line_r )
+      {
+        return LogControlImpl::instance().getStream( group_r,
+                                                   level_r,
+                                                   file_r,
+                                                   func_r,
+                                                   line_r );
+      }
+
+      /** That's what Loglinebuf calls.  */
+      inline void putStream( const std::string & group_r, LogLevel level_r,
+                             const char * file_r, const char * func_r, int line_r,
+                             const std::string & buffer_r )
+      {
+        LogControlImpl::instance().putStream( group_r, level_r,
+                                            file_r, func_r, line_r,
+                                            buffer_r );
+      }
+
+      bool isExcessive()
+      { return LogControlImpl::instance().isExcessive(); }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace logger
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LogControl
+    //  Forward to LogControlImpl singleton.
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    using logger::LogControlImpl;
+
+    void LogControl::logfile( const Pathname & logfile_r )
+    { LogControlImpl::instance().logfile( logfile_r ); }
+
+    void LogControl::logfile( const Pathname & logfile_r, mode_t mode_r )
+    { LogControlImpl::instance().logfile( logfile_r, mode_r ); }
+
+    shared_ptr<LogControl::LineWriter> LogControl::getLineWriter() const
+    { return LogControlImpl::instance().getLineWriter(); }
+
+    void LogControl::setLineWriter( const shared_ptr<LineWriter> & writer_r )
+    { LogControlImpl::instance().setLineWriter( writer_r ); }
+
+    void LogControl::setLineFormater( const shared_ptr<LineFormater> & formater_r )
+    { LogControlImpl::instance().setLineFormater( formater_r ); }
+
+    void LogControl::logNothing()
+    { LogControlImpl::instance().setLineWriter( shared_ptr<LineWriter>() ); }
+
+    void LogControl::logToStdErr()
+    { LogControlImpl::instance().setLineWriter( shared_ptr<LineWriter>( new log::StderrLineWriter ) ); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // LogControl::TmpExcessive
+    //
+    ///////////////////////////////////////////////////////////////////
+    LogControl::TmpExcessive::TmpExcessive()
+    { LogControlImpl::instance().excessive( true ); }
+    LogControl::TmpExcessive::~TmpExcessive()
+    { LogControlImpl::instance().excessive( false );  }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const LogControl & obj )
+    {
+      return str << LogControlImpl::instance();
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/LogControl.h b/zypp/base/LogControl.h
new file mode 100644 (file)
index 0000000..84262a5
--- /dev/null
@@ -0,0 +1,208 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/LogControl.h
+ *
+*/
+#ifndef ZYPP_BASE_LOGCONTROL_H
+#define ZYPP_BASE_LOGCONTROL_H
+
+#include <iosfwd>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace log
+  { /////////////////////////////////////////////////////////////////
+
+    /** If you want to log the (formated) loglines by yourself,
+     *  derive from this, and overload \c writeOut.
+     * Expect \a formated_r to be a formated log line without trailing \c NL.
+     * Ready to be written to the log.
+     */
+    struct LineWriter
+    {
+      virtual void writeOut( const std::string & /*formated_r*/ )
+      {}
+      virtual ~LineWriter()
+      {}
+    };
+
+    /** Base class for ostream based \ref LineWriter */
+    struct StreamLineWriter : public LineWriter
+    {
+      StreamLineWriter( std::ostream & str_r ) : _str( &str_r ) {}
+
+      virtual void writeOut( const std::string & formated_r )
+      { (*_str) << formated_r << std::endl; }
+
+      protected:
+        StreamLineWriter() : _str( 0 ) {}
+        std::ostream *_str;
+    };
+
+    /** \ref LineWriter to stdout. */
+    struct StdoutLineWriter : public StreamLineWriter
+    {
+      StdoutLineWriter();
+    };
+
+    /** \ref LineWriter to stderr. */
+    struct StderrLineWriter : public StreamLineWriter
+    {
+      StderrLineWriter();
+    };
+
+    /** \ref LineWriter to file.
+     * If \c mode_r is not \c 0, \c file_r persissions are changed
+     * accordingly. \c "-" logs to \c cerr.
+    */
+    struct FileLineWriter : public StreamLineWriter
+    {
+      FileLineWriter( const Pathname & file_r, mode_t mode_r = 0 );
+      protected:
+        shared_ptr<void> _outs;
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace log
+  ///////////////////////////////////////////////////////////////////
+
+
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LogControl
+    //
+    /** Maintain logfile related options.
+     * \note A Singleton using a Singleton implementation class,
+     * that's why there is no _pimpl like in other classes.
+    */
+    class LogControl
+    {
+      friend std::ostream & operator<<( std::ostream & str, const LogControl & obj );
+
+    public:
+      /** Singleton access. */
+      static LogControl instance()
+      { return LogControl(); }
+
+
+      /** \see \ref log::LineWriter */
+      typedef log::LineWriter LineWriter;
+
+      /** If you want to format loglines by yourself,
+       *  derive from this, and overload \c format.
+       * Return a formated logline without trailing \c NL.
+       * Ready to be written to the log.
+      */
+      struct LineFormater
+      {
+        virtual std::string format( const std::string & /*group_r*/,
+                                    logger::LogLevel    /*level_r*/,
+                                    const char *        /*file_r*/,
+                                    const char *        /*func_r*/,
+                                    int                 /*line_r*/,
+                                    const std::string & /*message_r*/ );
+        virtual ~LineFormater() {}
+      };
+
+    public:
+      /** Assign a LineFormater.
+       * If you want to format loglines by yourself. NULL installs the
+       * default formater.
+      */
+      void setLineFormater( const shared_ptr<LineFormater> & formater_r );
+
+    public:
+      /** Set path for the logfile.
+       * Permission for logfiles is set to 0640 unless an explicit mode_t
+       * value is given. An empty pathname turns off logging. <tt>"-"</tt>
+       * logs to std::err.
+       * \throw if \a logfile_r is not usable.
+      */
+      void logfile( const Pathname & logfile_r );
+      void logfile( const Pathname & logfile_r, mode_t mode_r );
+
+      /** Turn off logging. */
+      void logNothing();
+
+      /** Log to std::err. */
+      void logToStdErr();
+
+    public:
+      /** Get the current LineWriter */
+      shared_ptr<LineWriter> getLineWriter() const;
+
+      /** Assign a LineWriter.
+       * If you want to log the (formated) loglines by yourself.
+       * NULL turns off logging (same as logNothing)
+       * \see \ref log::LineWriter
+       */
+      void setLineWriter( const shared_ptr<LineWriter> & writer_r );
+
+    public:
+      /** Turn on excessive logging for the lifetime of this object.*/
+      struct TmpExcessive
+      {
+        TmpExcessive();
+        ~TmpExcessive();
+      };
+
+      /** Exchange LineWriter for the lifetime of this object.
+       * \see \ref log::LineWriter
+      */
+      struct TmpLineWriter
+      {
+        TmpLineWriter( const shared_ptr<LineWriter> & writer_r = shared_ptr<LineWriter>() )
+          : _writer( LogControl::instance().getLineWriter() )
+        { LogControl::instance().setLineWriter( writer_r ); }
+
+        /** Convenience ctor taking over ownership of an allocated LineWriter.
+         *\code
+         * TmpLineWriter mylw( new log::StderrLineWriter );
+         * \endcode
+        */
+        template<class _LineWriter>
+        TmpLineWriter( _LineWriter * _allocated_r )
+          : _writer( LogControl::instance().getLineWriter() )
+        { LogControl::instance().setLineWriter( shared_ptr<LineWriter>( _allocated_r ) ); }
+
+        ~TmpLineWriter()
+        { LogControl::instance().setLineWriter( _writer ); }
+
+      private:
+        shared_ptr<LineWriter> _writer;
+      };
+
+    private:
+      /** Default ctor: Singleton */
+      LogControl()
+      {}
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates LogControl Stream output */
+    std::ostream & operator<<( std::ostream & str, const LogControl & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_LOGCONTROL_H
diff --git a/zypp/base/LogTools.h b/zypp/base/LogTools.h
new file mode 100644 (file)
index 0000000..a1e257c
--- /dev/null
@@ -0,0 +1,391 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/LogTools.h
+ *
+*/
+#ifndef ZYPP_BASE_LOGTOOLS_H
+#define ZYPP_BASE_LOGTOOLS_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <list>
+#include <set>
+#include <map>
+
+#include "zypp/base/Tr1hash.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Deprecated.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  using std::endl;
+
+  /** Print range defined by iterators (multiline style).
+   * \code
+   * intro [ pfx ITEM [ { sep ITEM }+ ] sfx ] extro
+   * \endcode
+   *
+   * The defaults print the range enclosed in \c {}, one item per
+   * line indented by 2 spaces.
+   * \code
+   * {
+   *   item1
+   *   item2
+   * }
+   * {} // on empty range
+   * \endcode
+   *
+   * A comma separated list enclosed in \c () would be:
+   * \code
+   * dumpRange( stream, begin, end, "(", "", ", ", "", ")" );
+   * // or shorter:
+   * dumpRangeLine( stream, begin, end );
+   * \endcode
+   *
+   * \note Some special handling is required for printing std::maps.
+   * Therefore iomaipulators \ref dumpMap, \ref dumpKeys and \ref dumpValues
+   * are provided.
+   * \code
+   * std::map<string,int> m;
+   * m["a"]=1;
+   * m["b"]=2;
+   * m["c"]=3;
+   *
+   * dumpRange( DBG, dumpMap(m).begin(), dumpMap(m).end() ) << endl;
+   * // {
+   * //   [a] = 1
+   * //   [b] = 2
+   * //   [c] = 3
+   * // }
+   * dumpRange( DBG, dumpKeys(m).begin(), dumpKeys(m).end() ) << endl;
+   * // {
+   * //   a
+   * //   b
+   * //   c
+   * // }
+   * dumpRange( DBG, dumpValues(m).begin(), dumpValues(m).end() ) << endl;
+   * // {
+   * //   1
+   * //   2
+   * //   3
+   * // }
+   * dumpRangeLine( DBG, dumpMap(m).begin(), dumpMap(m).end() ) << endl;
+   * // ([a] = 1, [b] = 2, [c] = 3)
+   * dumpRangeLine( DBG, dumpKeys(m).begin(), dumpKeys(m).end() ) << endl;
+   * // (a, b, c)
+   * dumpRangeLine( DBG, dumpValues(m).begin(), dumpValues(m).end() ) << endl;
+   * // (1, 2, 3)
+   * \endcode
+  */
+  template<class _Iterator>
+    std::ostream & dumpRange( std::ostream & str,
+                              _Iterator begin, _Iterator end,
+                              const std::string & intro = "{",
+                              const std::string & pfx   = "\n  ",
+                              const std::string & sep   = "\n  ",
+                              const std::string & sfx   = "\n",
+                              const std::string & extro = "}" )
+    {
+      str << intro;
+      if ( begin != end )
+        {
+          str << pfx << *begin;
+          for (  ++begin; begin != end; ++begin )
+            str << sep << *begin;
+          str << sfx;
+        }
+      return str << extro;
+    }
+
+  /** Print range defined by iterators (single line style).
+   * \see dumpRange
+   */
+  template<class _Iterator>
+    std::ostream & dumpRangeLine( std::ostream & str,
+                                  _Iterator begin, _Iterator end )
+    { return dumpRange( str, begin, end, "(", "", ", ", "", ")" ); }
+
+
+  template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::vector<_Tp> & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
+
+  template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::set<_Tp> & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
+
+  template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::tr1::unordered_set<_Tp> & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
+
+  template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::multiset<_Tp> & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
+
+  template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::list<_Tp> & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace _logtoolsdetail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    // mapEntry
+    ///////////////////////////////////////////////////////////////////
+
+    /** std::pair wrapper for std::map output.
+     * Just because we want a special output format for std::pair
+     * used in a std::map. The mapped std::pair is printed as
+     * <tt>[key] = value</tt>.
+    */
+    template<class _Pair>
+      class MapEntry
+      {
+      public:
+        MapEntry( const _Pair & pair_r )
+        : _pair( &pair_r )
+        {}
+
+        const _Pair & pair() const
+        { return *_pair; }
+
+      private:
+        const _Pair *const _pair;
+      };
+
+    /** \relates MapEntry Stream output. */
+    template<class _Pair>
+      std::ostream & operator<<( std::ostream & str, const MapEntry<_Pair> & obj )
+      {
+        return str << '[' << obj.pair().first << "] = " << obj.pair().second;
+      }
+
+    /** \relates MapEntry Convenience function to create MapEntry from std::pair. */
+    template<class _Pair>
+      MapEntry<_Pair> mapEntry( const _Pair & pair_r )
+      { return MapEntry<_Pair>( pair_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+    // dumpMap
+    ///////////////////////////////////////////////////////////////////
+
+    /** std::map wrapper for stream output.
+     * Uses a transform_iterator to wrap the std::pair into MapEntry.
+     *
+     */
+    template<class _Map>
+      class DumpMap
+      {
+      public:
+        typedef _Map                        MapType;
+        typedef typename _Map::value_type   PairType;
+        typedef MapEntry<PairType>          MapEntryType;
+
+        struct Transformer : public std::unary_function<PairType, MapEntryType>
+        {
+          MapEntryType operator()( const PairType & pair_r ) const
+          { return mapEntry( pair_r ); }
+        };
+
+        typedef transform_iterator<Transformer, typename MapType::const_iterator>
+                MapEntry_const_iterator;
+
+      public:
+        DumpMap( const _Map & map_r )
+        : _map( &map_r )
+        {}
+
+        const _Map & map() const
+        { return *_map; }
+
+        MapEntry_const_iterator begin() const
+        { return make_transform_iterator( map().begin(), Transformer() ); }
+
+        MapEntry_const_iterator end() const
+        { return make_transform_iterator( map().end(), Transformer() );}
+
+      private:
+        const _Map *const _map;
+      };
+
+    /** \relates DumpMap Stream output. */
+    template<class _Map>
+      std::ostream & operator<<( std::ostream & str, const DumpMap<_Map> & obj )
+      { return dumpRange( str, obj.begin(), obj.end() ); }
+
+    /** \relates DumpMap Convenience function to create DumpMap from std::map. */
+    template<class _Map>
+      DumpMap<_Map> dumpMap( const _Map & map_r )
+      { return DumpMap<_Map>( map_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+    // dumpKeys
+    ///////////////////////////////////////////////////////////////////
+
+    /** std::map wrapper for stream output of keys.
+     * Uses MapKVIterator iterate and write the key values.
+     * \code
+     * std::map<...> mymap;
+     * std::cout << dumpKeys(mymap) << std::endl;
+     * \endcode
+     */
+    template<class _Map>
+      class DumpKeys
+      {
+      public:
+        typedef typename MapKVIteratorTraits<_Map>::Key_const_iterator MapKey_const_iterator;
+
+      public:
+        DumpKeys( const _Map & map_r )
+        : _map( &map_r )
+        {}
+
+        const _Map & map() const
+        { return *_map; }
+
+        MapKey_const_iterator begin() const
+        { return make_map_key_begin( map() ); }
+
+        MapKey_const_iterator end() const
+        { return make_map_key_end( map() ); }
+
+      private:
+        const _Map *const _map;
+      };
+
+    /** \relates DumpKeys Stream output. */
+    template<class _Map>
+      std::ostream & operator<<( std::ostream & str, const DumpKeys<_Map> & obj )
+      { return dumpRange( str, obj.begin(), obj.end() ); }
+
+    /** \relates DumpKeys Convenience function to create DumpKeys from std::map. */
+    template<class _Map>
+      DumpKeys<_Map> dumpKeys( const _Map & map_r )
+      { return DumpKeys<_Map>( map_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+    // dumpValues
+    ///////////////////////////////////////////////////////////////////
+
+    /** std::map wrapper for stream output of values.
+     * Uses MapKVIterator iterate and write the values.
+     * \code
+     * std::map<...> mymap;
+     * std::cout << dumpValues(mymap) << std::endl;
+     * \endcode
+     */
+    template<class _Map>
+      class DumpValues
+      {
+      public:
+        typedef typename MapKVIteratorTraits<_Map>::Value_const_iterator MapValue_const_iterator;
+
+      public:
+        DumpValues( const _Map & map_r )
+        : _map( &map_r )
+        {}
+
+        const _Map & map() const
+        { return *_map; }
+
+        MapValue_const_iterator begin() const
+        { return make_map_value_begin( map() ); }
+
+        MapValue_const_iterator end() const
+        { return make_map_value_end( map() ); }
+
+      private:
+        const _Map *const _map;
+      };
+
+    /** \relates DumpValues Stream output. */
+    template<class _Map>
+      std::ostream & operator<<( std::ostream & str, const DumpValues<_Map> & obj )
+      { return dumpRange( str, obj.begin(), obj.end() ); }
+
+    /** \relates DumpValues Convenience function to create DumpValues from std::map. */
+    template<class _Map>
+      DumpValues<_Map> dumpValues( const _Map & map_r )
+      { return DumpValues<_Map>( map_r ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace _logtoolsdetail
+  ///////////////////////////////////////////////////////////////////
+
+  // iomanipulator
+  using _logtoolsdetail::mapEntry;   // std::pair as '[key] = value'
+  using _logtoolsdetail::dumpMap;    // dumpRange '[key] = value'
+  using _logtoolsdetail::dumpKeys;   // dumpRange keys
+  using _logtoolsdetail::dumpValues; // dumpRange values
+
+  template<class _Key, class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::map<_Key, _Tp> & obj )
+    { return str << dumpMap( obj ); }
+
+  template<class _Key, class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::tr1::unordered_map<_Key, _Tp> & obj )
+    { return str << dumpMap( obj ); }
+
+  template<class _Key, class _Tp>
+    std::ostream & operator<<( std::ostream & str, const std::multimap<_Key, _Tp> & obj )
+    { return str << dumpMap( obj ); }
+
+  /** Print stream status bits.
+   * Prints the values of a streams \c good, \c eof, \c failed and \c bad bit.
+   *
+   * \code
+   *  [g___] - good
+   *  [_eF_] - eof and fail bit set
+   *  [__FB] - fail and bad bit set
+   * \endcode
+  */
+  inline std::ostream & operator<<( std::ostream & str, const std::basic_ios<char> & obj )
+  {
+    std::string ret( "[" );
+    ret += ( obj.good() ? 'g' : '_' );
+    ret += ( obj.eof()  ? 'e' : '_' );
+    ret += ( obj.fail() ? 'F' : '_' );
+    ret += ( obj.bad()  ? 'B' : '_' );
+    ret += "]";
+    return str << ret;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // iomanipulator: str << dump(val) << ...
+  // calls:         std::ostream & dumpOn( std::ostream & str, const Type & obj )
+  ///////////////////////////////////////////////////////////////////
+
+  namespace detail
+  {
+    template<class _Tp>
+    struct Dump
+    {
+      Dump( const _Tp & obj_r ) : _obj( obj_r ) {}
+      const _Tp & _obj;
+    };
+
+    template<class _Tp>
+    std::ostream & operator<<( std::ostream & str, const Dump<_Tp> & obj )
+    { return dumpOn( str, obj._obj ); }
+  }
+
+  template<class _Tp>
+  detail::Dump<_Tp> dump( const _Tp & obj_r )
+  { return detail::Dump<_Tp>(obj_r); }
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_LOGTOOLS_H
diff --git a/zypp/base/Logger.h b/zypp/base/Logger.h
new file mode 100644 (file)
index 0000000..4ba1955
--- /dev/null
@@ -0,0 +1,124 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Logger.h
+ *
+*/
+#ifndef ZYPP_BASE_LOGGER_H
+#define ZYPP_BASE_LOGGER_H
+
+#include <iosfwd>
+#include <cstring>
+
+/** \defgroup ZYPP_BASE_LOGGER_MACROS ZYPP_BASE_LOGGER_MACROS
+ *  Convenience macros for logging.
+ *
+ * The macros finaly call @ref getStream, providing appropriate arguments,
+ * to return the log stream.
+ *
+ * @code
+ * _DBG("foo") << ....
+ * @endcode
+ * Logs a debug message for group @a "foo".
+ *
+ * @code
+ * #undef ZYPP_BASE_LOGGER_LOGGROUP
+ * #define ZYPP_BASE_LOGGER_LOGGROUP "foo"
+ *
+ * DBG << ....
+ * @endcode
+ * Defines group @a "foo" as default for log messages and logs a
+ * debug message.
+ */
+/*@{*/
+
+#ifndef ZYPP_BASE_LOGGER_LOGGROUP
+/** Default log group if undefined. */
+#define ZYPP_BASE_LOGGER_LOGGROUP "DEFINE_LOGGROUP"
+#endif
+
+#define XXX _XXX( ZYPP_BASE_LOGGER_LOGGROUP )
+#define DBG _DBG( ZYPP_BASE_LOGGER_LOGGROUP )
+#define MIL _MIL( ZYPP_BASE_LOGGER_LOGGROUP )
+#define WAR _WAR( ZYPP_BASE_LOGGER_LOGGROUP )
+#define ERR _ERR( ZYPP_BASE_LOGGER_LOGGROUP )
+#define SEC _SEC( ZYPP_BASE_LOGGER_LOGGROUP )
+#define INT _INT( ZYPP_BASE_LOGGER_LOGGROUP )
+#define USR _USR( ZYPP_BASE_LOGGER_LOGGROUP )
+
+#define _XXX(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_XXX )
+#define _DBG(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP"++", zypp::base::logger::E_MIL )
+#define _MIL(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_MIL )
+#define _WAR(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_WAR )
+#define _ERR(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_ERR )
+#define _SEC(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_SEC )
+#define _INT(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_INT )
+#define _USR(GROUP) ZYPP_BASE_LOGGER_LOG( GROUP, zypp::base::logger::E_USR )
+
+#define _BASEFILE ( *__FILE__ == '/' ? strrchr( __FILE__, '/' ) + 1 : __FILE__ )
+
+/** Actual call to @ref getStream. */
+#define ZYPP_BASE_LOGGER_LOG(GROUP,LEVEL) \
+        zypp::base::logger::getStream( GROUP, LEVEL, _BASEFILE, __FUNCTION__, __LINE__ )
+
+/*@}*/
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace logger
+    { /////////////////////////////////////////////////////////////////
+
+      /** Definition of log levels.
+       *
+       * @see getStream
+      */
+      enum LogLevel {
+        E_XXX = 999, /**< Excessive logging. */
+        E_DBG = 0,   /**< Debug or verbose. */
+        E_MIL,       /**< Milestone. */
+        E_WAR,       /**< Warning. */
+        E_ERR,       /**< Error. */
+        E_SEC,       /**< Secutrity related. */
+        E_INT,       /**< Internal error. */
+        E_USR        /**< User log. */
+      };
+
+      /** Return a log stream to write on.
+       *
+       * The returned log stream is determined by @a group_r and
+       * @a level_r. The remaining arguments @a file_r, @a func_r
+       * and @a line_r are expected to denote the location in the
+       * source code that issued the message.
+       *
+       * @note You won't call @ref getStream directly, but use the
+       * @ref ZYPP_BASE_LOGGER_MACROS.
+      */
+      extern std::ostream & getStream( const char * group_r,
+                                       LogLevel     level_r,
+                                       const char * file_r,
+                                       const char * func_r,
+                                       const int    line_r );
+      extern bool isExcessive();
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace logger
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_LOGGER_H
diff --git a/zypp/base/Measure.cc b/zypp/base/Measure.cc
new file mode 100644 (file)
index 0000000..e5b3915
--- /dev/null
@@ -0,0 +1,270 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Measure.cc
+ *
+*/
+extern "C"
+{
+#include <sys/times.h>
+#include <unistd.h>
+}
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Measure.h"
+#include "zypp/base/String.h"
+
+using std::endl;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "Measure"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace debug
+  { /////////////////////////////////////////////////////////////////
+
+      /** Times measured by \ref Measure. */
+      struct Tm
+      {
+        Tm()
+        : _real( 0 )
+        , _proc( tmsEmpty )
+        {}
+
+        void get()
+        {
+          _real = ::time(NULL);
+          ::times( &_proc );
+        }
+
+        Tm operator-( const Tm & rhs ) const
+        {
+          Tm ret( *this );
+          ret._real -= rhs._real;
+          ret._proc.tms_utime -= rhs._proc.tms_utime;
+          ret._proc.tms_stime -= rhs._proc.tms_stime;
+          ret._proc.tms_cutime -= rhs._proc.tms_cutime;
+          ret._proc.tms_cstime -= rhs._proc.tms_cstime;
+          return ret;
+        }
+
+        std::string asString() const
+        {
+          std::string ret( timeStr( _real ) );
+          ret += " (u ";
+          ret += timeStr( asSec( _proc.tms_utime ) );
+          ret += " s ";
+          ret += timeStr( asSec( _proc.tms_stime ) );
+          ret += " c ";
+          ret += timeStr( asSec( _proc.tms_cutime + _proc.tms_cstime ) );
+          ret += ")";
+          return ret;
+        }
+
+        std::string stringIf( clock_t ticks_r, const std::string & tag_r ) const
+        {
+          std::string ret;
+          if ( ticks_r )
+            {
+              ret += tag_r;
+              ret += timeStr( asSec( ticks_r ) );
+            }
+          return ret;
+        }
+
+        double asSec( clock_t ticks_r ) const
+        { return double(ticks_r) / ticks; }
+
+        std::string timeStr( time_t sec_r ) const
+        {
+          time_t h = sec_r/3600;
+          sec_r -= h*3600;
+          time_t m = sec_r/60;
+          sec_r -= m*60;
+          if ( h )
+            return str::form( "%lu:%02lu:%02lu", h, m, sec_r );
+          if ( m )
+            return str::form( "%lu:%02lu", m, sec_r );
+          return str::form( "%lu", sec_r );
+        }
+
+        std::string timeStr( double sec_r ) const
+        {
+          time_t h = time_t(sec_r)/3600;
+          sec_r -= h*3600;
+          time_t m = time_t(sec_r)/60;
+          sec_r -= m*60;
+          if ( h )
+            return str::form( "%lu:%02lu:%05.2lf", h, m, sec_r );
+          if ( m )
+            return str::form( "%lu:%05.2lf", m, sec_r );
+          return str::form( "%.2lf", sec_r );
+        }
+
+        /** Systems ticks per second. */
+        static const long ticks;
+        /** Empty struct tms. */
+        static const struct tms tmsEmpty;
+        /** Real time via \c ::time. */
+        time_t      _real;
+        /** Process times via \c ::times. */
+        struct tms  _proc;
+      };
+
+      const struct tms Tm::tmsEmpty = { 0, 0, 0, 0 };
+      const long Tm::ticks = sysconf(_SC_CLK_TCK);
+
+      /** \refers Tm Stream output. */
+      std::ostream & operator<<( std::ostream & str, const Tm & obj )
+      {
+        return str << obj.asString();
+      }
+
+      
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Measure::Impl
+    //
+    /** Measure implementation. */
+    class Measure::Impl
+    {
+    public:
+      Impl( const std::string & ident_r )
+      : _ident  ( ident_r )
+      , _seq    ( 0 )
+      {
+        INT << "START MEASURE(" << _ident << ")" << endl;
+        _start.get();
+      }
+
+      ~Impl()
+      {
+        _stop.get();
+        ++_seq;
+        std::ostream & str( INT << "MEASURE(" << _ident << ") " );
+        dumpMeasure( str );
+      }
+
+      void restart()
+      {
+        INT << "RESTART MEASURE(" << _ident << ")" << endl;
+        _start = _stop;
+      }
+      
+      void elapsed() const
+      {
+        _stop.get();
+        ++_seq;
+        std::ostream & str( INT << "ELAPSED(" << _ident << ") " );
+        dumpMeasure( str );
+        _elapsed = _stop;
+      }
+
+    private:
+      std::ostream & dumpMeasure( std::ostream & str_r ) const
+      {
+        str_r << ( _stop - _start );
+        if ( _seq > 1 ) // diff to previous _elapsed
+          {
+            str_r << " [" << ( _stop - _elapsed ) << "]";
+          }
+        return str_r << endl;
+      }
+
+    private:
+      std::string       _ident;
+      Tm               _start;
+      mutable unsigned _seq;
+      mutable Tm       _elapsed;
+      mutable Tm       _stop;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Measure
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::Measure
+    // METHOD TYPE : Ctor
+    //
+    Measure::Measure()
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::Measure
+    // METHOD TYPE : Ctor
+    //
+    Measure::Measure( const std::string & ident_r )
+    : _pimpl( new Impl( ident_r ) )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::~Measure
+    // METHOD TYPE : Dtor
+    //
+    Measure::~Measure()
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::start
+    // METHOD TYPE : void
+    //
+    void Measure::start( const std::string & ident_r )
+    {
+      stop();
+      _pimpl.reset( new Impl( ident_r ) );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::start
+    // METHOD TYPE : void
+    //
+    void Measure::restart()
+    {
+      _pimpl->restart();
+    }
+    
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::
+    // METHOD TYPE : void
+    //
+    void Measure::elapsed() const
+    {
+      if ( _pimpl )
+        _pimpl->elapsed();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Measure::
+    // METHOD TYPE : void
+    //
+    void Measure::stop()
+    {
+      _pimpl.reset();
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace debug
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/Measure.h b/zypp/base/Measure.h
new file mode 100644 (file)
index 0000000..8c2806b
--- /dev/null
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Measure.h
+ *
+*/
+#ifndef ZYPP_BASE_MEASURE_H
+#define ZYPP_BASE_MEASURE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace debug
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Measure
+    //
+    /** Tool to measure elapsed real and process times.
+     *
+     * Timer is started by either passing a string to the ctor,
+     * or callign \ref start. The string passed is printed on
+     * all messages to help identifying the timer.
+     *
+     * Elapsed time is printed on calling \ref elapsed (timer
+     * keeps running) or \ref stop.
+     *
+     * Calling \ref stop, stops the timer. The same, if the timer
+     * goes out of scope.
+     *
+     * Elapsed time is printed as:
+     * \code
+     * 'REAL TIME' (u 'USER TIME' s 'SYSTEM TIME' c 'TIME OF CHILDREN')
+     * \endcode
+     * In brackets the time elapsed since a previous call to \ref elapsed.
+     * All units are seconds.
+     *
+     * \code
+     * Measure m( "Parse" );
+     * ...
+     * m.elapsed();
+     * ...
+     * m.elapsed();
+     * ...
+     * m.elapsed();
+     * ...
+     * m.stop();
+     *
+     * // START MEASURE(Parse)
+     * // ELAPSED(Parse)  0 (u 0.13 s 0.00 c 0.00)
+     * // ELAPSED(Parse)  0 (u 0.15 s 0.02 c 0.00) [ 0 (u 0.02 s 0.02 c 0.00)]
+     * // ELAPSED(Parse)  0 (u 0.17 s 0.02 c 0.00) [ 0 (u 0.02 s 0.00 c 0.00)]
+     * // MEASURE(Parse)  0 (u 0.17 s 0.02 c 0.00) [ 0 (u 0.00 s 0.00 c 0.00)]
+     * \endcode
+    */
+    class Measure
+    {
+    public:
+      /** Default Ctor does nothing. */
+      Measure();
+
+      /** Ctor taking \a ident_r string and auto starts timer. */
+      explicit
+      Measure( const std::string & ident_r );
+
+      /** Dtor. */
+      ~Measure();
+
+      /** Start timer for \a ident_r string.
+       * Implies stoping a running timer.
+      */
+      void start( const std::string & ident_r = std::string() );
+
+      /** re start the timer without reset-ing it. */
+      void restart();
+      
+      /** Print elapsed time for a running timer.
+       * Timer keeps on running.
+      */
+      void elapsed() const;
+
+      /** Stop a running timer. */
+      void stop();
+
+    private:
+      /** Implementation. */
+      class Impl;
+      /** Pointer to implementation. */
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace debug
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_MEASURE_H
diff --git a/zypp/base/NonCopyable.h b/zypp/base/NonCopyable.h
new file mode 100644 (file)
index 0000000..78d2cfb
--- /dev/null
@@ -0,0 +1,34 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/NonCopyable.h
+*/
+#ifndef ZYPP_BASE_NONCOPYABLE_H
+#define ZYPP_BASE_NONCOPYABLE_H
+
+#include <boost/noncopyable.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    /** Ensure derived classes cannot be copied.
+     * Use private inheritance.
+    */
+    typedef boost::noncopyable NonCopyable;
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_NONCOPYABLE_H
diff --git a/zypp/base/ProfilingFormater.cc b/zypp/base/ProfilingFormater.cc
new file mode 100644 (file)
index 0000000..28a21ac
--- /dev/null
@@ -0,0 +1,66 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/ProfilingFormater.cc
+ *
+*/
+
+#include <sys/time.h>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/String.h"
+#include "zypp/Date.h"
+#include "zypp/PathInfo.h"
+
+
+#include "zypp/base/ProfilingFormater.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    // ProfilingFormater
+    ///////////////////////////////////////////////////////////////////
+    
+    std::string ProfilingFormater::format( const std::string & group_r,
+                                                  logger::LogLevel    level_r,
+                                                  const char *        file_r,
+                                                  const char *        func_r,
+                                                  int                 line_r,
+                                                  const std::string & message_r )
+    {
+       struct timeval tp;
+       int rtn;
+       rtn = gettimeofday( &tp, NULL);
+       
+        return str::form( "%ld.%ld [%d] <%d> %s(%s):%d %s",
+                        tp.tv_sec,
+                        tp.tv_usec,
+                        level_r,
+                        getpid(),
+                        /*group_r.c_str(),*/
+                        file_r, func_r, line_r,
+                        message_r.c_str() );
+    }
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/ProfilingFormater.h b/zypp/base/ProfilingFormater.h
new file mode 100644 (file)
index 0000000..de9e047
--- /dev/null
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/ProfilingFormater.h
+ *
+*/
+#ifndef ZYPP_BASE_PROFILINGFORMATER_H
+#define ZYPP_BASE_PROFILINGFORMATER_H
+
+#include <iosfwd>
+#include <string>
+#include "zypp/base/LogControl.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    struct ProfilingFormater : public LogControl::LineFormater
+    {
+      virtual std::string format( const std::string & /*group_r*/,
+                                  logger::LogLevel    /*level_r*/,
+                                  const char *        /*file_r*/,
+                                  const char *        /*func_r*/,
+                                  int                 /*line_r*/,
+                                  const std::string & /*message_r*/ );
+      virtual ~ProfilingFormater() {}
+    };
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_PROFILINGFORMATER_H
diff --git a/zypp/base/ProvideNumericId.h b/zypp/base/ProvideNumericId.h
new file mode 100644 (file)
index 0000000..2990e2d
--- /dev/null
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/ProvideNumericId.h
+ *
+*/
+#ifndef ZYPP_BASE_PROVIDENUMERICID_H
+#define ZYPP_BASE_PROVIDENUMERICID_H
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ProvideNumericId
+    //
+    /** Base class for objects providing a numeric Id.
+     * The ctor creates a NumericId from some static counter.
+     *
+     * The only assertion is that \c 0 is not used as an Id,
+     * \b unless the derived class explicitly requests this by
+     * using \ref ProvideNumericId( const void *const ).
+     *
+     * Why should you want to use \c 0 as an Id? E.g if your class
+     * provides some (singleton) No-object. Might be desirable to
+     * make the No-object have No-Id.
+     *
+     * \code
+     * struct Foo : public base::ProvideNumericId<Foo,unsigned>
+     * {};
+     * Foo foo;
+     * foo.numericId(); // returns foo's NumericId.
+     * \endcode
+    */
+    template<class _Derived, class _NumericIdType>
+      struct ProvideNumericId
+      {
+      public:
+        /** \return The objects numeric Id. */
+        _NumericIdType numericId() const
+        { return _numericId; }
+
+      protected:
+        /** Default ctor */
+        ProvideNumericId()
+        : _numericId( nextId() )
+        {}
+        /** Copy ctor */
+        ProvideNumericId( const ProvideNumericId & /*rhs*/ )
+        : _numericId( nextId() )
+        {}
+        /** Assign */
+        ProvideNumericId & operator=( const ProvideNumericId & /*rhs*/ )
+        { return *this; }
+        /** Dtor */
+        ~ProvideNumericId()
+        {}
+      protected:
+        /** No-Id ctor (0).
+         * Explicitly request Id \c 0. Use it with care!
+        */
+        ProvideNumericId( const void *const )
+        : _numericId( 0 )
+        {}
+      private:
+        /** Provide the next Id to use. */
+        static _NumericIdType nextId()
+        {
+          static _NumericIdType _staticCounter = 0;
+          // Assert not returning 0
+          return ++_staticCounter;
+        }
+        /**  */
+        const _NumericIdType _numericId;
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_PROVIDENUMERICID_H
diff --git a/zypp/base/PtrTypes.h b/zypp/base/PtrTypes.h
new file mode 100644 (file)
index 0000000..8af6174
--- /dev/null
@@ -0,0 +1,508 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/PtrTypes.h
+ *  \ingroup ZYPP_SMART_PTR
+ *  \see ZYPP_SMART_PTR
+*/
+#ifndef ZYPP_BASE_PTRTYPES_H
+#define ZYPP_BASE_PTRTYPES_H
+
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    /** \defgroup ZYPP_SMART_PTR Smart pointer types
+     *  Smart pointer types.
+     *
+     * Namespace zypp provides 3 smart pointer types \b using the
+     * boost smart pointer library.
+     *
+     * \li \c scoped_ptr Simple sole ownership of single objects. Noncopyable.
+     *
+     * \li \c shared_ptr Object ownership shared among multiple pointers
+     *
+     * \li \c weak_ptr Non-owning observers of an object owned by shared_ptr.
+     *
+     * And \ref zypp::RW_pointer, as wrapper around a smart pointer,
+     * poviding \c const correct read/write access to the object it refers.
+    */
+    /*@{*/
+
+    /** shared_ptr custom deleter doing nothing.
+     * A custom deleter is a function being called when the
+     * last shared_ptr goes out of score. Per default the
+     * object gets deleted, but you can insall custom deleters
+     * as well. This one does nothing.
+     *
+     * \code
+     *  // Some class providing a std::istream
+     *  struct InpuStream
+     * {
+     *   // Per deafult use std::cin.
+     *   InputStream()
+     *   : _stream( &std::cin, NullDeleter() )
+     *   {}
+     *   // Or read from a file.
+     *   InputStream( const Pathname & file_r )
+     *   : _stream( new ifgzstream( _path.asString().c_str() ) )
+     *   {}
+     *   // Or use a stream priovided by the application.
+     *   InputStream( std::istream & stream_r )
+     *   : _stream( &stream_r, NullDeleter() )
+     *   {}
+     *
+     *   std::istream & stream()
+     *   { return *_stream; }
+     *
+     * private:
+     *   shared_ptr<std::istream> _stream;
+     * };
+     * \endcode
+    */
+    struct NullDeleter
+    {
+      void operator()( const void *const ) const
+      {}
+    };
+
+    /** \class scoped_ptr */
+    using boost::scoped_ptr;
+
+    /** \class shared_ptr */
+    using boost::shared_ptr;
+
+    /** \class weak_ptr */
+    using boost::weak_ptr;
+
+    /** \class intrusive_ptr */
+    using boost::intrusive_ptr;
+
+    /** */
+    using boost::static_pointer_cast;
+    /**  */
+    using boost::const_pointer_cast;
+    /**  */
+    using boost::dynamic_pointer_cast;
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace std
+{ /////////////////////////////////////////////////////////////////
+
+  // namespace sub {
+  //    class Foo;
+  //    typedef zypp::intrusive_ptr<Foo> Foo_Ptr; // see DEFINE_PTR_TYPE(NAME) macro below
+  // }
+
+  // Defined in namespace std g++ finds the output operator (König-Lookup),
+  // even if we typedef the pointer in a different namespace than ::zypp.
+  // Otherwise we had to define an output operator always in the same namespace
+  // as the typedef (else g++ will just print the pointer value).
+
+  /** \relates zypp::shared_ptr Stream output. */
+  template<class _D>
+  inline std::ostream & operator<<( std::ostream & str, const zypp::shared_ptr<_D> & obj )
+  {
+    if ( obj )
+      return str << *obj;
+    return str << std::string("NULL");
+  }
+  /** \relates zypp::shared_ptr Stream output. */
+  template<class _D>
+  inline std::ostream & dumpOn( std::ostream & str, const zypp::shared_ptr<_D> & obj )
+  {
+    if ( obj )
+      return dumpOn( str, *obj );
+    return str << std::string("NULL");
+  }
+
+  /** \relates zypp::intrusive_ptr Stream output. */
+  template<class _D>
+  inline std::ostream & operator<<( std::ostream & str, const zypp::intrusive_ptr<_D> & obj )
+  {
+    if ( obj )
+      return str << *obj;
+    return str << std::string("NULL");
+  }
+  /** \relates zypp::intrusive_ptr Stream output. */
+  template<class _D>
+  inline std::ostream & dumpOn( std::ostream & str, const zypp::intrusive_ptr<_D> & obj )
+  {
+    if ( obj )
+      return dumpOn( str, *obj );
+    return str << std::string("NULL");
+  }
+  /////////////////////////////////////////////////////////////////
+} // namespace std
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // RW_pointer traits
+    //
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Don't forgett to provide versions for _Ptr and _constPtr,
+     * esp. if creation a of temporary is not acceptable (eg. when
+     * checking the ref count value).
+    */
+    namespace rw_pointer {
+
+      template<class _D>
+        struct Shared
+        {
+          typedef shared_ptr<_D>       _Ptr;
+          typedef shared_ptr<const _D> _constPtr;
+          /** Check whether pointer is not shared. */
+          bool unique( const _constPtr & ptr_r )
+          { return !ptr_r || ptr_r.unique(); }
+          bool unique( const _Ptr & ptr_r )
+          { return !ptr_r || ptr_r.unique(); }
+          /** Return number of references. */
+          long use_count( const _constPtr & ptr_r ) const
+          { return ptr_r.use_count(); }
+          long use_count( const _Ptr & ptr_r ) const
+          { return ptr_r.use_count(); }
+        };
+
+      template<class _D>
+        struct Intrusive
+        {
+          typedef intrusive_ptr<_D>       _Ptr;
+          typedef intrusive_ptr<const _D> _constPtr;
+          /** Check whether pointer is not shared. */
+          bool unique( const _constPtr & ptr_r )
+          { return !ptr_r || (ptr_r->refCount() <= 1); }
+          bool unique( const _Ptr & ptr_r )
+          { return !ptr_r || (ptr_r->refCount() <= 1); }
+          /** Return number of references. */
+          long use_count( const _constPtr & ptr_r ) const
+          { return ptr_r ? ptr_r->refCount() : 0; }
+          long use_count( const _Ptr & ptr_r ) const
+          { return ptr_r ? ptr_r->refCount() : 0; }
+        };
+
+       template<class _D>
+        struct Scoped
+        {
+          typedef scoped_ptr<_D>       _Ptr;
+          typedef scoped_ptr<const _D> _constPtr;
+          /** Check whether pointer is not shared. */
+          bool unique( const _constPtr & ptr_r )
+          { return true; }
+          bool unique( const _Ptr & ptr_r )
+          { return true; }
+          /** Return number of references. */
+          long use_count( const _constPtr & ptr_r ) const
+          { return ptr_r ? 1 : 0; }
+          long use_count( const _Ptr & ptr_r ) const
+          { return ptr_r ? 1 : 0; }
+        };
+
+   }
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : RW_pointer
+    //
+    /** Wrapper for \c const correct access via \ref ZYPP_SMART_PTR.
+     *
+     * zypp::RW_pointer<tt>\<_D,_Traits></tt> stores a \ref ZYPP_SMART_PTR
+     * of type \c _Traits::_Ptr, which must be convertible into a <tt>_D *</tt>.
+     * Pointer style access (via \c -> and \c *) offers a <tt>const _D *</tt> in const
+     * a context, otherwise a <tt>_D *</tt>. Thus \em RW_ means \em read/write,
+     * as you get a different type, dependent on whether you're allowed to
+     * read or write.
+     *
+     * Forwarding access from an interface to an implemantation class, an
+     * RW_pointer prevents const interface methods from accidentally calling
+     * nonconst implementation methods.
+     *
+     * The second template argument defaults to
+     * <tt>_Traits = rw_pointer::Shared<_D></tt> thus wraping a
+     * <tt>shared_ptr<_D></tt>. To wrap an <tt>intrusive_ptr<_D></tt>
+     * use <tt>rw_pointer::Intrusive<_D></tt>.
+     *
+     * \see zypp::RWCOW_pointer for 'copy on write' functionality.
+     *
+     * \code
+     * #include "zypp/base/PtrTypes.h"
+     *
+     * class Foo
+     * {
+     *   ...
+     *   private:
+     *     // Implementation class
+     *     struct Impl;
+     *     // Pointer to implementation; actually a shared_ptr<Impl>
+     *     RW_pointer<Impl> _pimpl;
+     *
+     *     void baa()       { _pimpl->... } // is Impl *
+     *     void baa() const { _pimpl->... } // is Impl const *
+     * };
+     * \endcode
+    */
+    template<class _D, class _Traits = rw_pointer::Shared<_D> >
+      struct RW_pointer
+      {
+        typedef typename _Traits::_Ptr               _Ptr;
+        typedef typename _Traits::_constPtr          _constPtr;
+        typedef typename _Ptr::unspecified_bool_type unspecified_bool_type;
+
+        explicit
+        RW_pointer( typename _Ptr::element_type * dptr = 0 )
+        : _dptr( dptr )
+        {}
+
+        explicit
+        RW_pointer( _Ptr dptr )
+        : _dptr( dptr )
+        {}
+
+        void reset()
+        { _Ptr().swap( _dptr ); }
+
+        void reset( typename _Ptr::element_type * dptr )
+        { _Ptr( dptr ).swap( _dptr ); }
+
+        void swap( RW_pointer & rhs )
+        { _dptr.swap( rhs._dptr ); }
+
+        void swap( _Ptr & rhs )
+        { _dptr.swap( rhs ); }
+
+        operator unspecified_bool_type() const
+        { return _dptr; }
+
+        const _D & operator*() const
+        { return *_dptr; };
+
+        const _D * operator->() const
+        { return _dptr.get(); }
+
+        const _D * get() const
+        { return _dptr.get(); }
+
+        _D & operator*()
+        { return *_dptr; }
+
+        _D * operator->()
+        { return _dptr.get(); }
+
+        _D * get()
+        { return _dptr.get(); }
+
+      public:
+        bool unique() const
+       { return _Traits().unique( _dptr ); }
+
+       long use_count() const
+       { return _Traits().use_count( _dptr ); }
+
+        _constPtr getPtr() const
+        { return _dptr; }
+
+        _Ptr getPtr()
+        { return _dptr; }
+
+      private:
+        _Ptr _dptr;
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RW_pointer Stream output.
+     *
+     * Print the \c _D object the RW_pointer refers, or \c "NULL"
+     * if the pointer is \c NULL.
+    */
+    template<class _D, class _Ptr>
+      inline std::ostream &
+      operator<<( std::ostream & str, const RW_pointer<_D, _Ptr> & obj )
+      {
+        if ( obj.get() )
+          return str << *obj.get();
+        return str << std::string("NULL");
+      }
+
+    /** \relates RW_pointer */
+    template<class _D, class _Ptr>
+      inline bool
+      operator==( const RW_pointer<_D, _Ptr> & lhs, const RW_pointer<_D, _Ptr> & rhs )
+      {
+        return( lhs.get() == rhs.get() );
+      }
+
+    /** \relates RW_pointer */
+    template<class _D, class _Ptr>
+      inline bool
+      operator!=( const RW_pointer<_D, _Ptr> & lhs, const RW_pointer<_D, _Ptr> & rhs )
+      {
+        return ! ( lhs == rhs );
+      }
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : RWCOW_pointer
+    //
+    /** \ref RW_pointer supporting 'copy on write' functionality.
+     *
+     * \em Write access to the underlying object creates a copy, iff
+     * the object is shared.
+     *
+     * See \ref RW_pointer.
+    */
+    template<class _D, class _Traits = rw_pointer::Shared<_D> >
+      struct RWCOW_pointer
+      {
+        typedef typename _Traits::_Ptr               _Ptr;
+        typedef typename _Traits::_constPtr          _constPtr;
+        typedef typename _Ptr::unspecified_bool_type unspecified_bool_type;
+
+        explicit
+        RWCOW_pointer( typename _Ptr::element_type * dptr = 0 )
+        : _dptr( dptr )
+        {}
+
+        explicit
+        RWCOW_pointer( _Ptr dptr )
+        : _dptr( dptr )
+        {}
+
+        void reset()
+        { _Ptr().swap( _dptr ); }
+
+        void reset( typename _Ptr::element_type * dptr )
+        { _Ptr( dptr ).swap( _dptr ); }
+
+        void swap( RWCOW_pointer & rhs )
+        { _dptr.swap( rhs._dptr ); }
+
+        void swap( _Ptr & rhs )
+        { _dptr.swap( rhs ); }
+
+        operator unspecified_bool_type() const
+        { return _dptr; }
+
+        const _D & operator*() const
+        { return *_dptr; };
+
+        const _D * operator->() const
+        { return _dptr.get(); }
+
+        const _D * get() const
+        { return _dptr.get(); }
+
+        _D & operator*()
+        { assertUnshared(); return *_dptr; }
+
+        _D * operator->()
+        { assertUnshared(); return _dptr.get(); }
+
+        _D * get()
+        { assertUnshared(); return _dptr.get(); }
+
+      public:
+        bool unique() const
+       { return _Traits().unique( _dptr ); }
+
+       long use_count() const
+       { return _Traits().use_count( _dptr ); }
+
+        _constPtr getPtr() const
+        { return _dptr; }
+
+        _Ptr getPtr()
+        { assertUnshared(); return _dptr; }
+
+      private:
+
+        void assertUnshared()
+        {
+          if ( !unique() )
+            _Ptr( rwcowClone( _dptr.get() ) ).swap( _dptr );
+        }
+
+      private:
+        _Ptr _dptr;
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RWCOW_pointer Clone the underlying object.
+     * Calls \a rhs <tt>-\>clone()</tt>. Being defined as a
+     * function outside \ref RWCOW_pointer allows to overload
+     * it, in case a specific \a _D does not have <tt>clone()</tt>.
+    */
+    template<class _D>
+      inline _D * rwcowClone( const _D * rhs )
+      { return rhs->clone(); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RWCOW_pointer Stream output.
+     *
+     * Print the \c _D object the RWCOW_pointer refers, or \c "NULL"
+     * if the pointer is \c NULL.
+    */
+    template<class _D, class _Ptr>
+      inline std::ostream &
+      operator<<( std::ostream & str, const RWCOW_pointer<_D, _Ptr> & obj )
+      {
+        if ( obj.get() )
+          return str << *obj.get();
+        return str << std::string("NULL");
+      }
+
+    /** \relates RWCOW_pointer */
+    template<class _D, class _Ptr>
+      inline bool
+      operator==( const RWCOW_pointer<_D, _Ptr> & lhs, const RWCOW_pointer<_D, _Ptr> & rhs )
+      {
+        return( lhs.get() == rhs.get() );
+      }
+
+    /** \relates RWCOW_pointer */
+    template<class _D, class _Ptr>
+      inline bool
+      operator!=( const RWCOW_pointer<_D, _Ptr> & lhs, const RWCOW_pointer<_D, _Ptr> & rhs )
+      {
+        return ! ( lhs == rhs );
+      }
+
+    ///////////////////////////////////////////////////////////////////
+
+    /*@}*/
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+/** Forward declaration of Ptr types */
+#define DEFINE_PTR_TYPE(NAME) \
+class NAME;                                                      \
+extern void intrusive_ptr_add_ref( const NAME * );               \
+extern void intrusive_ptr_release( const NAME * );               \
+typedef zypp::intrusive_ptr<NAME>       NAME##_Ptr;        \
+typedef zypp::intrusive_ptr<const NAME> NAME##_constPtr;
+
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_PTRTYPES_H
diff --git a/zypp/base/Random.cc b/zypp/base/Random.cc
new file mode 100644 (file)
index 0000000..123da94
--- /dev/null
@@ -0,0 +1,54 @@
+
+#include <cstdlib>
+#include <cstdio>
+#include <iostream>
+#include <fcntl.h>
+#include "zypp/base/Random.h"
+
+using namespace std;
+
+namespace zypp { namespace base {
+
+ // Taken from KApplication
+int random_int()
+{
+  static bool init = false;
+  if (!init)
+  {
+      unsigned int seed;
+      init = true;
+      int fd = open("/dev/urandom", O_RDONLY);
+      if (fd < 0 || ::read(fd, &seed, sizeof(seed)) != sizeof(seed))
+      {
+            // No /dev/urandom... try something else.
+            srand(getpid());
+            seed = rand()+time(0);
+      }
+      if (fd >= 0) close(fd);
+      srand(seed);
+  }
+  return rand();
+}
+
+// Taken from KApplication
+std::string random_string(int length)
+{
+  if (length <=0 ) return std::string();
+
+  std::string str; str.resize( length );
+  int i = 0;
+  while (length--)
+  {
+      int r=::random() % 62;
+      r+=48;
+      if (r>57) r+=7;
+      if (r>90) r+=6;
+      str[i++] =  char(r);
+      // so what if I work backwards?
+  }
+  return str;
+}
+
+
+} }
+
diff --git a/zypp/base/Random.h b/zypp/base/Random.h
new file mode 100644 (file)
index 0000000..9c988f9
--- /dev/null
@@ -0,0 +1,48 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_BASE_Random_H
+#define ZYPP_BASE_Random_H
+
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { ///////////////////////////
+    // Taken from KApplication
+    int random_int();
+    // Taken from KApplication
+    std::string random_string(int length);
+
+
+    /** Return a random number from <tt>[0,RAND_MAX[</tt>. */
+    inline unsigned random()
+    {
+      return random_int();
+    }
+    /** Return a random number from <tt>[0,size_r[</tt>. */
+    inline unsigned random( unsigned size_r )
+    {
+      return random_int() % size_r;
+    }
+    /** Return a random number from <tt>[min_r,min_r+size_r[</tt>. */
+    inline unsigned random( unsigned min_r, unsigned size_r )
+    {
+      return min_r + random( size_r );
+    }
+
+
+  } //ns base
+} // ns zypp
+
+#endif
+
diff --git a/zypp/base/ReferenceCounted.cc b/zypp/base/ReferenceCounted.cc
new file mode 100644 (file)
index 0000000..ccd5ff7
--- /dev/null
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/ReferenceCounted.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/ReferenceCounted.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ReferenceCounted::ReferenceCounted()
+    : _counter( 0 )
+    {}
+
+    ReferenceCounted::ReferenceCounted( const ReferenceCounted & /*rhs*/ )
+    : _counter( 0 )
+    {}
+
+    ReferenceCounted::~ReferenceCounted()
+    {
+      if ( _counter )
+        {
+          INT << "~ReferenceCounted: nonzero reference count" << std::endl;
+          throw std::out_of_range( "~ReferenceCounted: nonzero reference count" );
+        }
+    }
+
+    void ReferenceCounted::unrefException() const
+    {
+      INT << "ReferenceCounted::unref: zero reference count" << std::endl;
+      throw std::out_of_range( "ReferenceCounted::unref: zero reference count" );
+    }
+
+    std::ostream & ReferenceCounted::dumpOn( std::ostream & str ) const
+    {
+      return str << "ReferenceCounted(@" << (const void *)this
+                 << "<=" << _counter << ")";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/ReferenceCounted.h b/zypp/base/ReferenceCounted.h
new file mode 100644 (file)
index 0000000..9ec2b02
--- /dev/null
@@ -0,0 +1,143 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/ReferenceCounted.h
+ *
+*/
+#ifndef ZYPP_BASE_REFERENCECOUNTED_H
+#define ZYPP_BASE_REFERENCECOUNTED_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ReferenceCounted
+    //
+    /** Base class for reference counted objects.
+     * \todo Make counter thread safe.
+    */
+    class ReferenceCounted
+    {
+      /** Stream output via dumpOn. */
+      friend std::ostream & operator<<( std::ostream & str, const ReferenceCounted & obj );
+
+    public:
+      /** Default ctor.
+       * Initial reference count is zero.
+      */
+      ReferenceCounted();
+
+      /** Copy ctor.
+       * Initial reference count is zero.
+      */
+      ReferenceCounted( const ReferenceCounted & rhs );
+
+      /** Dtor.
+       * \throw std::out_of_range if reference count is not zero.
+      */
+      virtual ~ReferenceCounted();
+
+      /** Assignment.
+       * Reference count remains untouched.
+      */
+      ReferenceCounted & operator=( const ReferenceCounted & )
+      { return *this; }
+
+    public:
+      /** Return reference counter value. */
+      unsigned refCount() const
+      { return _counter; }
+
+      /** Add a reference. */
+      void ref() const
+      { ref_to( ++_counter ); }
+
+      /** Release a reference.
+       * Deletes the object if reference count gets zero.
+       * \throw std::out_of_range if reference count is zero.
+      */
+      void unref() const
+      {
+        if ( !_counter )
+          unrefException(); // will throw!
+        if ( --_counter )
+          unref_to( _counter );
+        else
+          delete this;
+      }
+
+      /** Called by zypp::intrusive_ptr to add a reference.
+       * \see ZYPP_SMART_PTR
+      */
+      static void add_ref( const ReferenceCounted * ptr_r )
+      { if( ptr_r ) ptr_r->ref(); }
+
+      /** Called by zypp::intrusive_ptr to add a reference.
+       * \see ZYPP_SMART_PTR
+      */
+      static void release( const ReferenceCounted * ptr_r )
+      { if( ptr_r ) ptr_r->unref(); }
+
+    protected:
+      /** Overload to realize std::ostream & operator\<\<. */
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      /** Trigger derived classes after refCount was increased. */
+      virtual void ref_to( unsigned /* rep_cnt_r */ ) const {}
+
+      /** Trigger derived classes after refCount was decreased.
+       * No trigger is sent, if refCount got zero (i.e. the
+       * object is deleted).
+       **/
+      virtual void unref_to( unsigned /* rep_cnt_r */ ) const {}
+
+    private:
+      /** The reference counter. */
+      mutable unsigned _counter;
+
+      /** Throws Exception on unref. */
+      void unrefException() const;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ReferenceCounted intrusive_ptr hook to add_ref. */
+    inline void intrusive_ptr_add_ref( const ReferenceCounted * ptr_r )
+    { ReferenceCounted::add_ref( ptr_r ); }
+
+    /** \relates ReferenceCounted intrusive_ptr hook to release. */
+    inline void intrusive_ptr_release( const ReferenceCounted * ptr_r )
+    { ReferenceCounted::release( ptr_r ); }
+
+    /** \relates ReferenceCounted Stream output. */
+    inline std::ostream & operator<<( std::ostream & str, const ReferenceCounted & obj )
+    { return obj.dumpOn( str ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#define IMPL_PTR_TYPE(NAME) \
+void intrusive_ptr_add_ref( const NAME * ptr_r )               \
+{ zypp::base::ReferenceCounted::add_ref( ptr_r ); }                  \
+void intrusive_ptr_release( const NAME * ptr_r )               \
+{ zypp::base::ReferenceCounted::release( ptr_r ); }
+
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_REFERENCECOUNTED_H
diff --git a/zypp/base/Regex.cc b/zypp/base/Regex.cc
new file mode 100644 (file)
index 0000000..ac37a98
--- /dev/null
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Regex.cc
+ *
+*/
+#include <cstdio>
+#include <cstdarg>
+
+#include <iostream>
+
+#include "zypp/base/Regex.h"
+
+using namespace zypp;
+using namespace zypp::str;
+
+regex::regex()
+  : m_flags(match_extended)
+  , m_valid(false)
+{
+
+}
+
+void regex::assign(const std::string& str,int flags)
+{
+  m_valid = true;
+  m_str = str;
+  m_flags = flags;
+  int err;
+  char errbuff[100];
+  if (!(flags & normal)) {
+    flags |= match_extended;
+    flags &= ~(normal);
+  }
+
+  if ((err = regcomp(&m_preg, str.c_str(), flags))) {
+    m_valid = false;
+    regerror(err, &m_preg, errbuff, sizeof(errbuff));
+    ZYPP_THROW(regex_error(std::string(errbuff)));
+  }
+}
+
+regex::regex(const std::string& str, int flags)
+{
+  assign(str, flags);
+}
+
+regex::~regex() throw()
+{
+  if (m_valid)
+    regfree(&m_preg);
+}
+
+bool zypp::str::regex_match(const char * s, smatch& matches, const regex& regex)
+{
+  bool r = s && regex.m_valid && !regexec(&regex.m_preg, s, 12, &matches.pmatch[0], 0);
+  if (r)
+    matches.match_str = s;
+  return r;
+}
+
+bool zypp::str::regex_match(const char * s,  const regex& regex)
+{
+  return s && !regexec(&regex.m_preg, s, 0, NULL, 0);
+}
+
+smatch::smatch()
+{
+  memset(&pmatch, -1, sizeof(pmatch));
+}
+
+std::string smatch::operator[](unsigned i) const
+{
+  if (i < sizeof(pmatch)/sizeof(*pmatch) && pmatch[i].rm_so != -1)
+    return match_str.substr(pmatch[i].rm_so, pmatch[i].rm_eo-pmatch[i].rm_so);
+  return std::string();
+}
+
+
+unsigned smatch::size() const
+{
+  unsigned matches = 0;
+  while (matches <  ((sizeof(pmatch)/sizeof(*pmatch))-1) && pmatch[matches+1].rm_so != -1) {
+    //    std::cout << "match[" << matches << "]: *" << (*this)[matches
+    //        +1] << "*" << std::endl;
+    matches++;
+  }
+
+  return matches;
+}
diff --git a/zypp/base/Regex.h b/zypp/base/Regex.h
new file mode 100644 (file)
index 0000000..77424e6
--- /dev/null
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Regex.h
+ *
+*/
+#ifndef ZYPP_BASE_REGEX_H
+#define ZYPP_BASE_REGEX_H
+
+#include <string>
+#include <regex.h>
+
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  /** String related utilities and \ref ZYPP_STR_REGEX.
+   \see \ref ZYPP_STR_REGEX
+  */
+  namespace str
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /** \defgroup ZYPP_STR_REGEX Regular expressions
+     *
+     * Namespace zypp::str regular expressions \b using the glibc regex library.
+     *
+     * \see \ref sat::AttrMatcher string matcher supporing regex, globing, etc.
+     *
+     * regex
+     * regex_match
+     * smatch
+     */
+
+    typedef Exception regex_error;
+
+    class smatch;
+    class regex;
+
+    bool regex_match(const char * s, str::smatch& matches, const regex& regex);
+    inline bool regex_match(const std::string& s, str::smatch& matches, const regex& regex)
+    { return regex_match( s.c_str(), matches, regex ); }
+
+    bool regex_match(const char * s, const regex& regex);
+    inline bool regex_match(const std::string& s, const regex& regex)
+    { return regex_match( s.c_str(), regex ); }
+
+    /**
+     * \see \ref sat::AttrMatcher string matcher supporing regex, globing, etc.
+     * \ingroup ZYPP_STR_REGEX
+     */
+    class regex {
+    public:
+
+      enum RegFlags {
+        optimize = 0,
+        match_extra = 0,
+        icase = REG_ICASE,
+        nosubs = REG_NOSUB,
+        match_extended = REG_EXTENDED,
+        normal = 1<<16
+      };
+
+      regex();
+      regex(const std::string& s,int flags = match_extended);
+      ~regex() throw();
+
+      regex(const regex & rhs)
+      { assign(rhs.m_str, rhs.m_flags); }
+
+      regex & operator=(const regex & rhs)
+      { assign(rhs.m_str, rhs.m_flags); return *this; }
+
+      /**
+       * string representation of the regular expression
+       */
+      std::string asString() const
+      { return m_str; }
+
+    public:
+      /** Expert backdoor. Returns pointer to the compiled regex for direct use in regexec() */
+      regex_t * get()
+      { return & m_preg; }
+
+    private:
+      void assign(const std::string& s,int flags = match_extended);
+
+    private:
+      friend class smatch;
+      friend bool regex_match(const char * s, str::smatch& matches, const regex& regex);
+      friend bool regex_match(const char * s,  const regex& regex);
+      std::string m_str;
+      int m_flags;
+      regex_t m_preg;
+      bool m_valid;
+    };
+
+    /**
+     * \ingroup ZYPP_STR_REGEX
+     * \see regex
+     */
+    class smatch {
+    public:
+      smatch();
+
+      std::string operator[](unsigned i) const;
+
+      unsigned size() const;
+
+      std::string match_str;
+      regmatch_t pmatch[12];
+    };
+
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace str
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_STRING_H
diff --git a/zypp/base/SafeBool.h b/zypp/base/SafeBool.h
new file mode 100644 (file)
index 0000000..a203abd
--- /dev/null
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/SafeBool.h
+ *
+*/
+#ifndef ZYPP_BASE_SAFEBOOL_H
+#define ZYPP_BASE_SAFEBOOL_H
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    namespace safebool_detail
+    {
+      class SafeBoolBase
+      {
+      protected:
+        typedef void (SafeBoolBase::*bool_type)() const;
+        void theTrueBoolType() const {}
+
+        SafeBoolBase() {}
+        SafeBoolBase( const SafeBoolBase & ) {}
+        ~SafeBoolBase() {}
+        SafeBoolBase & operator=( const SafeBoolBase & ) { return *this; }
+      };
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SafeBool
+    //
+    /** Validate objects in a boolean context without harmful side effects.
+     * \see http://www.artima.com/cppsource/safebool.html
+     *
+     * Uses CRTP to avoid a virtual function. \c _Derived must provide
+     * <tt>bool boolTest() const</tt> preformong the test.
+     *
+     * \note Using SafeBool enables ==/!= comparision for \c Foo, based on
+     * the bool_type values. Make shure you overload \b both operators, in
+     * case an other semantic is desired for ==/!=.
+     *
+     * \code
+     * class Foo : protected base::SafeBool<Foo>
+     * {
+     * public:
+     *   using base::SafeBool<Foo>::operator bool_type;
+     *
+     * private:
+     *   friend SafeBool<TT>::operator bool_type() const;
+     *   bool boolTest() const
+     *   {
+     *     // Perform Boolean logic here
+     *   }
+     * };
+     * \endcode
+     * \todo Investigate why Bit refuses private inheritance
+     * and exposition of operator bool_type. Seems to be a gcc
+     * bug. protected works.
+    */
+    template<class _Derived>
+      struct SafeBool : private safebool_detail::SafeBoolBase
+      {
+        typedef safebool_detail::SafeBoolBase::bool_type bool_type;
+        operator bool_type() const
+        {
+          return( (static_cast<const _Derived *>(this))->boolTest()
+                  ? &safebool_detail::SafeBoolBase::theTrueBoolType
+                  : 0 );
+        }
+      protected:
+        ~SafeBool() {}
+      };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_SAFEBOOL_H
diff --git a/zypp/base/SerialNumber.cc b/zypp/base/SerialNumber.cc
new file mode 100644 (file)
index 0000000..4d77e42
--- /dev/null
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/SerialNumber.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/SerialNumber.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SerialNumber
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SerialNumber::SerialNumber
+  //   METHOD TYPE : Ctor
+  //
+  SerialNumber::SerialNumber( bool dirty_r )
+    : _dirty( dirty_r )
+    , _serial( 0 )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SerialNumber::~SerialNumber
+  //   METHOD TYPE : Dtor
+  //
+  SerialNumber::~SerialNumber()
+  {}
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const SerialNumber & obj )
+  {
+    return str << "SERIAL" << (obj._dirty?"*":"(") << obj._serial << (obj._dirty?"*":")");
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SerialNumberWatcher
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SerialNumberWatcher::SerialNumberWatcher
+  //   METHOD TYPE : Ctor
+  //
+  SerialNumberWatcher::SerialNumberWatcher( unsigned serial_r )
+  : _serial( serial_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SerialNumberWatcher::SerialNumberWatcher
+  //   METHOD TYPE : Ctor
+  //
+  SerialNumberWatcher::SerialNumberWatcher( const SerialNumber & serial_r )
+  : _serial( serial_r.serial() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : SerialNumber::~SerialNumber
+  //   METHOD TYPE : Dtor
+  //
+  SerialNumberWatcher::~SerialNumberWatcher()
+  {}
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const SerialNumberWatcher & obj )
+  {
+    return str << "LAST_SERIAL(" << obj._serial << ")";
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/SerialNumber.h b/zypp/base/SerialNumber.h
new file mode 100644 (file)
index 0000000..86be153
--- /dev/null
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/SerialNumber.h
+ *
+*/
+#ifndef ZYPP_BASE_SERIALNUMBER_H
+#define ZYPP_BASE_SERIALNUMBER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SerialNumber
+  //
+  /** Simple serial number provider.
+   *
+   * \ref serial returns a serial number. The number returned stays
+   * the same unless \ref setDirty was called to bring the object
+   * into \c dirty state. The next call to \ref serial will increment
+   * the serial number and bring the object into \c clean state.
+   *
+   * \code
+   * SerialNumber sno;
+   * sno.serial();             // SERIAL(0); () = clean
+   * sno.setDirty();           // SERIAL*0*; ** = dirty
+   * sno.serial();             // SERIAL(1)
+   * sno.setDirty();           // SERIAL*1*
+   * sno.setDirty();           // SERIAL*1*
+   * sno.serial();             // SERIAL(2)
+   * \endcode
+   */
+  class SerialNumber
+  {
+    friend std::ostream & operator<<( std::ostream & str, const SerialNumber & obj );
+
+    public:
+      /** Ctor taking initial \c dirty value. */
+      SerialNumber( bool dirty_r = false );
+      /** Dtor */
+      virtual ~SerialNumber();
+
+    public:
+      void setDirty()
+      { _dirty = true; }
+
+    public:
+      bool dirty() const
+      { return _dirty; }
+
+      bool clean() const
+      { return !_dirty; }
+
+      unsigned serial() const
+      {
+        if ( _dirty )
+        {
+          ++_serial;
+          _dirty = false;
+        }
+        return _serial;
+      }
+
+    private:
+      mutable bool     _dirty;
+      mutable unsigned _serial;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates SerialNumber Stream output */
+  std::ostream & operator<<( std::ostream & str, const SerialNumber & obj );
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SerialNumberWatcher
+  //
+  /** Simple serial number watcher.
+   *
+   * \ref SerialNumberWatcher remembers a serial number
+   * and tells whenever new numbers you feed change.
+   *
+   * All methods are overloaded to take an \unsigned or a
+   * <tt>const SerialNumber &</tt> as argument.
+   *
+   * \code
+   * SerialNumber sno;
+   *
+   * void check()
+   * {
+   *   static SerialNumberWatcher watcher( sno );
+   *
+   *   if ( watcher.remember( sno ) )
+   *   {
+   *     cout << "Serial number changed." << endl;
+   *   }
+   * }
+   *
+   * int main()
+   * {
+   *   check();          // This call would trigger, if check used a
+   *                     // default constructed SerialNumberWatcher.
+   *
+   *   check();          //
+   *   sno.dirty();
+   *   check();          // "Serial number changed."
+   *   check();          //
+   *   sno.dirty();
+   *   check();          // "Serial number changed."
+   * \endcode
+   */
+  class SerialNumberWatcher
+  {
+    friend std::ostream & operator<<( std::ostream & str, const SerialNumberWatcher & obj );
+
+    public:
+      /** Ctor taking an initial \c serial value.
+       *
+       * A default constructed SerialNumberWatcher remembers the serial
+       * number <tt>(unsigned)-1</tt>. So it is most likely the the 1st
+       * call to \ref remember returns \ref isDirty.
+       *
+       * Vice versa, initializing the SerialNumberWatcher with the current
+       * SerialNumber, most likely prevents the 1st to \ref remember to
+       * return \ref isDirty.
+      */
+      SerialNumberWatcher( unsigned serial_r = (unsigned)-1 );
+      /** Ctor taking an initial \c serial value. */
+      SerialNumberWatcher( const SerialNumber & serial_r );
+      /** Dtor */
+      virtual ~SerialNumberWatcher();
+
+    public:
+      /** Return whether \c serial_r differs. */
+      bool isDirty( unsigned serial_r ) const
+      { return( _serial != serial_r ); }
+      /** \overload */
+      bool isDirty( const SerialNumber & serial_r ) const
+      { return( _serial != serial_r.serial() ); }
+
+      /** Return whether \c serial_r is still unchanged. */
+      bool isClean( unsigned serial_r ) const
+      { return( _serial == serial_r ); }
+      /** \overload */
+      bool isClean( const SerialNumber & serial_r ) const
+      { return( _serial == serial_r.serial() ); }
+
+    public:
+      /** Return \ref isDirty, storing \c serial_r as new value. */
+      bool remember( unsigned serial_r ) const
+      {
+        if ( isDirty( serial_r ) )
+        {
+          _serial = serial_r;
+          return true;
+        }
+        return false;
+      }
+      /** \overload */
+      bool remember( const SerialNumber & serial_r ) const
+      { return remember( serial_r.serial() ); }
+
+    private:
+      mutable unsigned _serial;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates SerialNumberWatcher Stream output */
+  std::ostream & operator<<( std::ostream & str, const SerialNumberWatcher & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_SERIALNUMBER_H
diff --git a/zypp/base/Signal.h b/zypp/base/Signal.h
new file mode 100644 (file)
index 0000000..e4ffd6a
--- /dev/null
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Signal.h
+ *
+*/
+#ifndef ZYPP_BASE_SIGNAL_H
+#define ZYPP_BASE_SIGNAL_H
+
+#include <csignal>
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Exception safe signal handler save/restore.
+   * \ingroup g_RAII
+   */
+  class SignalSaver
+  {
+    public:
+      SignalSaver( int signum_r, sighandler_t handler_r )
+      : _signum( signum_r )
+      { _orighandler = ::signal( signum_r, handler_r ); }
+      ~SignalSaver()
+      { ::signal( _signum, _orighandler ); }
+    private:
+      int _signum;
+      ::sighandler_t _orighandler;
+  };
+
+  /** Exception safe sigprocmask save/restore.
+   * \ingroup g_RAII
+   */
+  class SigprocmaskSaver
+  {
+    public:
+      /** Ctor saving the original sigprocmask. */
+      SigprocmaskSaver()
+      { ::sigprocmask( SIG_SETMASK, NULL, &_origmask ); }
+      /** Dtor restoring the original sigprocmask. */
+      ~SigprocmaskSaver()
+      { ::sigprocmask( SIG_SETMASK, &_origmask, NULL ); }
+    public:
+      /** Temporary block signal. */
+      void block( int signum_r )
+      {
+       ::sigset_t mask;
+       ::sigemptyset( & mask );
+       ::sigaddset( & mask, signum_r );
+       ::sigprocmask( SIG_BLOCK, &mask, NULL );
+      }
+      /** Temporary unblock signal. */
+      void unblock( int signum_r )
+      {
+       ::sigset_t mask;
+       ::sigemptyset( & mask );
+       ::sigaddset( & mask, signum_r );
+       ::sigprocmask( SIG_UNBLOCK, &mask, NULL );
+      }
+      /** Whether signal delivery is pending. */
+      bool pending( int signum_r )
+      {
+       ::sigset_t mask;
+       ::sigpending( &mask );
+       return ::sigismember( &mask, signum_r );
+      }
+      /** Wait for signals not blocked in original sigprocmask. */
+      void suspend()
+      { ::sigsuspend( &_origmask ); }
+    private:
+      ::sigset_t _origmask;
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_SIGNAL_H
diff --git a/zypp/base/String.cc b/zypp/base/String.cc
new file mode 100644 (file)
index 0000000..897de79
--- /dev/null
@@ -0,0 +1,406 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/String.cc
+ *
+*/
+#include <cstdio>
+#include <cstdarg>
+
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/LogTools.h"
+
+using std::string;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace str
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : form
+     **      FUNCTION TYPE : std::string
+    */
+    std::string form( const char * format, ... )
+    {
+      SafeBuf safe;
+
+      va_list ap;
+      va_start( ap, format );
+      vasprintf( &safe._buf, format, ap );
+      va_end( ap );
+
+      return safe.asString();
+    }
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : strerror
+     **      FUNCTION TYPE : std::string
+    */
+    std::string strerror( int errno_r )
+    {
+      return form( "(%d)%s", errno_r, ::strerror( errno_r ) );
+    }
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : strToTrue
+     **      FUNCTION TYPE : bool
+    */
+    bool strToTrue( const C_Str & str )
+    {
+      std::string t( toLower( str ) );
+      return(    t == "1"
+              || t == "yes"
+              || t == "true"
+              || t == "on"
+              || t == "+"
+              || strtonum<long long>( str )
+            );
+    }
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : strToFalse
+     **      FUNCTION TYPE : bool
+    */
+    bool strToFalse( const C_Str & str )
+    {
+      std::string t( toLower( str ) );
+      return ! (    t == "0"
+                 || t == "no"
+                 || t == "false"
+                 || t == "off"
+                 || t == "-"
+               );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // Hexencode
+    ///////////////////////////////////////////////////////////////////
+    namespace {
+      /** What's not decoded. */
+      inline bool heIsAlNum( char ch )
+      {
+        return ( ( 'a' <= ch && ch <= 'z' )
+               ||( 'A' <= ch && ch <= 'Z' )
+               ||( '0' <= ch && ch <= '9' ) );
+      }
+      /** Hex-digit to number or -1. */
+      inline int heDecodeCh( char ch )
+      {
+        if ( '0' <= ch && ch <= '9' )
+          return( ch - '0' );
+        if ( 'A' <= ch && ch <= 'Z' )
+          return( ch - 'A' + 10 );
+        if ( 'a' <= ch && ch <= 'z' )
+          return( ch - 'A' + 10 );
+        return -1;
+      }
+    }
+
+    std::string hexencode( const C_Str & str_r )
+    {
+      static const char *const hdig = "0123456789ABCDEF";
+      std::string res;
+      res.reserve( str_r.size() );
+      for ( const char * it = str_r.c_str(); *it; ++it )
+      {
+        if ( heIsAlNum( *it ) )
+        {
+          res += *it;
+        }
+        else
+        {
+          res += '%';
+          res += hdig[(unsigned char)(*it)/16];
+          res += hdig[(unsigned char)(*it)%16];
+        }
+      }
+      return res;
+    }
+
+    std::string hexdecode( const C_Str & str_r )
+    {
+      std::string res;
+      res.reserve( str_r.size() );
+      for_( it, str_r.c_str(), str_r.c_str()+str_r.size() )
+      {
+        if ( *it == '%' )
+        {
+          int d1 = heDecodeCh( *(it+1) );
+          if ( d1 != -1 )
+          {
+            int d2 = heDecodeCh( *(it+2) );
+            if ( d2 != -1 )
+            {
+              res += (d1<<4)|d2;
+              it += 2;
+              continue;
+            }
+          }
+        }
+        // verbatim if no %XX:
+        res += *it;
+      }
+      return res;
+    }
+    ///////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : toLower
+     **      FUNCTION TYPE : std::string
+    */
+    std::string toLower( const std::string & s )
+    {
+      if ( s.empty() )
+        return s;
+
+      std::string ret( s );
+      for ( std::string::size_type i = 0; i < ret.length(); ++i )
+        {
+          if ( isupper( ret[i] ) )
+            ret[i] = static_cast<char>(tolower( ret[i] ));
+        }
+      return ret;
+    }
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : toUpper
+     **      FUNCTION TYPE : std::string
+    */
+    std::string toUpper( const std::string & s )
+    {
+      if ( s.empty() )
+        return s;
+
+      std::string ret( s );
+      for ( std::string::size_type i = 0; i < ret.length(); ++i )
+        {
+          if ( islower( ret[i] ) )
+            ret[i] = static_cast<char>(toupper( ret[i] ));
+        }
+      return ret;
+    }
+
+    /******************************************************************
+     **
+     **      FUNCTION NAME : trim
+     **      FUNCTION TYPE : std::string
+    */
+    std::string trim( const std::string & s, const Trim trim_r )
+    {
+      if ( s.empty() || trim_r == NO_TRIM )
+        return s;
+
+      std::string ret( s );
+
+      if ( trim_r & L_TRIM )
+        {
+          std::string::size_type p = ret.find_first_not_of( " \t\n" );
+          if ( p == std::string::npos )
+            return std::string();
+
+          ret = ret.substr( p );
+        }
+
+      if ( trim_r & R_TRIM )
+        {
+          std::string::size_type p = ret.find_last_not_of( " \t\n" );
+          if ( p == std::string::npos )
+            return std::string();
+
+          ret = ret.substr( 0, p+1 );
+        }
+
+      return ret;
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : stripFirstWord
+    ** FUNCTION TYPE : std::string
+    */
+    std::string stripFirstWord( std::string & line, const bool ltrim_first )
+    {
+      if ( ltrim_first )
+        line = ltrim( line );
+
+      if ( line.empty() )
+        return line;
+
+      std::string ret;
+      std::string::size_type p = line.find_first_of( " \t" );
+
+      if ( p == std::string::npos ) {
+        // no ws on line
+        ret = line;
+        line.erase();
+      } else if ( p == 0 ) {
+        // starts with ws
+        // ret remains empty
+        line = ltrim( line );
+      }
+      else {
+        // strip word and ltim line
+        ret = line.substr( 0, p );
+        line = ltrim( line.erase( 0, p ) );
+      }
+      return ret;
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : stripLastWord
+    ** FUNCTION TYPE : std::string
+    */
+    std::string stripLastWord( std::string & line, const bool rtrim_first )
+    {
+      if ( rtrim_first )
+        line = rtrim( line );
+
+      if ( line.empty() )
+        return line;
+
+      std::string ret;
+      std::string::size_type p = line.find_last_of( " \t" );
+
+      if ( p == std::string::npos ) {
+        // no ws on line
+        ret = line;
+        line.erase();
+      } else if ( p == line.size()-1 ) {
+        // ends with ws
+        // ret remains empty
+        line = rtrim( line );
+      }
+      else {
+        // strip word and rtim line
+        ret = line.substr( p+1 );
+        line = rtrim( line.erase( p ) );
+      }
+      return ret;
+    }
+
+    string gsub(const string& sData, const string& sFrom, const string& sTo)
+    {
+      string sNew;
+      sNew.reserve(sData.size());
+
+      if (! sData.empty())
+      {
+        string::size_type frLen = sFrom.length();
+        string::size_type loc = 0;
+        string::size_type oldLoc = 0;
+
+        while (string::npos != (loc = sData.find(sFrom, loc)))
+        {
+          sNew.append(sData,oldLoc,loc-oldLoc);
+          sNew.append(sTo);
+          loc += frLen;
+          oldLoc = loc;
+          if (loc >= sData.length())
+            break;
+        }
+        if (oldLoc!=sData.size())
+            sNew.append(sData,oldLoc,sData.size()-oldLoc);
+      }
+
+      return sNew;
+    }
+
+    string & replaceAll(string & str, const string & from, const string & to)
+    {
+      string::size_type pos = 0;
+      while((pos = str.find(from, pos)) != string::npos)
+      {
+        str.replace(pos, from.size(), to);
+        pos += to.size();
+
+        if (pos >= str.length())
+          break;
+      }
+      return str;
+    }
+
+
+    std::string escape( const C_Str & str_r, const char sep_r )
+    {
+      std::vector<char> buf;
+      for_( s, str_r.c_str(), s+str_r.size() )
+      {
+        switch ( *s )
+        {
+        case '"':
+        case '\'':
+        case '\\':
+          buf.push_back( '\\' );
+          buf.push_back( *s );
+          break;
+        default:
+          if ( *s == sep_r )
+            buf.push_back( '\\' );
+          buf.push_back( *s );
+        }
+      }
+      return std::string( buf.begin(), buf.end() );
+    }
+
+    std::string getline( std::istream & str, const Trim trim_r )
+    {
+      return trim( receiveUpTo( str, '\n' ), trim_r );
+    }
+
+    std::string getline( std::istream & str, bool trim_r )
+    {
+      return trim( receiveUpTo( str, '\n' ), trim_r?TRIM:NO_TRIM );
+    }
+
+    std::string receiveUpTo( std::istream & str, const char delim_r, bool returnDelim_r )
+    {
+      std::ostringstream datas;
+      do {
+       char ch;
+       if ( str.get( ch ) )
+       {
+         if ( ch != delim_r )
+         {
+           datas.put( ch );
+         }
+         else
+         {
+           if ( returnDelim_r )
+             datas.put( ch );
+           break;      // --> delimiter found
+         }
+       }
+       else
+       {
+         // clear fail bit if we read data before reaching EOF
+         if ( str.eof() && datas.tellp() )
+           str.clear( std::ios::eofbit );
+         break;        // --> no consumable data.
+       }
+      } while ( true );
+      return datas.str();
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace str
+  ///////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////
diff --git a/zypp/base/String.h b/zypp/base/String.h
new file mode 100644 (file)
index 0000000..ea37b47
--- /dev/null
@@ -0,0 +1,840 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/String.h
+ *
+*/
+#ifndef ZYPP_BASE_STRING_H
+#define ZYPP_BASE_STRING_H
+
+#include <cstring>
+
+#include <iosfwd>
+#include <vector>
+#include <string>
+#include <sstream>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/PtrTypes.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Convenience \c char* constructible from \c std::string and \c char*,
+   *  it maps \c (char*)0 to an empty string.
+   *
+   * \code
+   * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
+   * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
+   * \endcode
+   *
+   * Called with a plain \c char* as argument, the \c std::string is created form
+   * for nothing. The implementation actually does not use the \c std::string.
+   *
+   * Best would be to implement \c hasPrefix for each combination of \c char*
+   * and \c std::string arguments:
+   *
+   * \code
+   * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
+   * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
+   *
+   * bool hasPrefix( const std::string & str_r, const char * prefix_r )
+   * { return( !prefix_r || ::strncmp( str_r.c_str(), prefix_r, ::strlen(prefix_r) ) == 0 ); }
+   *
+   * bool hasPrefix( const char * str_r, const std::string & prefix_r )
+   * { return( str_r ? ::strncmp( str_r, prefix_r.c_str(), prefix_r.size() ) == 0 : prefix_r.empty() ); }
+   *
+   * bool hasPrefix( const char * str_r, const char * prefix_r )
+   * { return( str && prefix_r ? ::strncmp( str_r, prefix_r, ::strlen(prefix_r) ) == 0
+   *                           : !((str_r && *str_r) || (prefix_r && *prefix_r)); }
+   * \endcode
+   *
+   * This is where \ref C_Str can help. Constructible from \c std::string and \c char*,
+   * it \e reduces the \c std::string to it's \c char*. At the same time it converts
+   * \c (char*)0 into an \c "" string.
+   *
+   * \code
+   * bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
+   * { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
+   * \endcode
+   */
+  class C_Str
+  {
+    public:
+      typedef std::string::size_type size_type;
+
+    public:
+      C_Str()                            : _val( 0 ),             _sze( 0 ) {}
+      C_Str( char * c_str_r )            : _val( c_str_r ),       _sze( std::string::npos ) {}
+      C_Str( const char * c_str_r )      : _val( c_str_r ),       _sze( std::string::npos ) {}
+      C_Str( const std::string & str_r ) : _val( str_r.c_str() ), _sze( str_r.size() ) {}
+
+    public:
+      bool      isNull()       const { return !_val; }
+      bool      empty()        const { return !(_val && *_val); }
+      size_type size()         const
+      {
+        if ( _sze == std::string::npos )
+        { _sze = _val ? ::strlen( _val ) : 0; }
+        return _sze;
+      };
+
+      operator const char *() const { return c_str(); }
+      const char * c_str()    const { return _val ? _val : ""; }
+
+    private:
+      const char *const _val;
+      mutable size_type _sze;
+  };
+
+  /** \relates C_Str Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const C_Str & obj )
+  { return str << obj.c_str(); }
+
+  ///////////////////////////////////////////////////////////////////
+  /** String related utilities and \ref ZYPP_STR_REGEX.
+   \see \ref ZYPP_STR_REGEX
+  */
+  namespace str
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Global asString() that works with std::string too
+     */
+    inline std::string asString( const std::string &t )
+    { return t; }
+
+    inline std::string asString( const char * t )
+    { return t; }
+
+    template<class _T>
+        inline std::string asString( const _T &t )
+        { return t.asString(); }
+
+    template<class _T>
+        inline std::string asString( const intrusive_ptr<_T> &p )
+        { return p->asString(); }
+
+    template<class _T>
+        inline std::string asString( const weak_ptr<_T> &p )
+        { return p->asString(); }
+
+    template<>
+        inline std::string asString( const bool &t )
+        { return t ? "+" : "-"; }
+
+    ///////////////////////////////////////////////////////////////////
+    /** Printf style construction of std::string. */
+    std::string form( const char * format, ... )
+    __attribute__ ((format (printf, 1, 2)));
+
+    ///////////////////////////////////////////////////////////////////
+    /** Return string describing the \a error_r code.
+     * Like ::strerror, but the numerical value is included in
+     * the string as well.
+    */
+    std::string strerror( int errno_r );
+
+    ///////////////////////////////////////////////////////////////////
+    /** Assert \c free called for allocated <tt>char *</tt>.
+     * \code
+     * ...
+     * SafeBuf safe;
+     * vasprintf( &safe._buf, format, ap );
+     * return safe.asString();
+     * \endcode
+     *
+     * \ingroup g_RAII
+    */
+    struct SafeBuf
+    {
+      char * _buf;
+      SafeBuf() : _buf( 0 ) {}
+      ~SafeBuf() { if ( _buf ) free( _buf ); }
+      std::string asString() const
+      { return _buf ? std::string(_buf) : std::string(); }
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    /** Convenient building of std::string via std::ostream::operator<<.
+     * Basically this is an \ref ostringstream which is autocenvertible
+     * into a \ref string.
+     * \code
+     *  void fnc( const std::string & txt_r );
+     *  fnc( str::Str() << "Hello " << 13 );
+     *
+     *  std::string txt( str::Str() << 45 );
+     * \endcode
+    */
+    struct Str
+    {
+      template<class _Tp>
+      Str & operator<<( const _Tp & val )
+      { _str << val; return *this; }
+
+      operator std::string() const
+      { return _str.str(); }
+
+      std::ostringstream _str;
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name String representation of number.
+     *
+     * Optional second argument sets the minimal string width (' ' padded).
+     * Negative values will cause the number to be left adjusted within the string.
+     *
+     * Default width is 0.
+     * \code
+     * numstring(42)           -> "42"
+     * numstring(42, 4)        -> "  42"
+     * numstring(42,-4)        -> "42  "
+     * \endcode
+     **/
+    //@{
+    inline std::string numstring( char n,               int w = 0 ) { return form( "%*hhd",  w, n ); }
+    inline std::string numstring( unsigned char n,      int w = 0 ) { return form( "%*hhu",  w, n ); }
+    inline std::string numstring( short n,              int w = 0 ) { return form( "%*hd",   w, n ); }
+    inline std::string numstring( unsigned short n,     int w = 0 ) { return form( "%*hu",   w, n ); }
+    inline std::string numstring( int n,                int w = 0 ) { return form( "%*d",    w, n ); }
+    inline std::string numstring( unsigned n,           int w = 0 ) { return form( "%*u",    w, n ); }
+    inline std::string numstring( long n,               int w = 0 ) { return form( "%*ld",   w, n ); }
+    inline std::string numstring( unsigned long n,      int w = 0 ) { return form( "%*lu",   w, n ); }
+    inline std::string numstring( long long n,          int w = 0 ) { return form( "%*lld",  w, n ); }
+    inline std::string numstring( unsigned long long n, int w = 0 ) { return form( "%*llu",  w, n ); }
+
+    template<> inline std::string asString( const char & t )                   { return numstring( t ); }
+    template<> inline std::string asString( const unsigned char & t )          { return numstring( t ); }
+    template<> inline std::string asString( const short & t )                  { return numstring( t ); }
+    template<> inline std::string asString( const unsigned short & t )         { return numstring( t ); }
+    template<> inline std::string asString( const int & t )                    { return numstring( t ); }
+    template<> inline std::string asString( const unsigned & t )               { return numstring( t ); }
+    template<> inline std::string asString( const long & t )                   { return numstring( t ); }
+    template<> inline std::string asString( const unsigned long & t )          { return numstring( t ); }
+    template<> inline std::string asString( const long long & t )              { return numstring( t ); }
+    template<> inline std::string asString( const unsigned long long & t )     { return numstring( t ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name String representation of number as hex value with leading '0x'.
+     * Optional second argument sets the minimal
+     * string width (0 padded). Negative values will cause the number to be left adjusted
+     * within the string. Default width is 10 (4 for char).
+     * <PRE>
+     * hexstring(42)           -> "0x0000002a"
+     * hexstring(42, 4)        -> "0x2a"
+     * hexstring(42,-4)        -> "0x2a"
+     * </PRE>
+     **/
+    //@{
+    inline std::string hexstring( char n,               int w = 4 ) { return form( "%#0*hhx", w, n ); }
+    inline std::string hexstring( unsigned char n,      int w = 4 ) { return form( "%#0*hhx", w, n ); }
+    inline std::string hexstring( short n,              int w = 10 ){ return form( "%#0*hx",  w, n ); }
+    inline std::string hexstring( unsigned short n,     int w = 10 ){ return form( "%#0*hx",  w, n ); }
+    inline std::string hexstring( int n,                int w = 10 ){ return form( "%#0*x",   w, n ); }
+    inline std::string hexstring( unsigned n,           int w = 10 ){ return form( "%#0*x",   w, n ); }
+    inline std::string hexstring( long n,               int w = 10 ){ return form( "%#0*lx",  w, n ); }
+    inline std::string hexstring( unsigned long n,      int w = 10 ){ return form( "%#0*lx",  w, n ); }
+    inline std::string hexstring( long long n,          int w = 0 ) { return form( "%#0*llx", w, n ); }
+    inline std::string hexstring( unsigned long long n, int w = 0 ) { return form( "%#0*llx", w, n ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name String representation of number as octal value with leading '0'.
+     * Optional second argument sets the minimal
+     * string width (0 padded). Negative values will cause the number to be left adjusted
+     * within the string. Default width is 5 (4 for char).
+     * <PRE>
+     * octstring(42)           -> "00052"
+     * octstring(42, 4)        -> "0052"
+     * octstring(42,-4)        -> "052 "
+     * </PRE>
+     **/
+    //@{
+    inline std::string octstring( char n,               int w = 4 ) { return form( "%#0*hho",  w, n ); }
+    inline std::string octstring( unsigned char n,      int w = 4 ) { return form( "%#0*hho",  w, n ); }
+    inline std::string octstring( short n,              int w = 5 ) { return form( "%#0*ho",   w, n ); }
+    inline std::string octstring( unsigned short n,     int w = 5 ) { return form( "%#0*ho",   w, n ); }
+    inline std::string octstring( int n,                int w = 5 ) { return form( "%#0*o",    w, n ); }
+    inline std::string octstring( unsigned n,           int w = 5 ) { return form( "%#0*o",    w, n ); }
+    inline std::string octstring( long n,               int w = 5 ) { return form( "%#0*lo",   w, n ); }
+    inline std::string octstring( unsigned long n,      int w = 5 ) { return form( "%#0*lo",   w, n ); }
+    inline std::string octstring( long long n,          int w = 0 ) { return form( "%#0*llo",  w, n ); }
+    inline std::string octstring( unsigned long long n, int w = 0 ) { return form( "%#0*llo",  w, n ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** Parsing numbers from string.
+    */
+    //@{
+    /** String to integer type determined by template arg.
+     * \note Only specializations are defined.
+     * \code
+     * time_t t = strtonum<time_t>( "42" );
+     * \endcode
+    */
+    template<typename _It>
+      _It strtonum( const C_Str & str );
+
+    template<>
+      inline short              strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
+    template<>
+      inline int                strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
+    template<>
+      inline long               strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
+    template<>
+      inline long long          strtonum( const C_Str & str ) { return ::strtoll ( str, NULL, 0 ); }
+
+    template<>
+      inline unsigned short     strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
+    template<>
+      inline unsigned           strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
+    template<>
+      inline unsigned long      strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
+    template<>
+      inline unsigned long long strtonum( const C_Str & str ) { return ::strtoull( str, NULL, 0 ); }
+
+    /** String to integer type detemined 2nd function arg \a i.
+     * \code
+     * time_t t; strtonum( "42", t );
+     * \endcode
+    */
+    template<typename _It>
+      inline _It strtonum( const C_Str & str, _It & i )
+      { return i = strtonum<_It>( str ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** Parsing boolean from string.
+    */
+    //@{
+    /** Return \c true if str is <tt>1, true, yes, on</tt> (or a nonzero number). */
+    bool strToTrue( const C_Str & str );
+
+    /** Return \c false if str is <tt>0, false, no, off</tt>. */
+    bool strToFalse( const C_Str & str );
+
+    /** Parse \c str into a bool depending on the default value.
+     * If the \c default is true, look for a legal \c false string.
+     * If the \c default is false, look for a legal \c true string.
+     */
+    inline bool strToBool( const C_Str & str, bool default_r )
+    { return( default_r ? strToFalse( str ) : strToTrue( str ) ); }
+
+    /** Parse \c str into a bool if it's a legal \c true or \c false string.
+     * If \c str is not a recognized \c true or \c false string, \a return_r
+     * is left unchanged.
+     */
+    inline bool strToBoolNodefault( const C_Str & str, bool & return_r )
+    {
+      if ( strToTrue( str ) ) return (return_r = true);
+      if ( !strToFalse( str ) ) return (return_r = false);
+      return return_r;
+    }
+
+    //@}
+
+    /**
+     * \short Looks for text in a string and replaces it.
+     *
+     * \note It only perform substtution in one pass
+     */
+    std::string gsub( const std::string& sData, const std::string& sFrom, const std::string& sTo);
+
+    /**
+     * \short Looks for text in string and replaces it in place
+     *
+     *
+     * \note It only perform substtution in one pass
+     * \note use only if you replace same lenght strings, otherwise use gsub
+     */
+    std::string& replaceAll( std::string & str, const std::string & from, const std::string & to);
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Split. */
+    //@{
+    /** Split \a line_r into words.
+     * Any sequence of characters in \a sepchars_r is treated as
+     * delimiter. The words are passed to OutputIterator \a result_r.
+     * \code
+     * std::vector<std::string> words;
+     * str::split( "some line", std::back_inserter(words) )
+     * \endcode
+     *
+    */
+    template<class _OutputIterator>
+      unsigned split( const C_Str &   line_r,
+                      _OutputIterator result_r,
+                      const C_Str &   sepchars_r = " \t" )
+      {
+        const char * beg = line_r;
+        const char * cur = beg;
+        // skip leading sepchars
+        while ( *cur && ::strchr( sepchars_r, *cur ) )
+          ++cur;
+        unsigned ret = 0;
+        for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
+          {
+            // skip non sepchars
+            while( *cur && !::strchr( sepchars_r, *cur ) )
+              ++cur;
+            // build string
+            *result_r = std::string( beg, cur-beg );
+            // skip sepchars
+            while ( *cur && ::strchr( sepchars_r, *cur ) )
+              ++cur;
+          }
+        return ret;
+      }
+
+    /** Split \a line_r into words with respect to escape delimeters.
+     * Any sequence of characters in \a sepchars_r is treated as
+     * delimiter if not inside "" or "" or escaped by \, but not \\.
+     * The words are passed to OutputIterator \a result_r.
+     *
+     * \see \ref splitEscaped
+     *
+     * \code
+     * std::vector<std::string> words;
+     * str::splitEscaped( "some line", std::back_inserter(words) )
+     * \endcode
+     *
+     * \code
+     * example splitted strings
+     * normal line -> 2 elements ( "normal", "line" )
+     * escaped\ line -> 1 element( "escaped line" )
+     * "quoted line" -> 1 element same as above
+     * 'quoted line' -> 1 element same as above
+     * "escaped quote\'" -> 1 element ( "escaped quote'" )
+     *
+     * \param line_r   The string to parse.
+     * \param result_r
+     * \param sepchars_r  String of separator characters.
+     * \param withEmpty   Whether to include empty fields between separators in the result.
+     *
+     * \endcode
+     */
+    template<class _OutputIterator>
+      unsigned splitEscaped( const C_Str &   line_r,
+                      _OutputIterator result_r,
+                      const C_Str &   sepchars_r = " \t",
+                      bool withEmpty = false)
+      {
+        const char * beg = line_r;
+        const char * cur = beg;
+        unsigned ret = 0;
+
+        // skip leading sepchars
+        while ( *cur && ::strchr( sepchars_r, *cur ) )
+        {
+          ++cur;
+          if (withEmpty)
+          {
+            *result_r = "";
+            ++ret;
+          }
+        }
+
+        // there were only sepchars in the string
+        if (!*cur && withEmpty)
+        {
+          *result_r = "";
+          return ++ret;
+        }
+
+        // after the leading sepchars
+        for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
+          {
+            if ( *cur == '"'  || *cur == '\'' )
+            {
+              char closeChar = *cur;
+              ++cur;
+              bool cont = true;
+              while (cont)
+              {
+                while ( *cur && *cur != closeChar)
+                  ++cur;
+                if ( *cur == '\0' )
+                {
+                  return ret; //TODO parsing exception no closing quote
+                }
+                int escCount = 0;
+                const char * esc = cur-1;
+                while ( esc != beg && *esc == '\\' )
+                {
+                  escCount++;
+                  --esc;
+                }
+                cont = (escCount % 2 == 1); // find some non escaped escape char
+                cur++; //skip quote
+              }
+
+              std::string s( beg+1, cur-beg-2 ); //without quotes
+              //transform escaped escape
+              replaceAll( s, "\\\\", "\\" );
+              //transform escaped quotes (only same as open
+              char tmpn[2] = { closeChar, 0 };
+              char tmpo[3] = { '\\', closeChar, 0 };
+              replaceAll( s, tmpo, tmpn );
+
+              *result_r = s;
+            }
+            else
+            {
+              // skip non sepchars
+              while( *cur && !::strchr( sepchars_r, *cur ) )
+              {
+                //ignore char after backslash
+                if ( *cur == '\\' )
+                {
+                  ++cur;
+                }
+                ++cur;
+              }
+              // build string
+              std::string s( beg, cur-beg );
+              //transform escaped escape
+              replaceAll( s, "\\\\", "\\" );
+
+              const char *delimeter = sepchars_r;
+              while ( *delimeter )
+              {
+                std::string ds("\\");
+                const char tmp[2] = { *delimeter, '\0' };
+                std::string del(tmp);
+                ds+= del;
+                replaceAll( s, ds, del );
+                ++delimeter;
+              }
+
+              *result_r = s;
+            }
+            // skip sepchars
+            if ( *cur && ::strchr( sepchars_r, *cur ) )
+              ++cur;
+            while ( *cur && ::strchr( sepchars_r, *cur ) )
+            {
+              ++cur;
+              if (withEmpty)
+              {
+                *result_r = "";
+                ++ret;
+              }
+            }
+            // the last was a separator => one more field
+            if ( !*cur && withEmpty && ::strchr( sepchars_r, *(cur-1) ) )
+            {
+              *result_r = "";
+              ++ret;
+            }
+          }
+        return ret;
+      }
+
+    /** Split \a line_r into fields.
+     * Any single character in \a sepchars_r is treated as a
+     * field separator. The words are passed to OutputIterator
+     * \a result_r.
+     * \code
+     * ""        -> words 0
+     * ":"       -> words 2  |||
+     * "a"       -> words 1  |a|
+     * ":a"      -> words 2  ||a|
+     * "a:"      -> words 2  |a||
+     * ":a:"     -> words 3  ||a||
+     *
+     * \endcode
+     *
+     * \code
+     * std::vector<std::string> words;
+     * str::split( "some line", std::back_inserter(words) )
+     * \endcode
+     *
+    */
+    template<class _OutputIterator>
+      unsigned splitFields( const C_Str &   line_r,
+                            _OutputIterator result_r,
+                            const C_Str &   sepchars_r = ":" )
+      {
+        const char * beg = line_r;
+        const char * cur = beg;
+        unsigned ret = 0;
+        for ( beg = cur; *beg; beg = cur, ++result_r )
+          {
+            // skip non sepchars
+            while( *cur && !::strchr( sepchars_r, *cur ) )
+              ++cur;
+            // build string
+            *result_r = std::string( beg, cur-beg );
+            ++ret;
+            // skip sepchar
+            if ( *cur )
+            {
+              ++cur;
+              if ( ! *cur )                // ending with sepchar
+              {
+                *result_r = std::string(); // add final empty field
+                ++ret;
+                break;
+              }
+            }
+          }
+        return ret;
+      }
+
+    /**
+     * Split \a line_r into fields handling also escaped separators.
+     *
+     * \see splitFields()
+     * \see splitEscaped()
+     */
+    template<class _OutputIterator>
+      unsigned splitFieldsEscaped( const C_Str &   line_r,
+                            _OutputIterator result_r,
+                            const C_Str &   sepchars_r = ":" )
+      {
+        return
+          splitEscaped( line_r, result_r, sepchars_r, true /* withEmpty */ );
+      }
+
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Join. */
+    //@{
+    /** Join strings using separator \a sep_r (defaults to BLANK). */
+    template <class _Iterator>
+      std::string join( _Iterator begin, _Iterator end,
+                        const C_Str & sep_r = " " )
+      {
+        std::string res;
+        for ( _Iterator iter = begin; iter != end; ++ iter )
+          {
+            if ( iter != begin )
+              res += sep_r;
+            res += asString(*iter);
+          }
+        return res;
+      }
+
+    /** Join strings using separator \a sep_r (defaults to BLANK). */
+    template <class _Container>
+      std::string join( const _Container & cont_r,
+                        const C_Str & sep_r = " " )
+      { return join( cont_r.begin(), cont_r.end(), sep_r ); }
+
+    /** Join strings using separator \a sep_r, quoting or escaping the values.
+     * Separator defaults to BLANK. Use \ref splitEscaped to restore the
+     * values.
+     */
+    template <class _Iterator>
+      std::string joinEscaped( _Iterator begin, _Iterator end,
+                               const char sep_r = ' ' )
+      {
+        std::vector<char> buf;
+        for ( _Iterator iter = begin; iter != end; ++ iter )
+        {
+          if ( iter != begin )
+            buf.push_back( sep_r );
+
+          if ( iter->empty() )
+          {
+            // empty string goes ""
+            buf.push_back( '"' );
+            buf.push_back( '"' );
+          }
+          else
+          {
+            std::string toadd( asString(*iter) );
+            for_( ch, toadd.begin(), toadd.end() )
+            {
+              switch ( *ch )
+              {
+                case '"':
+                case '\'':
+                case '\\':
+                  buf.push_back( '\\' );
+                  buf.push_back( *ch );
+                  break;
+                default:
+                  if ( *ch == sep_r )
+                    buf.push_back( '\\' );
+                  buf.push_back( *ch );
+              }
+            }
+          }
+        }
+        return std::string( buf.begin(), buf.end() );
+      }
+
+
+      /**
+       * Escape desired character \a c using a backslash.
+       *
+       * For use when printing \a c separated values, and where
+       * \ref joinEscaped() is too heavy.
+       *
+       * \todo use C_Str instead of std::string to prevent unnecessary
+       * promotion to string if used with "string".
+       *
+       * \todo shoud not be documented in doxy-group 'Join'
+       */
+      std::string escape( const C_Str & str_r, const char c = ' ' );
+
+      /** Escape \a next_r and append it to \a str_r using separator \a sep_r. */
+      inline void appendEscaped( std::string & str_r, const C_Str & next_r, const char sep_r = ' ' )
+      {
+        if ( ! str_r.empty() )
+          str_r += sep_r;
+        if ( next_r.empty() )
+          str_r += "\"\"";
+        else
+          str_r += escape( next_r, sep_r );
+      }
+
+      //! \todo unsecape()
+
+    //@}
+    ///////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    /** \name Hexencode.
+     * Encode all characters other than [a-zA-Z0-9] as %XX.
+     * This includes the % character itself, which becomes %25.
+     */
+    //@{
+    /** Encode all characters other than [a-zA-Z0-9] as %XX.
+     * This includes the % character itself, which becomes %25.
+     */
+    std::string hexencode( const C_Str & str_r );
+    /** Decode hexencoded %XX sequences. */
+    std::string hexdecode( const C_Str & str_r );
+    //@}
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name Case conversion. */
+    //@{
+    /** Return lowercase version of \a s
+     * \todo improve
+    */
+    std::string toLower( const std::string & s );
+    /** \overload */
+    inline std::string toLower( const char * s )
+    { return( s ? toLower( std::string(s) ) : std::string() ); }
+
+    /** Return uppercase version of \a s
+     * \todo improve
+    */
+    std::string toUpper( const std::string & s );
+    /** \overload */
+    inline std::string toUpper( const char * s )
+    { return( s ? toUpper( std::string(s) ) : std::string() ); }
+    //@}
+
+
+    /** \name Case insensitive comparison. */
+    //@{
+    inline int compareCI( const C_Str & lhs, const C_Str & rhs )
+    { return ::strcasecmp( lhs, rhs ); }
+    //@}
+
+    /** \name Locate substring. */
+    //@{
+    /** Locate substring case sensitive. */
+    inline bool contains( const C_Str & str_r, const C_Str & val_r )
+    { return ::strstr( str_r, val_r ); }
+    /** Locate substring case insensitive. */
+    inline bool containsCI( const C_Str & str_r, const C_Str & val_r )
+    { return ::strcasestr( str_r, val_r ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
+    /** \name Trimming whitepace.
+     * \todo optimize l/r trim.
+    */
+    //@{
+    /** To define how to trim. */
+    enum Trim {
+      NO_TRIM = 0x00,
+      L_TRIM  = 0x01,
+      R_TRIM  = 0x02,
+      TRIM    = (L_TRIM|R_TRIM)
+    };
+
+    std::string trim( const std::string & s, const Trim trim_r = TRIM );
+
+    inline std::string ltrim( const std::string & s )
+    { return trim( s, L_TRIM ); }
+
+    inline std::string rtrim( const std::string & s )
+    { return trim( s, R_TRIM ); }
+    //@}
+
+    std::string stripFirstWord( std::string & line, const bool ltrim_first );
+
+    std::string stripLastWord( std::string & line, const bool rtrim_first );
+
+    /** Return stream content up to (but not returning) the next newline.
+     * \see \ref receiveUpTo
+     */
+    std::string getline( std::istream & str, bool trim = false );
+
+    /** Return stream content up to (but not returning) the next newline.
+     * \see \ref receiveUpTo
+     */
+    std::string getline( std::istream & str, const Trim trim_r );
+
+    /** Return stream content up to the next ocurrence of \c delim_r or EOF
+     * \c delim_r, if found, is always read from the stream. Whether it is
+     * also returned in the string depends on \c returnDelim_r.
+     * If the stream status is \c good, \c delim_r was found in the stream.
+     * If we reached EOF while looking for \c delim_r, \c eof is set; and
+     * also \c fail, if we did not read any data before.
+     */
+    std::string receiveUpTo( std::istream & str, const char delim_r, bool returnDelim_r = false );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name String prefix/suffix handling.
+     */
+    //@{
+    /** Return whether \a str_r has prefix \a prefix_r. */
+    inline bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
+    { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
+
+    /** Strip a \a prefix_r from \a str_r and return the resulting string. */
+    inline std::string stripPrefix( const C_Str & str_r, const C_Str & prefix_r )
+    { return( hasPrefix( str_r, prefix_r ) ? str_r + prefix_r.size() : str_r.c_str() ); }
+
+    /** Return whether \a str_r has suffix \a suffix_r. */
+    inline bool hasSuffix( const C_Str & str_r, const C_Str & suffix_r )
+    { return( str_r.size() >= suffix_r.size() && ::strncmp( str_r + str_r.size() - suffix_r.size() , suffix_r, suffix_r.size() ) == 0 ); }
+
+    /** Strip a \a suffix_r from \a str_r and return the resulting string. */
+    inline std::string stripSuffix( const C_Str & str_r, const C_Str & suffix_r )
+    {
+      if ( hasSuffix( str_r, suffix_r ) )
+        return std::string( str_r, str_r.size() - suffix_r.size() );
+      return str_r.c_str();
+    }
+
+    /** alias for \ref hasPrefix */
+    inline bool startsWith( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasPrefix( str_r, prefix_r ); }
+    /** alias for \ref hasSuffix */
+    inline bool endsWith( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasSuffix( str_r, prefix_r ); }
+    //@}
+    /////////////////////////////////////////////////////////////////
+  } // namespace str
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_STRING_H
diff --git a/zypp/base/Sysconfig.cc b/zypp/base/Sysconfig.cc
new file mode 100644 (file)
index 0000000..253c44d
--- /dev/null
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Sysconfig.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/base/Sysconfig.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace base {
+
+    namespace sysconfig {
+      map<string,string> read( const Pathname & _path )
+      {
+       DBG << "Load '" << _path << "'" << endl;
+       map<string,string> ret;
+      
+       string line;
+       ifstream in( _path.asString().c_str() );
+       if ( in.fail() ) {
+         WAR << "Unable to load '" << _path << "'" << endl;
+         return ret;
+       }
+
+       while( getline( in, line ) ) {
+         if ( *line.begin() != '#' ) {
+
+           string::size_type pos = line.find( '=', 0 );
+
+           if ( pos != string::npos ) {
+
+             string key = str::trim( line.substr( 0, pos ) );
+             string value = str::trim( line.substr( pos + 1, line.length() - pos - 1 ) );
+
+             if ( value.length() >= 2
+                  && *(value.begin()) == '"'
+                  && *(value.rbegin()) == '"' )
+             {
+               value = value.substr( 1, value.length() - 2 );
+             }
+             if ( value.length() >= 2
+                  && *(value.begin()) == '\''
+                  && *(value.rbegin()) == '\'' )
+             {
+               value = value.substr( 1, value.length() - 2 );
+             }
+             XXX << "KEY: '" << key << "' VALUE: '" << value << "'" << endl;
+             ret[key] = value;
+
+           } // '=' found
+
+         } // not comment
+
+       } // while getline
+  MIL << "done reading '" << _path << "'" << endl;
+       return ret;
+      }
+
+    } // namespace sysconfig
+  } // namespace base
+} // namespace zypp
diff --git a/zypp/base/Sysconfig.h b/zypp/base/Sysconfig.h
new file mode 100644 (file)
index 0000000..5d511dc
--- /dev/null
@@ -0,0 +1,29 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Sysconfig.h
+ *
+*/
+#ifndef ZYPP_BASE_SYSCONFIG_H
+#define ZYPP_BASE_SYSCONFIG_H
+
+#include <string>
+#include <map>
+#include "zypp/Pathname.h"
+
+namespace zypp {
+  namespace base {
+    namespace sysconfig {
+
+      std::map<std::string,std::string> read( const Pathname & _path );
+
+    } // namespace sysconfig
+  } // namespace base
+} // namespace zypp
+
+#endif // ZYPP_BASE_SYSCONFIG_H
diff --git a/zypp/base/Tr1hash.h b/zypp/base/Tr1hash.h
new file mode 100644 (file)
index 0000000..617febe
--- /dev/null
@@ -0,0 +1,55 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Tr1hash.h
+ *
+*/
+#ifndef ZYPP_BASE_TR1HASH_H
+#define ZYPP_BASE_TR1HASH_H
+
+#include <iosfwd>
+#include <tr1/unordered_set>
+#include <tr1/unordered_map>
+
+#include "zypp/base/PtrTypes.h"
+
+/** Define hash function for id based classes.
+ * Class has to provide a method \c id() retuning a unique number.
+ * \code
+ *  // in global namespace define:
+ *  ZYPP_DEFINE_ID_HASHABLE( ::zypp::sat::Sovable )
+ * \endcode
+ */
+#define ZYPP_DEFINE_ID_HASHABLE(C)           \
+namespace std { namespace tr1 {              \
+  template<class _Tp> struct hash;           \
+  template<> struct hash<C>                  \
+  {                                          \
+    size_t operator()( const C & __s ) const \
+    { return __s.id(); }                     \
+  };                                         \
+}}
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** clone function for RW_pointer */
+  template<class _D>
+  inline std::tr1::unordered_set<_D> * rwcowClone( const std::tr1::unordered_set<_D> * rhs )
+  { return new std::tr1::unordered_set<_D>( *rhs ); }
+
+  /** clone function for RW_pointer */
+  template<class _K, class _V>
+  inline std::tr1::unordered_map<_K,_V> * rwcowClone( const std::tr1::unordered_map<_K,_V> * rhs )
+  { return new std::tr1::unordered_map<_K,_V>( *rhs ); }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_TR1HASH_H
diff --git a/zypp/base/Unit.cc b/zypp/base/Unit.cc
new file mode 100644 (file)
index 0000000..264a1d8
--- /dev/null
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Unit.cc
+ *
+*/
+#include "zypp/base/String.h"
+
+#include "zypp/base/Unit.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    std::string Unit::form( double val_r,
+                            const std::string & symbol_r,
+                            unsigned field_width_r,
+                            unsigned unit_width_r,
+                            unsigned prec_r )
+    {
+      std::string ret = str::form( "%*.*f", field_width_r, prec_r, val_r );
+      if ( unit_width_r )
+        {
+          ret +=  str::form( " %*s", unit_width_r, symbol_r.c_str() );
+        }
+      return ret;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/Unit.h b/zypp/base/Unit.h
new file mode 100644 (file)
index 0000000..5fb7ac8
--- /dev/null
@@ -0,0 +1,103 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Unit.h
+ *
+*/
+#ifndef ZYPP_BASE_UNIT_H
+#define ZYPP_BASE_UNIT_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace base
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Unit
+    //
+    /** Simple handling of Units.
+     *
+     * Unit stores factor and symbol, and a precision value for printing.
+     * \ref form builds a string from a value according to the format
+     * specification.
+     * \code
+     * static const Unit B( 1, "B", 0 );
+     * static const Unit K( 1024, "K", 1 );
+     * static const Unit M( 1048576, "M", 1 );
+     * static const Unit G( 1073741824, "G", 2 );
+     * static const Unit T( 1099511627776, "T", 3 );
+     * \endcode
+    */
+      class Unit
+      {
+      public:
+        typedef long long ValueType;
+
+        /** Default ctor */
+        Unit()
+        : _factor( 1 )
+        , _prec( 0 )
+        {}
+
+        /** ctor */
+        Unit( ValueType factor_r, std::string symbol_r, unsigned prec_r )
+        : _factor( factor_r )
+        , _symbol( symbol_r )
+        , _prec( prec_r )
+        {}
+
+        ValueType factor() const
+        { return _factor; }
+
+        const std::string & symbol() const
+        { return _symbol; }
+
+        unsigned prec() const
+        { return _prec; }
+
+        /** Build string representation of \a val_r. */
+        std::string form( ValueType val_r,
+                          unsigned field_width_r = 0,
+                          unsigned unit_width_r  = 1 ) const
+        { return form( val_r, field_width_r, unit_width_r, _prec ); }
+
+        std::string form( ValueType val_r,
+                          unsigned field_width_r,
+                          unsigned unit_width_r,
+                          unsigned prec_r ) const
+        { return form( double(val_r)/_factor, _symbol,
+                       field_width_r, unit_width_r, prec_r ); }
+
+
+        static std::string form( double val_r,
+                                 const std::string & symbol_r,
+                                 unsigned field_width_r,
+                                 unsigned unit_width_r,
+                                 unsigned prec_r );
+
+      private:
+        ValueType   _factor;
+        std::string _symbol;
+        unsigned    _prec;
+      };
+    ///////////////////////////////////////////////////////////////////
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace base
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_UNIT_H
diff --git a/zypp/base/UserRequestException.cc b/zypp/base/UserRequestException.cc
new file mode 100644 (file)
index 0000000..707bafb
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/UserRequestException.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/base/UserRequestException.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : UserRequestException::UserRequestException
+  //   METHOD TYPE : Ctor
+  //
+  UserRequestException::UserRequestException( const std::string & msg_r )
+  : Exception( msg_r ), _kind( UNSPECIFIED )
+  {}
+
+  UserRequestException::UserRequestException( const std::string & msg_r, const Exception & history_r )
+  : Exception( msg_r, history_r ), _kind( UNSPECIFIED )
+  {}
+
+  UserRequestException::UserRequestException( Kind kind_r, const std::string & msg_r )
+  : Exception( msg_r ), _kind( kind_r )
+  {}
+
+  UserRequestException::UserRequestException( Kind kind_r, const std::string & msg_r, const Exception & history_r )
+  : Exception( msg_r, history_r ), _kind( kind_r )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   METHOD NAME : UserRequestException::dumpOn
+  //   METHOD TYPE : std::ostream &
+  //
+  std::ostream & UserRequestException::dumpOn( std::ostream & str ) const
+  {
+    switch ( _kind )
+    {
+      case UNSPECIFIED: str << "UNSPECIFIED"; break;
+      case IGNORE:      str << "IGNORE";      break;
+      case SKIP:        str << "SKIP";        break;
+      case RETRY:       str << "RETRY";       break;
+      case ABORT:       str << "ABORT";       break;
+       // no default !
+    }
+    return str << " request: " << msg();
+  }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/base/UserRequestException.h b/zypp/base/UserRequestException.h
new file mode 100644 (file)
index 0000000..c4103b2
--- /dev/null
@@ -0,0 +1,107 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/UserRequestException.h
+ *
+*/
+#ifndef ZYPP_BASE_USERREQUESTEXCEPTION_H
+#define ZYPP_BASE_USERREQUESTEXCEPTION_H
+
+#include <iosfwd>
+
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : UserRequestException
+  //
+  /** Base for exceptions caused by explicit user request.
+   *
+   * Use the derived convenience classes to throw exceptions
+   * of a certain kind.
+   * \code
+   *     ProgressData ticks( makeProgressData( input_r ) );
+   *     ticks.sendTo( fnc_r );
+   *     ticks.toMin(); // start sending min (0)
+   *
+   *     iostr::EachLine line( input_r );
+   *     for( ; line; line.next() )
+   *     {
+   *       // process the line
+   *
+   *       if ( ! ticks.set( input_r.stream().tellg() ) )
+   *         ZYPP_THROW( AbortRequestException( "" ) );
+   *     }
+   * \endcode
+   * \code
+   * // either this way
+   * catch ( const AbortRequestException & excpt_r )
+   * {
+   *   ...
+   * }
+   *
+   * // or that
+   * catch ( const UserRequestException & excpt_r )
+   * {
+   *   switch ( excpt_r.kind() )
+   *   {
+   *     case UserRequestException::ABORT:
+   *       ...
+   *       break;
+   *   }
+   * }
+   * \endcode
+  */
+  class UserRequestException : public Exception
+  {
+    public:
+      enum Kind { UNSPECIFIED, IGNORE, SKIP, RETRY, ABORT };
+    public:
+      explicit
+      UserRequestException( const std::string & msg_r = std::string() );
+      UserRequestException( const std::string & msg_r, const Exception & history_r );
+      explicit
+      UserRequestException( Kind kind_r, const std::string & msg_r = std::string() );
+      UserRequestException( Kind kind_r, const std::string & msg_r, const Exception & history_r );
+    public:
+      Kind kind() const
+      { return _kind; }
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      Kind _kind;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** Convenience macro to declare more specific PluginScriptExceptions. */
+#define declException( EXCP, KIND )                                    \
+  struct EXCP : public UserRequestException {                          \
+    explicit                                                           \
+    EXCP( const std::string & msg_r = std::string() )                  \
+      : UserRequestException( KIND, msg_r )                            \
+    {}                                                                 \
+    EXCP( const std::string & msg_r, const Exception & history_r )     \
+      : UserRequestException( KIND, msg_r, history_r )                 \
+    {}                                                                 \
+  }
+
+  declException( IgnoreRequestException, IGNORE );
+  declException( SkipRequestException, SKIP );
+  declException( RetryRequestException, RETRY );
+  declException( AbortRequestException, ABORT );
+
+#undef declException
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_USERREQUESTEXCEPTION_H
diff --git a/zypp/base/WatchFile.h b/zypp/base/WatchFile.h
new file mode 100644 (file)
index 0000000..b731f6e
--- /dev/null
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/WatchFile.h
+ *
+*/
+#ifndef ZYPP_BASE_WATCHFILE_H
+#define ZYPP_BASE_WATCHFILE_H
+
+#include <iosfwd>
+
+#include "zypp/PathInfo.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : WatchFile
+  //
+  /** Remember a files attributes to detect content changes.
+   *
+   * Repeatedly call \ref hasChanged to check whether the content has
+   * changed since the last call. Creation or deletion of the file will
+   * be reported as change as well.
+   *
+   * Per default the ctor stats the file, so \ref hasChanged will detect
+   * changes done after \ref WatchFile was created.
+   *
+   * You may omit the initial stat by passing \c NO_INIT as second argument
+   * to the ctor. \ref WatchFile  will behave as if the file did not exist
+   * at the time \ref WatchFile was created.
+   *
+   * \code
+   * static WatchFile sysconfigFile( "/etc/sysconfig/SuSEfirewall2",
+   *                                 WatchFile::NO_INIT );
+   * if ( sysconfigFile.hasChanged() )
+   * {
+   *   // reload the file...
+   * }
+   * \endcode
+  */
+  class WatchFile
+  {
+    public:
+      enum Initial { NO_INIT, INIT };
+
+    public:
+      /** */
+      WatchFile( const Pathname & path_r = Pathname(),
+                Initial mode            = INIT )
+      : _path( path_r )
+      {
+       PathInfo pi( mode == INIT ? path_r : Pathname() );
+       _size  = pi.size();
+       _mtime = pi.mtime();
+      }
+
+      const Pathname & path() const
+      { return _path; }
+
+      bool hasChanged()
+      {
+       PathInfo pi( _path );
+       if ( _size != pi.size() || _mtime != pi.mtime() )
+       {
+         _size = pi.size();
+         _mtime = pi.mtime();
+         return true;
+       }
+       return false;
+      }
+
+    private:
+      Pathname _path;
+      off_t  _size;
+      time_t _mtime;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_WATCHFILE_H
diff --git a/zypp/media/CredentialFileReader.cc b/zypp/media/CredentialFileReader.cc
new file mode 100644 (file)
index 0000000..853a587
--- /dev/null
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialFileReader.cc
+ *
+ */
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/parser/IniDict.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+using std::endl;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "parser"
+
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialFileReader
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialFileReader::CredentialFileReader(
+      const Pathname & crfile,
+      const ProcessCredentials & callback)
+  {
+    InputStream is(crfile);
+    parser::IniDict dict(is);
+    for (parser::IniDict::section_const_iterator its = dict.sectionsBegin();
+         its != dict.sectionsEnd();
+         ++its)
+    {
+      Url storedUrl;
+      if (!its->empty())
+      {
+        try { storedUrl = Url(*its); }
+        catch (const url::UrlException &)
+        {
+          ERR << "invalid URL '" << *its << "' in credentials in file: "
+              << crfile << endl;
+          continue;
+        }
+      }
+
+      AuthData_Ptr credentials;
+      credentials.reset(new AuthData());
+
+      // set url
+      if (storedUrl.isValid())
+        credentials->setUrl(storedUrl);
+
+      for (parser::IniDict::entry_const_iterator it = dict.entriesBegin(*its);
+           it != dict.entriesEnd(*its);
+           ++it)
+      {
+        if (it->first == "username")
+          credentials->setUsername(it->second);
+        else if (it->first == "password")
+          credentials->setPassword(it->second);
+        else
+          ERR << "Unknown attribute in [" << crfile << "]: "
+              << it->second << " ignored" << endl;
+      }
+
+      if (credentials->valid())
+        callback(credentials);
+      else
+        ERR << "invalid credentials in file: " << crfile << endl;
+    } // sections
+  }
+
+
+  CredentialFileReader::~CredentialFileReader()
+  {}
+
+
+    /////////////////////////////////////////////////////////////////
+  } // media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/media/CredentialFileReader.h b/zypp/media/CredentialFileReader.h
new file mode 100644 (file)
index 0000000..fba8e60
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialFileReader.h
+ *
+ */
+#ifndef ZYPP_MEDIA_CREDENTIALFILEREADER_H
+#define ZYPP_MEDIA_CREDENTIALFILEREADER_H
+
+#include "zypp/base/Function.h"
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/media/MediaUserAuth.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialFileReader 
+  //
+  class CredentialFileReader
+  {
+  public:
+    /**
+      * Callback definition.
+      * First parameter is the \ref Url with which the credentials are
+      * associated, the second are the credentials.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+    typedef function<bool(AuthData_Ptr &)> ProcessCredentials;
+
+    CredentialFileReader(const Pathname & crfile,
+                         const ProcessCredentials & callback);
+    ~CredentialFileReader();
+  private:
+    ProcessCredentials _callback;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+    /////////////////////////////////////////////////////////////////
+  } // media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // zypp
+///////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_MEDIA_CREDENTIALFILEREADER_H */
diff --git a/zypp/media/CredentialManager.cc b/zypp/media/CredentialManager.cc
new file mode 100644 (file)
index 0000000..c8a00ec
--- /dev/null
@@ -0,0 +1,452 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialManager.cc
+ *
+ */
+#include <iostream>
+#include <fstream>
+
+#include "zypp/ZConfig.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Easy.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+#include "zypp/media/CredentialManager.h"
+
+#define USER_CREDENTIALS_FILE ".zypp/credentials.cat"
+
+using namespace std;
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////
+  namespace media
+  { ////////////////////////////////////////////////////////////////////
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : AuthDataComparator
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  const url::ViewOption AuthDataComparator::vopt =
+    url::ViewOption::DEFAULTS
+    - url::ViewOption::WITH_USERNAME
+    - url::ViewOption::WITH_PASSWORD
+    - url::ViewOption::WITH_QUERY_STR;
+
+  bool
+  AuthDataComparator::operator()(
+      const AuthData_Ptr & lhs, const AuthData_Ptr & rhs)
+  {
+    if (lhs->username() != rhs->username())
+      return true;
+
+    if (lhs->url().asString(vopt) != rhs->url().asString(vopt))
+      return true;
+
+    return false;
+  }
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredManagerOptions
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredManagerOptions::CredManagerOptions(const Pathname & rootdir)
+    : globalCredFilePath(rootdir / ZConfig::instance().credentialsGlobalFile())
+    , customCredFileDir(rootdir / ZConfig::instance().credentialsGlobalDir())
+  {
+    char * homedir = getenv("HOME");
+    if (homedir)
+      userCredFilePath = rootdir / homedir / USER_CREDENTIALS_FILE;
+  }
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager::Impl
+  //
+  struct CredentialManager::Impl
+  {
+    Impl(const CredManagerOptions & options);
+
+    ~Impl()
+    {}
+
+    void init_globalCredentials();
+    void init_userCredentials();
+
+    bool processCredentials(AuthData_Ptr & cred);
+
+    AuthData_Ptr getCred(const Url & url) const;
+    AuthData_Ptr getCredFromFile(const Pathname & file);
+    void saveGlobalCredentials();
+    void saveUserCredentials();
+
+
+    CredManagerOptions _options;
+
+    CredentialSet _credsGlobal;
+    CredentialSet _credsUser;
+    CredentialSet _credsTmp;
+
+    bool _globalDirty;
+    bool _userDirty;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager::Impl
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialManager::Impl::Impl(const CredManagerOptions & options)
+    : _options(options)
+    , _globalDirty(false)
+    , _userDirty(false)
+  {
+    init_globalCredentials();
+    init_userCredentials();
+  }
+
+
+  void CredentialManager::Impl::init_globalCredentials()
+  {
+    if (_options.globalCredFilePath.empty())
+      DBG << "global cred file not known";
+    else if (PathInfo(_options.globalCredFilePath).isExist())
+    {
+    /*  list<Pathname> entries;
+      if (filesystem::readdir(entries, _options.globalCredFilePath, false) != 0)
+        ZYPP_THROW(Exception("failed to read directory"));
+
+      for_(it, entries.begin(), entries.end())*/
+
+      CredentialFileReader(_options.globalCredFilePath,
+          bind(&Impl::processCredentials, this, _1));
+    }
+    else
+      DBG << "global cred file does not exist";
+
+    _credsGlobal = _credsTmp; _credsTmp.clear();
+    DBG << "Got " << _credsGlobal.size() << " global records." << endl;
+  }
+
+
+  void CredentialManager::Impl::init_userCredentials()
+  {
+    if (_options.userCredFilePath.empty())
+      DBG << "user cred file not known";
+    else if (PathInfo(_options.userCredFilePath).isExist())
+    {
+    /*  list<Pathname> entries;
+      if (filesystem::readdir(entries, _options.userCredFilePath, false ) != 0)
+        ZYPP_THROW(Exception("failed to read directory"));
+
+      for_(it, entries.begin(), entries.end())*/
+      CredentialFileReader(_options.userCredFilePath,
+          bind(&Impl::processCredentials, this, _1));
+    }
+    else
+      DBG << "user cred file does not exist" << endl;
+
+    _credsUser = _credsTmp; _credsTmp.clear();
+    DBG << "Got " << _credsUser.size() << " user records." << endl;
+  }
+
+
+  bool CredentialManager::Impl::processCredentials(AuthData_Ptr & cred)
+  {
+    _credsTmp.insert(cred);
+    return true;
+  }
+
+
+  static AuthData_Ptr findIn(const CredentialManager::CredentialSet & set,
+                             const Url & url,
+                             url::ViewOption vopt)
+  {
+    const string & username = url.getUsername();
+    for(CredentialManager::CredentialIterator it = set.begin(); it != set.end(); ++it)
+    {
+      // this ignores url params - not sure if it is good or bad...
+      if (url.asString(vopt).find((*it)->url().asString(vopt)) == 0)
+      {
+        if (username.empty() || username == (*it)->username())
+          return *it;
+      }
+    }
+
+    return AuthData_Ptr();
+  }
+
+
+  AuthData_Ptr CredentialManager::Impl::getCred(const Url & url) const
+  {
+    AuthData_Ptr result;
+
+    // compare the urls via asString(), but ignore password
+    // default url::ViewOption will take care of that.
+    // operator==(Url,Url) compares the whole Url
+
+    url::ViewOption vopt;
+    vopt = vopt
+      - url::ViewOption::WITH_USERNAME
+      - url::ViewOption::WITH_PASSWORD
+      - url::ViewOption::WITH_QUERY_STR;
+
+    // search in global credentials
+    result = findIn(_credsGlobal, url, vopt);
+
+    // search in home credentials
+    if (!result)
+      result = findIn(_credsUser, url, vopt);
+
+    if (result)
+      DBG << "Found credentials for '" << url << "':" << endl << *result;
+    else
+      DBG << "No credentials for '" << url << "'" << endl;
+
+    return result;
+  }
+
+
+  AuthData_Ptr CredentialManager::Impl::getCredFromFile(const Pathname & file)
+  {
+    AuthData_Ptr result;
+
+    Pathname credfile;
+    if (file.absolute())
+      // get from that file
+      credfile = file;
+    else
+      // get from /etc/zypp/credentials.d, delete the leading path
+      credfile = _options.customCredFileDir / file.basename();
+
+    CredentialFileReader(credfile, bind(&Impl::processCredentials, this, _1));
+    if (_credsTmp.empty())
+      WAR << file << " does not contain valid credentials or is not readable." << endl;
+    else
+    {
+      result = *_credsTmp.begin();
+      _credsTmp.clear();
+    }
+
+    return result;
+  }
+
+  static int save_creds_in_file(
+      const CredentialManager::CredentialSet creds,
+      const Pathname & file,
+      const mode_t mode)
+  {
+    int ret = 0;
+    filesystem::assert_dir(file.dirname());
+
+    std::ofstream fs(file.c_str());
+    if (!fs)
+      ret = 1;
+
+    for_(it, creds.begin(), creds.end())
+    {
+      (*it)->dumpAsIniOn(fs);
+      fs << endl;
+    }
+    fs.close();
+
+    filesystem::chmod(file, mode);
+
+    return ret;
+  }
+
+  void  CredentialManager::Impl::saveGlobalCredentials()
+  {
+    save_creds_in_file(_credsGlobal, _options.globalCredFilePath, 0640);
+  }
+
+  void  CredentialManager::Impl::saveUserCredentials()
+  {
+    save_creds_in_file(_credsUser, _options.userCredFilePath, 0600);
+  }
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialManager::CredentialManager(const CredManagerOptions & opts)
+    : _pimpl(new Impl(opts))
+  {}
+
+
+  AuthData_Ptr CredentialManager::getCred(const Url & url)
+  {
+    string credfile = url.getQueryParam("credentials");
+    if (credfile.empty())
+      return _pimpl->getCred(url);
+    return _pimpl->getCredFromFile(credfile);
+  }
+
+
+  AuthData_Ptr CredentialManager::getCredFromFile(const Pathname & file)
+  { return _pimpl->getCredFromFile(file); }
+
+
+  void CredentialManager::addCred(const AuthData & cred)
+  {
+    Pathname credfile = cred.url().getQueryParam("credentials");
+    if (credfile.empty())
+      //! \todo ask user where to store these creds. saving to user creds for now
+      addUserCred(cred);
+    else
+      saveInFile(cred, credfile);
+  }
+
+
+  void CredentialManager::addGlobalCred(const AuthData & cred)
+  {
+    AuthData_Ptr c_ptr;
+    c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
+    pair<CredentialIterator, bool> ret = _pimpl->_credsGlobal.insert(c_ptr);
+    if (ret.second)
+      _pimpl->_globalDirty = true;
+    else if ((*ret.first)->password() != cred.password())
+    {
+      _pimpl->_credsGlobal.erase(ret.first);
+      _pimpl->_credsGlobal.insert(c_ptr);
+      _pimpl->_globalDirty = true;
+    }
+  }
+
+
+  void CredentialManager::addUserCred(const AuthData & cred)
+  {
+    AuthData_Ptr c_ptr;
+    c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
+    pair<CredentialIterator, bool> ret = _pimpl->_credsUser.insert(c_ptr);
+    if (ret.second)
+      _pimpl->_userDirty = true;
+    else if ((*ret.first)->password() != cred.password())
+    {
+      _pimpl->_credsUser.erase(ret.first);
+      _pimpl->_credsUser.insert(c_ptr);
+      _pimpl->_userDirty = true;
+    }
+  }
+
+
+  void CredentialManager::save()
+  {
+    if (_pimpl->_globalDirty)
+      _pimpl->saveGlobalCredentials();
+    if (_pimpl->_userDirty)
+      _pimpl->saveUserCredentials();
+    _pimpl->_globalDirty = false;
+    _pimpl->_userDirty = false;
+  }
+
+
+  void CredentialManager::saveInGlobal(const AuthData & cred)
+  {
+    addGlobalCred(cred);
+    save();
+  }
+
+
+  void CredentialManager::saveInUser(const AuthData & cred)
+  {
+    addUserCred(cred);
+    save();
+  }
+
+
+  void CredentialManager::saveInFile(const AuthData & cred, const Pathname & credFile)
+  {
+    AuthData_Ptr c_ptr;
+    c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
+    c_ptr->setUrl(Url()); // don't save url in custom creds file
+    CredentialManager::CredentialSet creds;
+    creds.insert(c_ptr);
+
+    int ret;
+    if (credFile.absolute())
+      ret = save_creds_in_file(creds, credFile, 0640);
+    else
+      ret = save_creds_in_file(
+          creds, _pimpl->_options.customCredFileDir / credFile, 0600);
+
+    if (!ret)
+    {
+      //! \todo figure out the reason(?), call back to user
+      ERR << "error saving the credentials" << endl;
+    }
+  }
+
+
+  void CredentialManager::clearAll(bool global)
+  {
+    if (global)
+    {
+      if (!filesystem::unlink(_pimpl->_options.globalCredFilePath))
+        ERR << "could not delete user credentials file "
+            << _pimpl->_options.globalCredFilePath << endl;
+      _pimpl->_credsUser.clear();
+    }
+    else
+    {
+      if (!filesystem::unlink(_pimpl->_options.userCredFilePath))
+        ERR << "could not delete global credentials file"
+            << _pimpl->_options.userCredFilePath << endl;
+      _pimpl->_credsGlobal.clear();
+    }
+  }
+
+
+  CredentialManager::CredentialIterator CredentialManager::credsGlobalBegin() const
+  { return _pimpl->_credsGlobal.begin(); }
+
+  CredentialManager::CredentialIterator CredentialManager::credsGlobalEnd() const
+  { return _pimpl->_credsGlobal.end(); }
+
+  CredentialManager::CredentialSize CredentialManager::credsGlobalSize() const
+  { return _pimpl->_credsGlobal.size(); }
+
+  bool CredentialManager::credsGlobalEmpty() const
+  { return _pimpl->_credsGlobal.empty(); }
+
+
+  CredentialManager::CredentialIterator CredentialManager::credsUserBegin() const
+  { return _pimpl->_credsUser.begin(); }
+
+  CredentialManager::CredentialIterator CredentialManager::credsUserEnd() const
+  { return _pimpl->_credsUser.end(); }
+
+  CredentialManager::CredentialSize CredentialManager::credsUserSize() const
+  { return _pimpl->_credsUser.size(); }
+
+  bool CredentialManager::credsUserEmpty() const
+  { return _pimpl->_credsUser.empty(); }
+
+
+    ////////////////////////////////////////////////////////////////////
+  } // media
+  //////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // zypp
+//////////////////////////////////////////////////////////////////////
diff --git a/zypp/media/CredentialManager.h b/zypp/media/CredentialManager.h
new file mode 100644 (file)
index 0000000..5ebcf76
--- /dev/null
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialManager.h
+ *
+ */
+#ifndef ZYPP_MEDIA_CREDENTIALMANAGER_H
+#define ZYPP_MEDIA_CREDENTIALMANAGER_H
+
+#include <set>
+
+#include "zypp/Pathname.h"
+#include "zypp/media/MediaUserAuth.h"
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  class Url;
+
+  //////////////////////////////////////////////////////////////////////
+  namespace media
+  { ////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredManagerOptions
+  //
+  /**
+   * \todo configurable cred file locations
+   */
+  struct CredManagerOptions
+  {
+    CredManagerOptions(const Pathname & rootdir = "");
+
+    Pathname globalCredFilePath;
+    Pathname userCredFilePath;
+    Pathname customCredFileDir;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+  // comparator for CredentialSet
+  struct AuthDataComparator
+  {
+    static const url::ViewOption vopt;
+    bool operator()(const AuthData_Ptr & lhs, const AuthData_Ptr & rhs);
+  };
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager
+  //
+  /**
+   * \todo better method names
+   * \todo delete(AuthData) method
+   */
+  class CredentialManager
+  {
+  public:
+    typedef std::set<AuthData_Ptr, AuthDataComparator> CredentialSet;
+    typedef CredentialSet::size_type                   CredentialSize;
+    typedef CredentialSet::const_iterator              CredentialIterator;
+
+
+    CredentialManager(const CredManagerOptions & opts = CredManagerOptions());
+
+    ~CredentialManager()
+    {}
+
+  public:
+    /**
+     * Get credentials for the specified \a url.
+     *
+     * If the URL contains also username, it will be used to find the match
+     * for this user (in case mutliple are available).
+     *
+     * \param url URL to find credentials for.
+     * \return Pointer to retrieved authentication data on success or an empty
+     *         AuthData_Ptr otherwise.
+     * \todo return a copy instead?
+     */
+    AuthData_Ptr getCred(const Url & url);
+
+    /**
+     * Read credentials from a file.
+     */
+    AuthData_Ptr getCredFromFile(const Pathname & file);
+
+    /**
+     * Add new global credentials.
+     */
+    void addGlobalCred(const AuthData & cred);
+
+    /**
+     * Add new user credentials.
+     */
+    void addUserCred(const AuthData & cred);
+
+    /**
+     * Add new credentials with user callbacks.
+     *
+     * If the cred->url() contains 'credentials' query parameter, the
+     * credentials will be automatically saved to the specified file using the
+     * \ref saveInFile() method.
+     *
+     * Otherwise a callback will be called asking whether to save to custom
+     * file, or to global or user's credentials catalog.
+     *
+     * \todo Currently no callback is called, credentials are automatically
+     *       saved to user's credentials.cat if no 'credentials' parameter
+     *       has been specified
+     */
+    void addCred(const AuthData & cred);
+
+    /**
+     * Saves any unsaved credentials added via \ref addUserCred() or
+     * \a addGlobalCred() methods.
+     */
+    void save();
+
+    /**
+     * Saves given \a cred to global credentials file.
+     *
+     * \note Use this method to add just one piece of credentials. To add
+     *       multiple items at once, use addGlobalCred() followed
+     *       by save()
+     */
+    void saveInGlobal(const AuthData & cred);
+
+    /**
+     * Saves given \a cred to user's credentials file.
+     *
+     * \note Use this method to add just one piece of credentials. To add
+     *       multiple items at once, use addUserCred() followed
+     *       by save()
+     */
+    void saveInUser(const AuthData & cred);
+
+    /**
+     * Saves given \a cred to user specified credentials file.
+     *
+     * If the credFile path is absolute, it will be saved at that precise
+     * location. If \a credFile is just a filename, it will be saved
+     * in \ref CredManagerOptions::customCredFileDir. Otherwise the current
+     * working directory will be prepended to the file path.
+     */
+    void saveInFile(const AuthData &, const Pathname & credFile);
+
+    /**
+     * Remove all global or user credentials from memory and disk.
+     *
+     * \param global  Whether to remove global or user credentials.
+     */
+    void clearAll(bool global = false);
+
+
+    CredentialIterator credsGlobalBegin() const;
+    CredentialIterator credsGlobalEnd()   const;
+    CredentialSize     credsGlobalSize()  const;
+    bool               credsGlobalEmpty() const;
+
+    CredentialIterator credsUserBegin() const;
+    CredentialIterator credsUserEnd()   const;
+    CredentialSize     credsUserSize()  const;
+    bool               credsUserEmpty() const;
+
+    class Impl;
+  private:
+    RW_pointer<Impl> _pimpl;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+    ////////////////////////////////////////////////////////////////////
+  } // media
+  //////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_MEDIA_CREDENTIALMANAGER_H */
+
diff --git a/zypp/media/CurlConfig.cc b/zypp/media/CurlConfig.cc
new file mode 100644 (file)
index 0000000..80dfcf4
--- /dev/null
@@ -0,0 +1,185 @@
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/media/CurlConfig.h"
+
+using namespace std;
+
+namespace zypp
+{
+  namespace media
+  {
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  METHOD NAME : CurlConfig::parseConfig
+  //  METHOD TYPE : int
+  //
+  int CurlConfig::parseConfig(CurlConfig & config, const std::string & filename)
+  {
+    Pathname curlrcFile;
+
+    if(filename.empty())
+    {
+      // attempts to load .curlrc from the homedir
+      char *home = getenv("HOME");
+      if(home)
+        curlrcFile = string( home ) + string( "/.curlrc" );
+    }
+    else
+      curlrcFile = filename;
+
+    PathInfo h_info(curlrcFile.dirname(), PathInfo::LSTAT);
+    PathInfo c_info(curlrcFile,           PathInfo::LSTAT);
+
+    if( h_info.isDir()  && h_info.owner() == getuid() &&
+        c_info.isFile() && c_info.owner() == getuid())
+    {
+      MIL << "Going to parse " << curlrcFile << endl;
+    }
+    else
+    {
+      char buf[32] = {NULL};
+      WAR << "Not allowed to parse '" << curlrcFile
+          << "': dir/file owner: " << h_info.owner() << "/" << c_info.owner()
+          << ", process uid: " << getuid()
+          << " (" << (!getlogin_r(buf, 31) ? buf : "") << ")" << std::endl;
+
+      return 1;
+    }
+
+    ifstream inp(curlrcFile.c_str());
+    for(iostr::EachLine in( inp ); in; in.next())
+    {
+      string line = str::trim(*in);
+
+      // skip empty lines and comments
+      if (line.empty())
+        continue;
+      switch (line[0])
+      {
+      case '#':
+      case '/':
+      case '\r':
+      case '\n':
+      case '*':
+      case '\0':
+        continue;
+      }
+
+      // DBG << "line " << in.lineNo() << ": " << line << endl; // can't log passwords
+
+      const char * beg = line.c_str();
+      const char * cur = beg;
+
+// space, '=' and ':' are all valid separators in curlrc
+#define ISSEP(x) (((x)=='=') || ((x) == ':') || isspace(x))
+
+      // skip leading dashes (they are optional)
+      while (*cur && *cur == '-')
+        cur++;
+      beg = cur;
+
+      // skip non-separator characters
+      while (*cur && !ISSEP(*cur))
+        cur++;
+
+      string option(beg, cur - beg);
+
+      // skip separator characters
+      while (*cur && ISSEP(*cur))
+        cur++;
+
+      // rewind to the end of the line
+      beg = cur;
+      while (*cur)
+        cur++;
+
+      string value(beg, cur - beg);
+
+      DBG << "GOT: " << option << endl;
+
+      if (!value.empty())
+      {
+        // quoted parameter
+        if (value[0] == '\"')
+        {
+          // remove the quotes
+          string::size_type pos = value.rfind('\"');
+          bool cut_last =
+            pos == value.size() - 1 && pos > 1 && value[pos-1] != '\\';
+          value = value.substr(1,
+              cut_last ? value.size() - 2 : value.size() - 1);
+
+          // replace special characters:
+          pos = 0;
+          while ((pos = value.find('\\', pos)) != string::npos)
+          {
+            // just erase the backslash if it is found at the end
+            if (pos == value.size() - 1)
+            {
+              value = value.erase(pos, 1);
+              break;
+            }
+
+            switch(value[pos+1])
+            {
+            case 't':
+              value = value.replace(pos, 2, "\t");
+              break;
+            case 'n':
+              value = value.replace(pos, 2, "\n");
+              break;
+            case 'r':
+              value = value.replace(pos, 2, "\r");
+              break;
+            case 'v':
+              value = value.replace(pos, 2, "\v");
+              break;
+            case '\\':
+              value = value.erase(pos++, 1);
+              break;
+            default:;
+              value = value.erase(pos, 1);
+            }
+          }
+        }
+
+        // DBG << "PARAM: " << value << endl; // can't log passwords
+      }
+
+      CurlConfig::setParameter(config, option, value);
+    } // for EachLine in curlrc
+
+    return 0;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  METHOD NAME : CurlConfig::setParameter
+  //  METHOD TYPE : int
+  //
+  int CurlConfig::setParameter(CurlConfig & config,
+                               const std::string & option,
+                               const std::string & value)
+  {
+    if (option == "proxy-user")
+      config.proxyuserpwd = value;
+    // add more curl config data here as they become needed
+    // else if (option == "foo")
+    else
+      DBG << "Ignoring option " << option << endl;
+
+    return 0;
+  }
+
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/CurlConfig.h b/zypp/media/CurlConfig.h
new file mode 100644 (file)
index 0000000..87b28ed
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef ZYPP_MEDIA_CURLRCONFIG_H_
+#define ZYPP_MEDIA_CURLRCONFIG_H_
+
+//#include "zypp/base/NonCopyable.h"
+#include "zypp/base/String.h"
+
+namespace zypp
+{
+  namespace media
+  {
+
+
+  /**
+   * Structure holding values of curlrc options.
+   */
+  struct CurlConfig
+  {
+  public:
+    /**
+     * Parse a curlrc file and store the result in the \a config structure.
+     * 
+     * \param config   a CurlConfig structure
+     * \param filename path to the curlrc file. If empty, ~/.curlrc is used.
+     * \return         0 on success, 1 if problem occurs.
+     */
+    static int parseConfig(CurlConfig & config, const std::string & filename = "");
+
+    /**
+     * Stores the \a value of the \a option in the \a config structure or
+     * logs an unknown option.
+     * 
+     * \return         0 on success, 1 if problem occurs.
+     */
+    static int setParameter(CurlConfig & config,
+                            const std::string & option,
+                            const std::string & value);
+
+  public:
+    std::string proxyuserpwd;
+    // add more curl config data here as they become needed
+  };
+
+
+  } // namespace media
+} // namespace zypp
+
+#endif /*ZYPP_MEDIA_CURLRCONFIG_H_*/
diff --git a/zypp/media/MediaAccess.cc b/zypp/media/MediaAccess.cc
new file mode 100644 (file)
index 0000000..482395f
--- /dev/null
@@ -0,0 +1,524 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAccess.cc
+ *
+*/
+
+#include <ctype.h>
+
+#include <iostream>
+#include <map>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ZConfig.h"
+#include "zypp/PluginScript.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/media/MediaException.h"
+#include "zypp/media/MediaAccess.h"
+#include "zypp/media/MediaHandler.h"
+
+#include "zypp/media/MediaNFS.h"
+#include "zypp/media/MediaCD.h"
+#include "zypp/media/MediaDIR.h"
+#include "zypp/media/MediaDISK.h"
+#include "zypp/media/MediaCIFS.h"
+#include "zypp/media/MediaCurl.h"
+#include "zypp/media/MediaAria2c.h"
+#include "zypp/media/MediaMultiCurl.h"
+#include "zypp/media/MediaISO.h"
+#include "zypp/media/MediaPlugin.h"
+#include "zypp/media/UrlResolverPlugin.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaAccess
+//
+///////////////////////////////////////////////////////////////////
+
+const Pathname MediaAccess::_noPath; // empty path
+
+///////////////////////////////////////////////////////////////////
+// constructor
+MediaAccess::MediaAccess ()
+    : _handler (0)
+{
+}
+
+// destructor
+MediaAccess::~MediaAccess()
+{
+  try
+    {
+      close(); // !!! make sure handler gets properly deleted.
+    }
+  catch(...) {}
+}
+
+AttachedMedia
+MediaAccess::attachedMedia() const
+{
+       return _handler ? _handler->attachedMedia()
+                       : AttachedMedia();
+}
+
+bool
+MediaAccess::isSharedMedia() const
+{
+       return _handler ? _handler->isSharedMedia()
+                       : false;
+}
+
+void
+MediaAccess::resetParentId()
+{
+       if( _handler) _handler->resetParentId();
+}
+
+bool
+MediaAccess::dependsOnParent() const
+{
+       return _handler ? _handler->dependsOnParent() : false;
+}
+
+bool
+MediaAccess::dependsOnParent(MediaAccessId parentId,
+                             bool exactIdMatch) const
+{
+       return _handler ? _handler->dependsOnParent(parentId, exactIdMatch)
+                       : false;
+}
+
+// open URL
+void
+MediaAccess::open (const Url& o_url, const Pathname & preferred_attach_point)
+{
+    if(!o_url.isValid()) {
+       MIL << "Url is not valid" << endl;
+        ZYPP_THROW(MediaBadUrlException(o_url));
+    }
+
+    close();
+
+    UrlResolverPlugin::HeaderList custom_headers;
+    Url url = UrlResolverPlugin::resolveUrl(o_url, custom_headers);
+    
+    std::string scheme = url.getScheme();
+    MIL << "Trying scheme '" << scheme << "'" << endl;
+
+    /*
+    ** WARNING: Don't forget to update MediaAccess::downloads(url)
+    **          if you are adding a new url scheme / handler!
+    */
+    if (scheme == "cd" || scheme == "dvd")
+        _handler = new MediaCD (url,preferred_attach_point);
+    else if (scheme == "nfs" || scheme == "nfs4")
+        _handler = new MediaNFS (url,preferred_attach_point);
+    else if (scheme == "iso")
+        _handler = new MediaISO (url,preferred_attach_point);
+    else if (scheme == "file" || scheme == "dir")
+        _handler = new MediaDIR (url,preferred_attach_point);
+    else if (scheme == "hd")
+        _handler = new MediaDISK (url,preferred_attach_point);
+    else if (scheme == "cifs" || scheme == "smb")
+       _handler = new MediaCIFS (url,preferred_attach_point);
+    else if (scheme == "ftp" || scheme == "http" || scheme == "https")
+    {
+        // Another good idea would be activate MediaAria2c handler via external var
+        bool use_aria = false;
+        bool use_multicurl = true;
+        string urlmediahandler ( url.getQueryParam("mediahandler") );
+        if ( urlmediahandler == "multicurl" )
+        {
+          use_aria = false;
+          use_multicurl = true;
+        }
+        else if ( urlmediahandler == "aria2c" )
+        {
+          use_aria = true;
+          use_multicurl = false;
+        }
+        else if ( urlmediahandler == "curl" )
+        {
+          use_aria = false;
+          use_multicurl = false;
+        }
+        else
+        {
+          if ( urlmediahandler.empty() )
+          {
+            WAR << "unknown mediahandler set: " << urlmediahandler << endl;
+          }
+          const char *ariaenv = getenv( "ZYPP_ARIA2C" );
+          const char *multicurlenv = getenv( "ZYPP_MULTICURL" );
+          // if user disabled it manually
+          if ( use_multicurl && multicurlenv && ( strcmp(multicurlenv, "0" ) == 0 ) )
+          {
+              WAR << "multicurl manually disabled." << endl;
+              use_multicurl = false;
+          }
+          else if ( !use_multicurl && multicurlenv && ( strcmp(multicurlenv, "1" ) == 0 ) )
+          {
+              WAR << "multicurl manually enabled." << endl;
+              use_multicurl = true;
+          }
+          // if user disabled it manually
+          if ( use_aria && ariaenv && ( strcmp(ariaenv, "0" ) == 0 ) )
+          {
+              WAR << "aria2c manually disabled. Falling back to curl" << endl;
+              use_aria = false;
+          }
+          else if ( !use_aria && ariaenv && ( strcmp(ariaenv, "1" ) == 0 ) )
+          {
+              // no aria for ftp - no advantage in that over curl
+              if ( url.getScheme() == "ftp" )
+                  WAR << "no aria2c for FTP, despite ZYPP_ARIA2C=1" << endl;
+              else
+              {
+                  WAR << "aria2c manually enabled." << endl;
+                  use_aria = true;
+              }
+          }
+        }
+
+        // disable if it does not exist
+        if ( use_aria && ! MediaAria2c::existsAria2cmd() )
+        {
+            WAR << "aria2c not found. Falling back to curl" << endl;
+            use_aria = false;
+        }
+
+        MediaCurl *curl;        
+
+        if ( use_aria )
+            curl = new MediaAria2c (url,preferred_attach_point);        
+        else if ( use_multicurl )                     
+            curl = new MediaMultiCurl (url,preferred_attach_point); 
+       else
+            curl = new MediaCurl (url,preferred_attach_point);
+        
+        UrlResolverPlugin::HeaderList::const_iterator it;
+        for (it = custom_headers.begin();
+             it != custom_headers.end();
+             ++it) {
+            std::string header = it->first + ": " + it->second;            
+            MIL << "Added custom header -> " << header << endl;
+            curl->settings().addHeader(header);
+        }
+        _handler = curl;        
+    }
+    else if (scheme == "plugin" )
+       _handler = new MediaPlugin (url,preferred_attach_point);
+    else
+    {
+       ZYPP_THROW(MediaUnsupportedUrlSchemeException(url));
+    }
+
+    // check created handler
+    if ( !_handler ){
+      ERR << "Failed to create media handler" << endl;
+      ZYPP_THROW(MediaSystemException(url, "Failed to create media handler"));
+    }
+
+    MIL << "Opened: " << *this << endl;
+}
+
+// Type of media if open, otherwise NONE.
+std::string
+MediaAccess::protocol() const
+{
+  if ( !_handler )
+    return "unknown";
+
+  return _handler->protocol();
+}
+
+bool
+MediaAccess::downloads() const
+{
+       return _handler ? _handler->downloads() : false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaAccess::url
+//     METHOD TYPE : Url
+//
+Url MediaAccess::url() const
+{
+  if ( !_handler )
+    return Url();
+
+  return _handler->url();
+}
+
+// close handler
+void
+MediaAccess::close ()
+{
+  ///////////////////////////////////////////////////////////////////
+  // !!! make shure handler gets properly deleted.
+  // I.e. release attached media before deleting the handler.
+  ///////////////////////////////////////////////////////////////////
+  if ( _handler ) {
+    try {
+      _handler->release();
+    }
+    catch (const MediaException & excpt_r)
+    {
+      ZYPP_CAUGHT(excpt_r);
+      WAR << "Close: " << *this << " (" << excpt_r << ")" << endl;
+      ZYPP_RETHROW(excpt_r);
+    }
+    MIL << "Close: " << *this << " (OK)" << endl;
+    delete _handler;
+    _handler = 0;
+  }
+}
+
+
+// attach media
+void MediaAccess::attach (bool next)
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("attach"));
+  }
+  _handler->attach(next);
+}
+
+// True if media is open and attached.
+bool
+MediaAccess::isAttached() const
+{
+  return( _handler && _handler->isAttached() );
+}
+
+
+bool MediaAccess::hasMoreDevices() const
+{
+  return _handler && _handler->hasMoreDevices();
+}
+
+
+void
+MediaAccess::getDetectedDevices(std::vector<std::string> & devices,
+                                unsigned int & index) const
+{
+  if (_handler)
+  {
+    _handler->getDetectedDevices(devices, index);
+    return;
+  }
+
+  if (!devices.empty())
+    devices.clear();
+  index = 0;
+}
+
+
+// local directory that corresponds to medias url
+// If media is not open an empty pathname.
+Pathname
+MediaAccess::localRoot() const
+{
+  if ( !_handler )
+    return _noPath;
+
+  return _handler->localRoot();
+}
+
+// Short for 'localRoot() + pathname', but returns an empty
+// * pathname if media is not open.
+Pathname
+MediaAccess::localPath( const Pathname & pathname ) const
+{
+  if ( !_handler )
+    return _noPath;
+
+  return _handler->localPath( pathname );
+}
+
+void
+MediaAccess::disconnect()
+{
+  if ( !_handler )
+    ZYPP_THROW(MediaNotOpenException("disconnect"));
+
+  _handler->disconnect();
+}
+
+
+void
+MediaAccess::release( const std::string & ejectDev )
+{
+  if ( !_handler )
+    return;
+
+  _handler->release( ejectDev );
+}
+
+// provide file denoted by path to attach dir
+//
+// filename is interpreted relative to the attached url
+// and a path prefix is preserved to destination
+void
+MediaAccess::provideFile( const Pathname & filename ) const
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("provideFile(" + filename.asString() + ")"));
+  }
+
+  _handler->provideFile( filename );
+}
+
+void
+MediaAccess::setDeltafile( const Pathname & filename ) const
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("setDeltafile(" + filename.asString() + ")"));
+  }
+
+  _handler->setDeltafile( filename );
+}
+
+void
+MediaAccess::releaseFile( const Pathname & filename ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releaseFile( filename );
+}
+
+// provide directory tree denoted by path to attach dir
+//
+// dirname is interpreted relative to the attached url
+// and a path prefix is preserved to destination
+void
+MediaAccess::provideDir( const Pathname & dirname ) const
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("provideDir(" + dirname.asString() + ")"));
+  }
+
+  _handler->provideDir( dirname );
+}
+
+void
+MediaAccess::provideDirTree( const Pathname & dirname ) const
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("provideDirTree(" + dirname.asString() + ")"));
+  }
+
+  _handler->provideDirTree( dirname );
+}
+
+void
+MediaAccess::releaseDir( const Pathname & dirname ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releaseDir( dirname );
+}
+
+void
+MediaAccess::releasePath( const Pathname & pathname ) const
+{
+  if ( !_handler )
+    return;
+
+  _handler->releasePath( pathname );
+}
+
+// Return content of directory on media
+void
+MediaAccess::dirInfo( std::list<std::string> & retlist, const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("dirInfo(" + dirname.asString() + ")"));
+  }
+
+  _handler->dirInfo( retlist, dirname, dots );
+}
+
+// Return content of directory on media
+void
+MediaAccess::dirInfo( filesystem::DirContent & retlist, const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("dirInfo(" + dirname.asString() + ")"));
+  }
+
+  _handler->dirInfo( retlist, dirname, dots );
+}
+
+// return if a file exists
+bool
+MediaAccess::doesFileExist( const Pathname & filename ) const
+{
+  if ( !_handler ) {
+    ZYPP_THROW(MediaNotOpenException("doesFileExist(" + filename.asString() + ")"));
+  }
+
+  return _handler->doesFileExist( filename );
+}
+
+std::ostream &
+MediaAccess::dumpOn( std::ostream & str ) const
+{
+  if ( !_handler )
+    return str << "MediaAccess( closed )";
+
+  str << _handler->protocol() << "(" << *_handler << ")";
+  return str;
+}
+
+void MediaAccess::getFile( const Url &from, const Pathname &to )
+{
+  DBG << "From: " << from << endl << "To: " << to << endl;
+
+  Pathname path = from.getPathData();
+  Pathname dir = path.dirname();
+  string base = path.basename();
+
+  Url u = from;
+  u.setPathData( dir.asString() );
+
+  MediaAccess media;
+
+  try {
+    media.open( u );
+    media.attach();
+    media._handler->provideFileCopy( base, to );
+    media.release();
+  }
+  catch (const MediaException & excpt_r)
+  {
+    ZYPP_RETHROW(excpt_r);
+  }
+}
+
+    std::ostream & operator<<( std::ostream & str, const MediaAccess & obj )
+    { return obj.dumpOn( str ); }
+
+///////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaAccess.h b/zypp/media/MediaAccess.h
new file mode 100644 (file)
index 0000000..e17f7b2
--- /dev/null
@@ -0,0 +1,453 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAccess.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAACCESS_H
+#define ZYPP_MEDIA_MEDIAACCESS_H
+
+#include <iosfwd>
+#include <map>
+#include <list>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/media/MediaException.h"
+#include "zypp/media/MediaSource.h"
+
+#include "zypp/Url.h"
+
+namespace zypp {
+  namespace media {
+
+    class MediaHandler;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaAccess
+    /**
+     * @short Handle access to a medium
+     *
+     * The concrete @ref MediaHandler for a certain url is created
+     * on @ref open and deleted on @close.
+     *
+     * The inteface here basically checks whether the handler exists,
+     * then forwards the request to @ref MediaHandler.
+     **/
+    class MediaAccess : public base::ReferenceCounted, private base::NonCopyable
+    {
+    public:
+       typedef intrusive_ptr<MediaAccess> Ptr;
+       typedef intrusive_ptr<const MediaAccess> constPtr;
+
+    private:
+
+       static const Pathname _noPath;
+
+       /**
+        * handler for 'physical' media
+        * == 0 if not open
+        **/
+       MediaHandler * _handler;
+
+       friend class MediaManager;
+       friend class MediaManager_Impl;
+
+       AttachedMedia        attachedMedia() const;
+
+       bool                 isSharedMedia() const;
+
+       void                 resetParentId();
+       bool                 dependsOnParent() const;
+
+       bool                 dependsOnParent(MediaAccessId parentId,
+                                            bool exactIdMatch) const;
+    public:
+
+       /**
+        * constructor
+        **/
+       MediaAccess();
+
+       /**
+        * open url. If preferred_attach_point is given,
+        * try to use it as attach point.
+        *
+        * <b>Caution:</b> The medium can choose a different attach point.
+        * Only getAttachPoint() knows the real attach point.
+        *
+        * \throws MediaException
+        *
+        **/
+       void open( const Url& url, const Pathname & preferred_attach_point = "" );
+
+       /**
+        * True if media is open.
+        **/
+       bool isOpen() const { return( _handler != 0 ); }
+
+       /**
+        * Hint if files are downloaded or not.
+        * @return True, if the files are downloaded.
+        */
+       bool        downloads() const;
+
+       /** \deprecated Use \ref Url::schemeIsDownloading */
+       static
+       ZYPP_DEPRECATED bool downloads(const Url &url)
+        { return url.schemeIsDownloading(); }
+
+       /** \deprecated Use \ref Url::schemeIsVolatile */
+       static
+       ZYPP_DEPRECATED bool canBeVolatile(const Url &url)
+        { return url.schemeIsVolatile(); }
+
+       /**
+        * Used Protocol if media is opened, otherwise 'unknown'.
+        **/
+        std::string protocol() const;
+
+       /**
+        * Url if media is opened, otherwise empty.
+        **/
+        Url url() const;
+
+       /**
+        * close url
+        *
+        * \throws MediaException
+        *
+        **/
+       void close();
+
+    public:
+
+       /**
+        * Use concrete handler to attach the media.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       void attach(bool next = false);
+
+       /**
+        * True if media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       bool isAttached() const;
+
+        bool hasMoreDevices() const;
+
+        /**
+         * Fill in a vector of detected ejectable devices and the index of the
+         * currently attached device within the vector. The contents of the vector
+         * are the device names (/dev/cdrom and such).
+         *
+         * \param devices  vector to load with the device names
+         * \param index    index of the currently used device in the devices vector
+         */
+        virtual void
+        getDetectedDevices(std::vector<std::string> & devices,
+                           unsigned int & index) const;
+
+
+       /**
+        * Return the local directory that corresponds to medias url,
+        * no matter if media isAttached or not. Files requested will
+        * be available at 'localRoot() + filename' or better
+        * 'localPath( filename )'.
+        *
+        * If media is not open an empty pathname is returned.
+        **/
+       Pathname localRoot() const;
+
+       /**
+        * Short for 'localRoot() + pathname', but returns an empty
+        * pathname if media is not open.
+        *
+        * Files provided will be available at 'localPath(filename)'.
+        **/
+       Pathname localPath( const Pathname & pathname ) const;
+
+        /**
+          Use concrete handler to disconnect the media.
+
+          This is useful for media which e.g. holds open a connection to a
+          server like FTP. After calling disconnect() the media object still is
+          valid and files are present.
+
+          After calling disconnect() it's not possible to call provideFile() or
+          provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        */
+        void disconnect();
+
+        /**
+         * Use concrete handler to release the media.
+         * @param ejectDev Device to eject. None if empty.
+         *
+         * \throws MediaException
+         *
+         **/
+        void release( const std::string & ejectDev = "" );
+
+       /**
+        * Use concrete handler to provide file denoted by path below
+        * 'attach point'. Filename is interpreted relative to the
+        * attached url and a path prefix is preserved.
+         *
+         * @param cached  If cached is set to true, the function checks, if
+         *                the file already exists and doesn't download it again
+         *                if it does. Currently only the existence is checked,
+         *                no other file attributes.
+        * @param checkonly If this and 'cached' are set to true only the
+        *                  existence of the file is checked but it's not
+        *                  downloaded. If 'cached' is unset an errer is
+        *                  returned always.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideFile( const Pathname & filename ) const;
+
+       /**
+        * Remove filename below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseFile( const Pathname & filename ) const;
+
+       /**
+        * Use concrete handler to provide directory denoted
+        * by path below 'attach point' (not recursive!).
+        * 'dirname' is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDir( const Pathname & dirname ) const;
+
+       /**
+        * Use concrete handler to provide directory tree denoted
+        * by path below 'attach point' (recursive!!).
+        * 'dirname' is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDirTree( const Pathname & dirname ) const;
+
+       /**
+        * Remove directory tree below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseDir( const Pathname & dirname ) const;
+
+       /**
+        * Remove pathname below attach point IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * If pathname denotes a directory it is recursively removed.
+        * If pathname is empty or '/' everything below the attachpoint
+        * is recursively removed.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releasePath( const Pathname & pathname ) const;
+
+       /**
+        * set a deltafile to be used in the next download
+        */
+       void setDeltafile( const Pathname & filename ) const;
+
+    public:
+
+       /**
+        * Return content of directory on media via retlist. If dots is false
+        * entries starting with '.' are not reported.
+        *
+        * The request is forwarded to the concrete handler,
+        * which may atempt to retieve the content e.g. via 'readdir'
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+        void dirInfo( std::list<std::string> & retlist,
+                        const Pathname & dirname, bool dots = true ) const;
+
+       /**
+        * Basically the same as dirInfo above. The content is returned as
+        * filesystem::DirContent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void dirInfo( filesystem::DirContent & retlist,
+                      const Pathname & dirname, bool dots = true ) const;
+
+        /**
+         * check if a file exists
+         *
+         * Asserted that url is a file and not a dir.
+         *
+         * \throws MediaException
+         *
+         **/
+        bool doesFileExist( const Pathname & filename ) const;
+
+       /**
+        * Destructor
+        **/
+       virtual ~MediaAccess();
+
+    public:
+
+       virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    public:
+        /**
+         * Get file from location at specified by URL and copy it to
+         * destination.
+         *
+         * @param from Source URL
+         * @param to   Destination file name
+        *
+        * \throws MediaException
+        *
+         **/
+        void getFile( const Url &from, const Pathname &to );
+
+    public:
+
+      /**
+       * Helper class that provides file on construction
+       * and cleans up on destruction.
+       *
+       * <b>Caution:</b> There's no synchronisation between multiple
+       * FileProvider instances, that provide the same file from the
+       * same media. If the first one goes out of scope, the file is
+       * cleaned. It's just a convenience for 'access and forgett'.
+       *
+       * <b>Caution:</b> We should either store the reference MediaAccess'
+       * MediaHandler here (for this MediaHandler must become a
+       * ref counting pointer class), or we need more info from MediaHandler
+       * (whether he's downloading to the local fs. If not, no releasefile
+       * is necessary).
+       * Currently we can not releaseFile after the media was closed
+       * (it's passed to the handler, which is deleted on close).
+       *
+       * \throws MediaBadFilenameException
+       * \throws MediaException
+       **/
+      class FileProvider {
+       FileProvider( const FileProvider & );             // no copy
+       FileProvider & operator=( const FileProvider & ); // no assign
+       private:
+         MediaAccess::constPtr _media;
+         Pathname              _file;
+         Pathname              _local_file;
+       public:
+         /**
+           * \throws MediaException
+           */
+         FileProvider( MediaAccess::constPtr media_r, const Pathname & file_r )
+           : _media( media_r )
+           , _file( file_r )
+           , _local_file( "" )
+         {
+           if ( _file.empty() ) {
+             ZYPP_THROW(MediaBadFilenameException(_file.asString()));
+           } else if ( _media ) {
+             try {
+               _media->provideFile( _file );
+               _local_file = _media->localPath( _file );
+             }
+             catch (const MediaException & excpt_r)
+              {
+               ZYPP_CAUGHT(excpt_r);
+               _media = NULL;
+               ZYPP_RETHROW(excpt_r);
+             }
+           }
+         }
+
+         ~FileProvider() {
+           if ( _media )
+           {
+             try {
+               _media->releaseFile( _file );
+             }
+             catch (const MediaException &excpt_r)
+             {
+               ZYPP_CAUGHT(excpt_r);
+             }
+              catch(...) {} // No exception from dtor!
+           }
+         }
+
+       public:
+
+         /**
+          * If no error, expect operator() to return the local
+          * Pathname of the provided file.
+          **/
+         Pathname localFile() const { return _local_file; }
+
+         /**
+          * Return the local Pathname of the provided file or
+          * an empty Pathname on error.
+          **/
+         Pathname operator()() const {
+           if ( _media )
+             return _media->localPath( _file );
+           return Pathname();
+         }
+      };
+    };
+
+    std::ostream & operator<<( std::ostream & str, const MediaAccess & obj );
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIAACCESS_H
+
diff --git a/zypp/media/MediaAria2c.cc b/zypp/media/MediaAria2c.cc
new file mode 100644 (file)
index 0000000..824b6de
--- /dev/null
@@ -0,0 +1,592 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAria2c.cc
+ *
+*/
+
+#include <iostream>
+#include <list>
+#include <vector>
+#include <fstream>
+#include <boost/lexical_cast.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Regex.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/ProgressData.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/ZYppCallbacks.h"
+
+#include "zypp/Edition.h"
+#include "zypp/Target.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZConfig.h"
+
+#include "zypp/TmpPath.h"
+
+#include "zypp/media/MediaAria2c.h"
+#include "zypp/media/proxyinfo/ProxyInfos.h"
+#include "zypp/media/ProxyInfo.h"
+#include "zypp/media/MediaUserAuth.h"
+#include "zypp/media/MediaCurl.h"
+#include "zypp/thread/Once.h"
+#include <cstdlib>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <boost/format.hpp>
+
+#define  DETECT_DIR_INDEX       0
+#define  CONNECT_TIMEOUT        60
+#define  TRANSFER_TIMEOUT       60 * 3
+#define  TRANSFER_TIMEOUT_MAX   60 * 60
+
+#define  ARIA_BINARY "aria2c"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp
+{
+namespace media
+{
+
+Pathname MediaAria2c::_cookieFile = "/var/lib/YaST2/cookies";
+std::string MediaAria2c::_aria2cVersion = "WE DON'T KNOW ARIA2C VERSION";
+
+//check if aria2c is present in the system
+bool
+MediaAria2c::existsAria2cmd()
+{
+  static const char* argv[] =
+  {
+    ARIA_BINARY,
+    "--version",
+    NULL
+  };
+  ExternalProgram aria( argv, ExternalProgram::Stderr_To_Stdout );
+  return( aria.close() == 0 );
+}
+
+/**
+ * comannd line for aria.
+ * The argument list gets passed as reference
+ * and it is filled.
+ */
+void fillAriaCmdLine( const string &ariaver,
+                      const TransferSettings &s,
+                      filesystem::TmpPath &credentials,
+                      const Url &url,
+                      const Pathname &destination,
+                      ExternalProgram::Arguments &args )
+{
+
+    // options that are not passed in the command line
+    // like credentials, every string is in the
+    // opt=val format
+    list<string> file_options;
+
+    args.push_back(ARIA_BINARY);
+    args.push_back(str::form("--user-agent=%s", s.userAgentString().c_str()));
+    args.push_back("--summary-interval=1");
+    args.push_back("--follow-metalink=mem");
+    args.push_back("--check-integrity=true");
+    args.push_back("--file-allocation=none");
+
+    // save the stats of the mirrors and use them as input later
+    Pathname statsFile = ZConfig::instance().repoCachePath() / "aria2.stats";
+    args.push_back(str::form("--server-stat-of=%s", statsFile.c_str()));
+    args.push_back(str::form("--server-stat-if=%s", statsFile.c_str()));
+    args.push_back("--uri-selector=adaptive");
+
+    // only present in recent aria lets find out the aria version
+    vector<string> fields;
+    // "aria2c version x.x"
+    str::split( ariaver, std::back_inserter(fields));
+    if ( fields.size() == 3 )
+    {
+        if ( Edition(fields[2]) >= Edition("1.1.2") )
+            args.push_back( "--use-head=false");
+    }
+
+    if ( s.maxDownloadSpeed() > 0 )
+        args.push_back(str::form("--max-download-limit=%ld", s.maxDownloadSpeed()));
+    if ( s.minDownloadSpeed() > 0 )
+        args.push_back(str::form("--lowest-speed-limit=%ld", s.minDownloadSpeed()));
+
+    args.push_back(str::form("--max-tries=%ld", s.maxSilentTries()));
+
+    if ( Edition(fields[2]) < Edition("1.2.0") )
+        WAR << "aria2c is older than 1.2.0, some features may be disabled" << endl;
+
+    // TODO make this one configurable
+    args.push_back(str::form("--max-concurrent-downloads=%ld", s.maxConcurrentConnections()));
+
+    // add the anonymous id.
+    for ( TransferSettings::Headers::const_iterator it = s.headersBegin();
+          it != s.headersEnd();
+          ++it )
+        args.push_back(str::form("--header=%s", it->c_str() ));
+
+    args.push_back( str::form("--connect-timeout=%ld", s.timeout()));
+
+    if ( s.username().empty() )
+    {
+        if ( url.getScheme() == "ftp" )
+        {
+            // set anonymous ftp
+            args.push_back(str::form("--ftp-user=%s", "suseuser" ));
+            args.push_back(str::form("--ftp-passwd=%s", VERSION ));
+
+            string id = "yast2";
+            id += VERSION;
+            DBG << "Anonymous FTP identification: '" << id << "'" << endl;
+        }
+    }
+    else
+    {
+        MIL << "Passing " << url.getScheme() << " credentials '" << s.username() << ':' << (s.password().empty() ? "" : "PASSWORD")<< "'" << endl;
+        if ( url.getScheme() == "ftp" )
+            file_options.push_back(str::form("ftp-user=%s", s.username().c_str() ));
+        else if ( url.getScheme() == "http" ||
+                  url.getScheme() == "https" )
+            file_options.push_back(str::form("http-user=%s", s.username().c_str() ));
+
+        if ( s.password().size() )
+        {
+            if ( url.getScheme() == "ftp" )
+                file_options.push_back(str::form("ftp-passwd=%s", s.password().c_str() ));
+            else if ( url.getScheme() == "http" ||
+                      url.getScheme() == "https" )
+                file_options.push_back(str::form("http-passwd=%s", s.password().c_str() ));
+        }
+    }
+
+    if ( s.proxyEnabled() )
+    {
+        args.push_back(str::form("--http-proxy=%s", s.proxy().c_str() ));
+        if ( ! s.proxyUsername().empty() )
+        {
+            MIL << "Passing " << /*url.getScheme()*/"http" << "-proxy credentials '" << s.proxyUsername() << ':' << (s.proxyPassword().empty() ? "" : "PASSWORD")<< "'" << endl;
+            file_options.push_back(str::form("http-proxy-user=%s", s.proxyUsername().c_str() ));
+            if ( ! s.proxyPassword().empty() )
+                file_options.push_back(str::form("http-proxy-passwd=%s", s.proxyPassword().c_str() ));
+        }
+    }
+
+    if ( ! destination.empty() )
+        args.push_back(str::form("--dir=%s", destination.c_str()));
+
+    // now append the file if there are hidden options
+    if ( ! file_options.empty() )
+    {
+        filesystem::TmpFile tmp;
+        ofstream outs( tmp.path().c_str() );
+        for_( it, file_options.begin(), file_options.end() )
+            outs << *it << endl;
+        outs.close();
+
+        credentials = tmp;
+        args.push_back(str::form("--conf-path=%s", credentials.path().c_str()));
+    }
+
+    // Credentials are passed via --{ftp,http}-{user,passwd}.
+    // Aria does not like them being repeated in the url. (bnc #544634)
+    args.push_back(url.asString( url.getViewOptions()
+                               - url::ViewOptions::WITH_USERNAME
+                               - url::ViewOptions::WITH_PASSWORD ).c_str());
+}
+
+const char *const MediaAria2c::agentString()
+{
+  // we need to add the release and identifier to the
+  // agent string.
+  // The target could be not initialized, and then this information
+  // is not available.
+  Target_Ptr target = zypp::getZYpp()->getTarget();
+
+  static const std::string _value(
+    str::form(
+       "ZYpp %s (%s) %s"
+       , VERSION
+       , MediaAria2c::_aria2cVersion.c_str()
+       , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
+    )
+  );
+  return _value.c_str();
+}
+
+
+
+MediaAria2c::MediaAria2c( const Url &      url_r,
+                      const Pathname & attach_point_hint_r )
+    : MediaCurl( url_r, attach_point_hint_r )
+{
+  MIL << "MediaAria2c::MediaAria2c(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+  //Get aria2c version
+  _aria2cVersion = getAria2cVersion();
+}
+
+void MediaAria2c::attachTo (bool next)
+{
+  MediaCurl::attachTo(next);
+  _settings.setUserAgentString(agentString());
+}
+
+bool
+MediaAria2c::checkAttachPoint(const Pathname &apoint) const
+{
+    return MediaCurl::checkAttachPoint( apoint );
+}
+
+void MediaAria2c::disconnectFrom()
+{
+    MediaCurl::disconnectFrom();
+}
+
+void MediaAria2c::releaseFrom( const std::string & ejectDev )
+{
+  MediaCurl::releaseFrom(ejectDev);
+}
+
+void MediaAria2c::getFile( const Pathname & filename ) const
+{
+    // Use absolute file name to prevent access of files outside of the
+    // hierarchy below the attach point.
+    getFileCopy(filename, localPath(filename).absolutename());
+}
+
+void MediaAria2c::getFileCopy( const Pathname & filename , const Pathname & target) const
+{
+  callback::SendReport<DownloadProgressReport> report;
+
+  Url fileurl(getFileUrl(filename));
+
+  bool retry = false;
+
+  ExternalProgram::Arguments args;
+
+  filesystem::TmpPath credentials;
+  fillAriaCmdLine(_aria2cVersion, _settings, credentials, fileurl, target.dirname(), args);
+
+  do
+  {
+    try
+    {
+      report->start(fileurl, target.asString() );
+
+      ExternalProgram aria(args, ExternalProgram::Stderr_To_Stdout);
+
+      // extended regex for parsing of progress lines
+      // progress line like: [#1 SIZE:8.3MiB/10.1MiB(82%) CN:5 SPD:6899.88KiB/s]
+      // but since 1.4.0:    [#1 SIZE:8.3MiB/10.1MiB(82%) CN:5 SPD:899.8KiBs]
+      //       (bnc #513944) [#1 SIZE:8.3MiB/10.1MiB(82%) CN:5 SPD:3.8MiBs]
+      //                     [#1 SIZE:8.3MiB/10.1MiB(82%) CN:5 SPD:38Bs]
+      // later got also ETA: [#1 SIZE:8.3MiB/10.1MiB(82%) CN:5 SPD:38Bs ETA:02s]
+      static str::regex rxProgress(
+          "^\\[#[0-9]+ SIZE:[0-9\\.]+(|Ki|Mi|Ti)B/[0-9\\.]+(|Ki|Mi|Ti)B\\(?([0-9]+)?%?\\)? CN:[0-9]+ SPD:([0-9\\.]+)(|Ki|Mi|Ti)Bs.*\\]$");
+
+      // whether we received correct progress line before corresponding FILE line
+      bool gotProgress = false;
+      // download progress in %
+      int progress = 0;
+      // current download speed in bytes
+      double current_speed = 0;
+      // download speed in bytes
+      double average_speed = 0;
+      // number of speed measurements
+      long average_speed_count = 0;
+
+      // here we capture aria output exceptions
+      vector<string> ariaExceptions;
+
+      // whether it makes sense to retry with --continue
+      bool partialDownload = false;
+      // whether user request abort of the download
+      bool userAbort = false;
+
+      //Process response
+      for(std::string ariaResponse( aria.receiveLine());
+          ariaResponse.length();
+          ariaResponse = aria.receiveLine())
+      {
+        string line = str::trim(ariaResponse);
+        // INT << line << endl;
+
+        // look for the progress line and save parsed values until we find
+        // a string with FILE: later.
+        if ( str::hasPrefix(line, "[#") )
+        {
+          str::smatch progressValues;
+          if (( gotProgress = str::regex_match(line, progressValues, rxProgress) ))
+          {
+            // INT << "got: progress: '" << progressValues[3]
+            //     << "' speed: '" << progressValues[4] << " "
+            //     << progressValues[5] << "Bs'" << endl;
+
+            // get the percentage (progress) data
+            progress = std::atoi(progressValues[3].c_str());
+
+            // get the speed
+
+            int factor = 1; // B/KiB/MiB multiplication factor
+            if (progressValues[5] == "Ki")
+              factor = 1024;
+            else if (progressValues[5] == "Mi")
+              factor = 0x100000;
+            else if (progressValues[5] == "Ti")
+              factor = 0x40000000;
+
+            try {
+              current_speed = boost::lexical_cast<double>(progressValues[4]);
+              // convert to and work with bytes everywhere (bnc #537870)
+              current_speed *= factor;
+            }
+            catch (const std::exception&) {
+              ERR << "Can't parse speed from '" << progressValues[4] << "'" << endl;
+              current_speed = 0;
+            }
+          }
+          else
+            ERR << "Can't parse progress line '" << line << "'" << endl;
+        }
+        // save error messages for later
+        else if ( str::hasPrefix(line, "Exception: ") )
+        {
+          // for auth exception, we throw
+          if (!line.substr(0,31).compare("Exception: Authorization failed") )
+          {
+            ZYPP_THROW(MediaUnauthorizedException(
+                       _url, "Login failed.", "Login failed", "auth hint"
+            ));
+          }
+          // otherwise, remember the error
+          string excpMsg = line.substr(10, line.size());
+          DBG << "aria2c reported: '" << excpMsg << "'" << endl;
+          ariaExceptions.push_back(excpMsg);
+        }
+        // The file line tells which file corresponds to the previous progress,
+        // eg.: FILE: ./packages.FL.gz
+        else if ( str::hasPrefix(line, "FILE: ") )
+        {
+          // get the FILE name
+          string theFile(line.substr(6, line.size()));
+          // is the report about the filename we are downloading?
+          // aria may report progress about metalinks, torrent and
+          // other stuff which is not the main transfer
+          // the reported file is the url before the server emits a response
+          // and then is reported as the target file
+          if ( Pathname(theFile) == target || theFile == fileurl.asCompleteString() )
+          {
+            // once we find the FILE: line, progress has to be
+            // non empty
+            if ( gotProgress )
+            {
+              // we have a new average speed
+              average_speed_count++;
+
+              // this is basically A: average
+              // ((n-1)A(n-1) + Xn)/n = A(n)
+              average_speed =
+                (((average_speed_count - 1)*average_speed) + current_speed)
+                / average_speed_count;
+
+              if (!partialDownload && progress > 0)
+                partialDownload = true;
+
+              if ( ! report->progress ( progress, fileurl, average_speed, current_speed ) )
+                userAbort = true;
+
+              // clear the flag to detect mismatches between [# and FILE: lines
+              gotProgress = false;
+            }
+            else
+            {
+              WAR << "aria2c reported a file, but no progress data available" << endl;
+            }
+          }
+          else
+          {
+            DBG << "Progress is not about '" << target << "' but '" << theFile << "'" << endl;
+          }
+        }
+        else
+        {
+            // other line type, just ignore it.
+        }
+      }
+
+      int code;
+      if (userAbort)
+      {
+        aria.kill();
+        code = 7;
+      }
+      else
+        code = aria.close();
+
+      switch ( code )
+      {
+        case 0: // success?
+          if ( ! PathInfo( target ).isExist() )
+          {
+            // bnc #564816: aria2 might return 0 if an error occurred
+            // _before_ the download actually started.
+
+            // TranslatorExplanation: Failed to download <FILENAME> from <SERVERURL>.
+            std::string msg( str::form(_("Failed to download %s from %s"),
+                             filename.c_str(), _url.asString().c_str() ) );
+
+            MediaException e( msg );
+            for_( it, ariaExceptions.begin(), ariaExceptions.end() )
+              e.addHistory( *it );
+
+            ZYPP_THROW( e );
+          }
+          break;
+
+        case 2: // timeout
+        {
+          MediaTimeoutException e(_url);
+          for_(it, ariaExceptions.begin(), ariaExceptions.end())
+              e.addHistory(*it);
+          ZYPP_THROW(e);
+        }
+        break;
+
+        case 3: // not found
+        case 4: // max notfound reached
+        {
+          MediaFileNotFoundException e(_url, filename);
+          for_(it, ariaExceptions.begin(), ariaExceptions.end())
+              e.addHistory(*it);
+          ZYPP_THROW(e);
+        }
+        break;
+
+        case 5: // too slow
+        case 6: // network problem
+        case 7: // unfinished downloads (ctr-c)
+        case 1: // unknown
+        default:
+        {
+          if ( partialDownload )
+          {
+            // Ask for retry on partial downloads, when it makes sense to retry with --continue!
+            // Other errors are handled by the layers above.
+            MediaException e(str::form(_("Download interrupted at %d%%"), progress ));
+            for_(it, ariaExceptions.begin(), ariaExceptions.end())
+              e.addHistory(*it);
+
+            DownloadProgressReport::Action action = report->problem( _url, DownloadProgressReport::ERROR, e.asUserHistory() );
+            if ( action == DownloadProgressReport::RETRY )
+            {
+              retry = true;
+              continue;
+            }
+          }
+
+          string msg;
+          if (userAbort)
+            msg = _("Download interrupted by user");
+          else
+            // TranslatorExplanation: Failed to download <FILENAME> from <SERVERURL>.
+            msg = str::form(_("Failed to download %s from %s"),
+                filename.c_str(), _url.asString().c_str());
+
+          MediaException e(msg);
+          for_(it, ariaExceptions.begin(), ariaExceptions.end())
+              e.addHistory(*it);
+
+          ZYPP_THROW(e);
+        }
+        break;
+      }
+
+      retry = false;
+    }
+    // retry with proper authentication data
+    catch (MediaUnauthorizedException & ex_r)
+    {
+      if(authenticate(ex_r.hint(), !retry))
+        retry = true;
+      else
+      {
+        report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
+        ZYPP_RETHROW(ex_r);
+      }
+
+    }
+    // unexpected exception
+    catch (MediaException & excpt_r)
+    {
+      // FIXME: error number fix
+      report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserHistory());
+      ZYPP_RETHROW(excpt_r);
+    }
+  }
+  while (retry);
+
+  report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
+}
+
+bool MediaAria2c::getDoesFileExist( const Pathname & filename ) const
+{
+    return MediaCurl::getDoesFileExist(filename);
+}
+
+bool MediaAria2c::doGetDoesFileExist( const Pathname & filename ) const
+{
+    return MediaCurl::doGetDoesFileExist(filename);
+}
+
+void MediaAria2c::getDir( const Pathname & dirname, bool recurse_r ) const
+{
+    MediaCurl::getDir(dirname, recurse_r);
+}
+
+bool MediaAria2c::authenticate(const std::string & availAuthTypes, bool firstTry) const
+{
+    return false;
+}
+
+void MediaAria2c::getDirInfo( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots ) const
+{
+  getDirectoryYast( retlist, dirname, dots );
+}
+
+void MediaAria2c::getDirInfo( filesystem::DirContent & retlist,
+                            const Pathname & dirname, bool dots ) const
+{
+  getDirectoryYast( retlist, dirname, dots );
+}
+
+std::string MediaAria2c::getAria2cVersion()
+{
+    static const char* argv[] =
+    {
+      ARIA_BINARY,
+      "--version",
+      NULL
+    };
+    ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
+    std::string vResponse( str::trim( aria.receiveLine() ) );
+    aria.close();
+    return vResponse;
+}
+} // namespace media
+} // namespace zypp
+//
diff --git a/zypp/media/MediaAria2c.h b/zypp/media/MediaAria2c.h
new file mode 100644 (file)
index 0000000..1169edf
--- /dev/null
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaAria2c.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAARIA2C_H
+#define ZYPP_MEDIA_MEDIAARIA2C_H
+
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/MediaCurl.h"
+#include "zypp/media/TransferSettings.h"
+#include "zypp/ZYppCallbacks.h"
+
+namespace zypp {
+  namespace media {
+
+/**
+ * @short Implementation class for FTP, HTTP and HTTPS MediaHandler using an external program (aria2c) to retrive files
+ *
+ * @author gfarrasb (gfarrasb@gmail.com)
+ * @author Duncan Mac-Vicar <dmacvicar@suse.de>
+ *
+ * @see MediaHandler
+ **/
+class MediaAria2c : public MediaCurl {
+
+  public:
+   /**
+    * check if aria2c command line is present in the system
+    **/
+    static bool existsAria2cmd();
+
+  protected:
+    virtual void attachTo (bool next = false);
+    virtual void releaseFrom( const std::string & ejectDev );
+    virtual void getFile( const Pathname & filename ) const;
+    virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+    virtual void getDirInfo( std::list<std::string> & retlist,
+                             const Pathname & dirname, bool dots = true ) const;
+    virtual void getDirInfo( filesystem::DirContent & retlist,
+                             const Pathname & dirname, bool dots = true ) const;
+    /**
+     * Repeatedly calls doGetDoesFileExist() until it successfully returns,
+     * fails unexpectedly, or user cancels the operation. This is used to
+     * handle authentication or similar retry scenarios on media level.
+     */
+    virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+    /**
+     * \see MediaHandler::getDoesFileExist
+     */
+    virtual bool doGetDoesFileExist( const Pathname & filename ) const;
+
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void disconnectFrom();
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void getFileCopy( const Pathname & srcFilename, const Pathname & targetFilename) const;
+
+    virtual bool checkAttachPoint(const Pathname &apoint) const;
+
+  public:
+
+    MediaAria2c( const Url &      url_r,
+              const Pathname & attach_point_hint_r );
+
+    virtual ~MediaAria2c() { try { release(); } catch(...) {} }
+
+    //static void setCookieFile( const Pathname & );
+
+    class Callbacks
+    {
+      public:
+       virtual ~Callbacks() {}
+        virtual bool progress( int percent ) = 0;
+    };
+
+  protected:
+
+    static const char *const agentString();
+
+  private:
+
+    bool authenticate(const std::string & availAuthTypes, bool firstTry) const;
+
+    std::string _currentCookieFile;
+    std::string _ca_path;
+    static Pathname _cookieFile;
+
+    /** External process to get aria2c version */
+    std::string getAria2cVersion();
+    static std::string _aria2cVersion;
+};
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIAARIA2C_H
diff --git a/zypp/media/MediaBlockList.cc b/zypp/media/MediaBlockList.cc
new file mode 100644 (file)
index 0000000..a0161eb
--- /dev/null
@@ -0,0 +1,533 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaBlockList.cc
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include <vector>
+#include <iostream>
+#include <fstream>
+
+#include "zypp/media/MediaBlockList.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+MediaBlockList::MediaBlockList(off_t size)
+{
+  filesize = size;
+  haveblocks = false;
+  chksumlen = 0;
+  chksumpad = 0;
+  rsumlen = 0;
+  rsumpad = 0;
+}
+
+size_t
+MediaBlockList::addBlock(off_t off, size_t size)
+{
+  haveblocks = true;
+  blocks.push_back(MediaBlock());
+  blocks.back().off = off;
+  blocks.back().size = size;
+  return blocks.size() - 1;
+}
+
+void
+MediaBlockList::setFileChecksum(std::string ctype, int cl, unsigned char *c)
+{
+  if (!cl)
+    return;
+  fsumtype = ctype;
+  fsum.resize(cl);
+  memcpy(&fsum[0], c, cl);
+}
+
+bool
+MediaBlockList::createFileDigest(Digest &digest) const
+{
+  return digest.create(fsumtype);
+}
+
+bool
+MediaBlockList::verifyFileDigest(Digest &digest) const
+{
+  if (!haveFileChecksum())
+    return true;
+  vector<unsigned char>dig = digest.digestVector();
+  if (dig.empty() || dig.size() < fsum.size())
+    return false;
+  return memcmp(&dig[0], &fsum[0], fsum.size()) ? false : true;
+}
+
+void
+MediaBlockList::setChecksum(size_t blkno, std::string cstype, int csl, unsigned char *cs, size_t cspad)
+{
+  if (!csl)
+    return;
+  if (!chksumlen)
+    {
+      if (blkno)
+       return;
+      chksumlen = csl;
+      chksumtype = cstype;
+      chksumpad = cspad;
+    }
+  if (csl != chksumlen || cstype != chksumtype || cspad != chksumpad || blkno != chksums.size() / chksumlen)
+    return;
+  chksums.resize(chksums.size() + csl);
+  memcpy(&chksums[csl * blkno], cs, csl);
+}
+
+void
+MediaBlockList::setRsum(size_t blkno, int rsl, unsigned int rs, size_t rspad)
+{
+  if (!rsl)
+    return;
+  if (!rsumlen)
+    {
+      if (blkno)
+       return;
+      rsumlen = rsl;
+      rsumpad = rspad;
+    }
+  if (rsl != rsumlen || rspad != rsumpad || blkno != rsums.size())
+    return;
+  rsums.push_back(rs);
+}
+
+bool
+MediaBlockList::createDigest(Digest &digest) const
+{
+  return digest.create(chksumtype);
+}
+
+bool
+MediaBlockList::verifyDigest(size_t blkno, Digest &digest) const
+{
+  if (!haveChecksum(blkno))
+    return true;
+  size_t size = blocks[blkno].size;
+  if (!size)
+    return true;
+  if (chksumpad > size)
+    {
+      char pad[chksumpad - size];
+      memset(pad, 0, chksumpad - size);
+      digest.update(pad, chksumpad - size);
+    }
+  vector<unsigned char>dig = digest.digestVector();
+  if (dig.empty() || dig.size() < size_t(chksumlen))
+    return false;
+  return memcmp(&dig[0], &chksums[chksumlen * blkno], chksumlen) ? false : true;
+}
+
+unsigned int
+MediaBlockList::updateRsum(unsigned int rs, const char* bytes, size_t len) const
+{
+  if (!len)
+    return rs;
+  unsigned short s, m;
+  s = (rs >> 16) & 65535;
+  m = rs & 65535;
+  for (; len > 0 ; len--)
+    {
+      unsigned short c = (unsigned char)*bytes++;
+      s += c;
+      m += s;
+    }
+  return (s & 65535) << 16 | (m & 65535);
+}
+
+bool
+MediaBlockList::verifyRsum(size_t blkno, unsigned int rs) const
+{
+  if (!haveRsum(blkno))
+    return true;
+  size_t size = blocks[blkno].size;
+  if (!size)
+    return true;
+  if (rsumpad > size)
+    {
+      unsigned short s, m;
+      s = (rs >> 16) & 65535;
+      m = rs & 65535;
+      m += s * (rsumpad - size);
+      rs = (s & 65535) << 16 | (m & 65535);
+    }
+  switch(rsumlen)
+    {
+    case 3:
+      rs &= 0xffffff;
+    case 2:
+      rs &= 0xffff;
+    case 1:
+      rs &= 0xff;
+    default:
+      break;
+    }
+  return rs == rsums[blkno];
+}
+
+bool
+MediaBlockList::checkRsum(size_t blkno, const unsigned char *buf, size_t bufl) const
+{
+  if (blkno >= blocks.size() || bufl < blocks[blkno].size)
+    return false;
+  unsigned int rs = updateRsum(0, (const char *)buf, blocks[blkno].size);
+  return verifyRsum(blkno, rs);
+}
+
+bool
+MediaBlockList::checkChecksum(size_t blkno, const unsigned char *buf, size_t bufl) const
+{
+  if (blkno >= blocks.size() || bufl < blocks[blkno].size)
+    return false;
+  Digest dig;
+  if (!createDigest(dig))
+    return false;
+  dig.update((const char *)buf, blocks[blkno].size);
+  return verifyDigest(blkno, dig);
+}
+
+// specialized version of checkChecksum that can deal with a "rotated" buffer
+bool
+MediaBlockList::checkChecksumRotated(size_t blkno, const unsigned char *buf, size_t bufl, size_t start) const
+{
+  if (blkno >= blocks.size() || bufl < blocks[blkno].size)
+    return false;
+  if (start == bufl)
+    start = 0;
+  Digest dig;
+  if (!createDigest(dig))
+    return false;
+  size_t size = blocks[blkno].size;
+  size_t len = bufl - start > size ? size : bufl - start;
+  dig.update((const char *)buf + start, len);
+  if (size > len)
+    dig.update((const char *)buf, size - len);
+  return verifyDigest(blkno, dig);
+}
+
+// write block to the file. can also deal with "rotated" buffers
+void
+MediaBlockList::writeBlock(size_t blkno, FILE *fp, const unsigned char *buf, size_t bufl, size_t start, vector<bool> &found) const
+{
+  if (blkno >= blocks.size() || bufl < blocks[blkno].size)
+    return;
+  off_t off = blocks[blkno].off;
+  size_t size = blocks[blkno].size;
+  if (fseeko(fp, off, SEEK_SET))
+    return;
+  if (start == bufl)
+    start = 0;
+  size_t len = bufl - start > size ? size : bufl - start;
+  if (fwrite(buf + start, len, 1, fp) != 1)
+    return;
+  if (size > len && fwrite(buf, size - len, 1, fp) != 1)
+    return;
+  found[blkno] = true;
+  found[blocks.size()] = true;
+}
+
+static size_t
+fetchnext(FILE *fp, unsigned char *bp, size_t blksize, size_t pushback, unsigned char *pushbackp)
+{
+  size_t l = blksize;
+  int c;
+
+  if (pushback)
+    {
+      if (pushbackp != bp)
+        memmove(bp, pushbackp, pushback);
+      bp += pushback;
+      l -= pushback;
+    }
+  while (l)
+    {
+      c = getc(fp);
+      if (c == EOF)
+        break;
+      *bp++ = c;
+      l--;
+    }
+  if (l)
+    memset(bp, 0, l);
+  return blksize - l;
+}
+
+
+void
+MediaBlockList::reuseBlocks(FILE *wfp, string filename)
+{
+  FILE *fp;
+
+  if (!chksumlen || (fp = fopen(filename.c_str(), "r")) == 0)
+    return;
+  size_t nblks = blocks.size();
+  vector<bool> found;
+  found.resize(nblks + 1);
+  if (rsumlen && !rsums.empty())
+    {
+      size_t blksize = blocks[0].size;
+      if (nblks == 1 && rsumpad && rsumpad > blksize)
+       blksize = rsumpad;
+      // create hash of checksums
+      unsigned int hm = rsums.size() * 2;
+      while (hm & (hm - 1))
+       hm &= hm - 1;
+      hm = hm * 2 - 1;
+      if (hm < 16383)
+       hm = 16383;
+      unsigned int *ht = new unsigned int[hm + 1];
+      memset(ht, 0, (hm + 1) * sizeof(unsigned int));
+      for (unsigned int i = 0; i < rsums.size(); i++)
+       {
+         if (blocks[i].size != blksize && (i != nblks - 1 || rsumpad != blksize))
+           continue;
+         unsigned int r = rsums[i];
+         unsigned int h = r & hm;
+         unsigned int hh = 7;
+         while (ht[h])
+           h = (h + hh++) & hm;
+         ht[h] = i + 1;
+       }
+
+      unsigned char *buf = new unsigned char[blksize];
+      unsigned char *buf2 = new unsigned char[blksize];
+      size_t pushback = 0;
+      unsigned char *pushbackp = 0;
+      int bshift = 0;
+      if ((blksize & (blksize - 1)) == 0)
+       for (bshift = 0; size_t(1 << bshift) != blksize; bshift++)
+         ;
+      unsigned short a, b;
+      a = b = 0;
+      memset(buf, 0, blksize);
+      bool eof = 0;
+      bool init = 1;
+      int sql = nblks > 1 && chksumlen < 16 ? 2 : 1;
+      while (!eof)
+       {
+         for (size_t i = 0; i < blksize; i++)
+           {
+             int c;
+             if (eof)
+               c = 0;
+             else
+               {
+                  if (pushback)
+                   {
+                     c = *pushbackp++;
+                     pushback--;
+                   }
+                 else
+                   c = getc(fp);
+                 if (c == EOF)
+                   {
+                     eof = true;
+                     c = 0;
+                     if (!i || sql == 2)
+                       break;
+                   }
+               }
+             int oc = buf[i];
+             buf[i] = c;
+             a += c - oc; 
+             if (bshift)
+               b += a - (oc << bshift);
+             else
+               b += a - oc * blksize;
+             if (init)
+               {
+                 if (size_t(i) != blksize - 1)
+                   continue;
+                 init = 0;
+               }
+             unsigned int r;
+             if (rsumlen == 1)
+               r = ((unsigned int)b & 255);
+             else if (rsumlen == 2)
+               r = ((unsigned int)b & 65535);
+             else if (rsumlen == 3)
+               r = ((unsigned int)a & 255) << 16 | ((unsigned int)b & 65535);
+             else
+               r = ((unsigned int)a & 65535) << 16 | ((unsigned int)b & 65535);
+             unsigned int h = r & hm; 
+             unsigned int hh = 7;
+             for (; ht[h]; h = (h + hh++) & hm)
+               {
+                 size_t blkno = ht[h] - 1;
+                 if (rsums[blkno] != r)
+                   continue;
+                 if (found[blkno])
+                   continue;
+                 if (sql == 2)
+                   {
+                     if (eof || blkno + 1 >= nblks)
+                       continue;
+                     pushback = fetchnext(fp, buf2, blksize, pushback, pushbackp);
+                     pushbackp = buf2;
+                     if (!pushback)
+                       continue;
+                     if (!checkRsum(blkno + 1, buf2, blksize))
+                       continue;
+                   }
+                 if (!checkChecksumRotated(blkno, buf, blksize, i + 1))
+                   continue;
+                 if (sql == 2 && !checkChecksum(blkno + 1, buf2, blksize))
+                   continue;
+                 writeBlock(blkno, wfp, buf, blksize, i + 1, found);
+                 if (sql == 2)
+                   {
+                     writeBlock(blkno + 1, wfp, buf2, blksize, 0, found);
+                     pushback = 0;
+                     blkno++;
+                   }
+                 while (!eof)
+                   {
+                     blkno++;
+                     pushback = fetchnext(fp, buf2, blksize, pushback, pushbackp);
+                     pushbackp = buf2;
+                     if (!pushback)
+                       break;
+                     if (!checkRsum(blkno, buf2, blksize))
+                       break;
+                     if (!checkChecksum(blkno, buf2, blksize))
+                       break;
+                     writeBlock(blkno, wfp, buf2, blksize, 0, found);
+                     pushback = 0;
+                   }
+                 init = false;
+                 memset(buf, 0, blksize);
+                 a = b = 0;
+                 i = size_t(-1);       // start with 0 on next iteration
+                 break;
+               }
+           }
+       }
+      delete[] buf2;
+      delete[] buf;
+      delete[] ht;
+    }
+  else if (chksumlen >= 16)
+    {
+      // dummy variant, just check the checksums
+      size_t bufl = 4096;
+      off_t off = 0;
+      unsigned char *buf = new unsigned char[bufl];
+      for (size_t blkno = 0; blkno < blocks.size(); ++blkno)
+       {
+         if (off > blocks[blkno].off)
+           continue;
+         size_t blksize = blocks[blkno].size;
+         if (blksize > bufl)
+           {
+             delete[] buf;
+             bufl = blksize;
+              buf = new unsigned char[bufl];
+           }
+         size_t skip = blocks[blkno].off - off;
+         while (skip)
+           {
+             size_t l = skip > bufl ? bufl : skip;
+             if (fread(buf, l, 1, fp) != 1)
+               break;
+             skip -= l;
+             off += l;
+           }
+         if (fread(buf, blksize, 1, fp) != 1)
+           break;
+         if (checkChecksum(blkno, buf, blksize))
+           writeBlock(blkno, wfp, buf, blksize, 0, found);
+         off += blksize;
+       }
+    }
+  if (!found[nblks])
+    return;
+  // now throw out all of the blocks we found
+  std::vector<MediaBlock> nblocks;
+  std::vector<unsigned char> nchksums;
+  std::vector<unsigned int> nrsums;
+  
+  for (size_t blkno = 0; blkno < blocks.size(); ++blkno)
+    {
+      if (!found[blkno])
+       {
+         // still need it
+         nblocks.push_back(blocks[blkno]);
+         if (chksumlen && (blkno + 1) * chksumlen <= chksums.size())
+           {
+             nchksums.resize(nblocks.size() * chksumlen);
+             memcpy(&nchksums[(nblocks.size() - 1) * chksumlen], &chksums[blkno * chksumlen], chksumlen);
+           }
+         if (rsumlen && (blkno + 1) <= rsums.size())
+           nrsums.push_back(rsums[blkno]);
+       }
+    }
+  blocks = nblocks;
+  chksums = nchksums;
+  rsums = nrsums;
+}
+
+std::string
+MediaBlockList::asString() const
+{
+  std::string s;
+  size_t i, j;
+
+  if (filesize != off_t(-1))
+    {
+      long long size = filesize;
+      s = zypp::str::form("[ BlockList, file size %lld\n", size);
+    }
+  else
+    s = "[ BlockList, filesize unknown\n";
+  if (!haveblocks)
+    s += "  No block information\n";
+  if (chksumpad)
+    s += zypp::str::form("  Checksum pad %zd\n", chksumpad);
+  if (rsumpad)
+    s += zypp::str::form("  Rsum pad %zd\n", rsumpad);
+  for (i = 0; i < blocks.size(); ++i)
+    {
+      long long off=blocks[i].off;
+      long long size=blocks[i].size;
+      s += zypp::str::form("  (%8lld, %8lld)", off, size);
+      if (chksumlen && chksums.size() >= (i + 1) * chksumlen)
+       {
+         s += "  " + chksumtype + ":";
+         for (j = 0; j < size_t(chksumlen); j++)
+           s += zypp::str::form("%02hhx", chksums[i * chksumlen + j]);
+       }
+      if (rsumlen && rsums.size() > i)
+       {
+         s += "  RSUM:";
+         s += zypp::str::form("%0*x", 2 * rsumlen, rsums[i]);
+       }
+      s += "\n";
+    }
+  s += "]";
+  return s;
+}
+
+  } // namespace media
+} // namespace zypp
+
diff --git a/zypp/media/MediaBlockList.h b/zypp/media/MediaBlockList.h
new file mode 100644 (file)
index 0000000..b3b3c94
--- /dev/null
@@ -0,0 +1,147 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaBlockList.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIABLOCKLIST_H
+#define ZYPP_MEDIA_MEDIABLOCKLIST_H
+
+#include <sys/types.h>
+#include <vector>
+
+#include "zypp/Digest.h"
+
+namespace zypp {
+  namespace media {
+
+/**
+ * a single block from the blocklist, consisting of an offset and a size
+ **/
+struct MediaBlock {
+  off_t off;
+  size_t size;
+};
+
+class MediaBlockList {
+public:
+  MediaBlockList(off_t filesize=off_t(-1));
+
+  /**
+   * do we have a blocklist describing the file?
+   * set to true when addBlock() is called
+   **/
+  inline bool haveBlocks() const {
+    return haveblocks;
+  }
+  /**
+   * add a block with offset off and size size to the block list. Note
+   * that blocks must be ordered and must not overlap. returns the
+   * block number.
+   **/
+  size_t addBlock(off_t off, size_t size);
+
+  /**
+   * return the offset/size of a block with number blkno
+   **/
+  inline MediaBlock getBlock(size_t blkno) const {
+    return blocks[blkno];
+  }
+  /**
+   * return the number of blocks in the blocklist
+   **/
+  inline size_t numBlocks() const {
+    return blocks.size();
+  }
+
+  /**
+   * set / return the size of the whole file
+   **/
+  inline void setFilesize(off_t newfilesize=off_t(-1)) {
+    filesize = newfilesize;
+  }
+  inline off_t getFilesize() const {
+    return filesize;
+  }
+  inline bool haveFilesize() const {
+    return filesize != off_t(-1);
+  }
+
+  /**
+   * set / verify the checksum over the whole file
+   **/
+  void setFileChecksum(std::string ctype, int cl, unsigned char *c);
+  bool createFileDigest(Digest &digest) const;
+  bool verifyFileDigest(Digest &digest) const;
+  inline bool haveFileChecksum() const {
+    return !fsumtype.empty() && fsum.size();
+  }
+
+  /**
+   * set / verify the (strong) checksum over a single block
+   **/
+  void setChecksum(size_t blkno, std::string cstype, int csl, unsigned char *cs, size_t cspad=0);
+  bool checkChecksum(size_t blkno, const unsigned char *buf, size_t bufl) const;
+  bool createDigest(Digest &digest) const;
+  bool verifyDigest(size_t blkno, Digest &digest) const;
+  inline bool haveChecksum(size_t blkno) const {
+    return chksumlen && chksums.size() >= chksumlen * (blkno + 1);
+  }
+
+  /**
+   * set / verify the (weak) rolling checksum over a single block
+   **/
+  void setRsum(size_t blkno, int rsl, unsigned int rs, size_t rspad=0);
+  bool checkRsum(size_t blkno, const unsigned char *buf, size_t bufl) const;
+  unsigned int updateRsum(unsigned int rs, const char *bytes, size_t len) const;
+  bool verifyRsum(size_t blkno, unsigned int rs) const;
+  inline bool haveRsum(size_t blkno) const {
+    return rsumlen && rsums.size() >= blkno + 1;
+  }
+  
+  /**
+   * scan a file for blocks from our blocklist. if we find a suitable block,
+   * it is removed from the list
+   **/
+  void reuseBlocks(FILE *wfp, std::string filename);
+
+  /**
+   * return block list as string
+   **/
+  std::string asString() const;
+
+private:
+  void writeBlock(size_t blkno, FILE *fp, const unsigned char *buf, size_t bufl, size_t start, std::vector<bool> &found) const;
+  bool checkChecksumRotated(size_t blkno, const unsigned char *buf, size_t bufl, size_t start) const;
+
+  off_t filesize;
+  std::string fsumtype;
+  std::vector<unsigned char> fsum;
+
+  bool haveblocks;
+  std::vector<MediaBlock> blocks;
+
+  std::string chksumtype;
+  int chksumlen;
+  size_t chksumpad;
+  std::vector<unsigned char> chksums;
+
+  std::string rsumtype;
+  int rsumlen;
+  size_t rsumpad;
+  std::vector<unsigned int> rsums;
+};
+
+inline std::ostream & operator<<(std::ostream &str, const MediaBlockList &bl)
+{ return str << bl.asString(); }
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIABLOCKLIST_H
+
diff --git a/zypp/media/MediaCD.cc b/zypp/media/MediaCD.cc
new file mode 100644 (file)
index 0000000..9eb49f7
--- /dev/null
@@ -0,0 +1,866 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCD.cc
+ *
+*/
+extern "C"
+{
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+#if HAVE_UDEV
+#include <libudev.h>
+#endif
+}
+
+#ifndef HAVE_UDEV
+#if HAVE_HAL
+#include "zypp/target/hal/HalContext.h"
+#endif
+#endif
+
+#include <cstring> // strerror
+#include <cstdlib> // getenv
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/media/Mount.h"
+#include "zypp/media/MediaCD.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/Url.h"
+#include "zypp/AutoDispose.h"
+
+
+
+/*
+** if to throw exception on eject errors or ignore them
+*/
+#define  REPORT_EJECT_ERRORS     1
+
+/*
+** If defined to the full path of the eject utility,
+** it will be used additionally to the eject-ioctl.
+*/
+#define EJECT_TOOL_PATH "/bin/eject"
+
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//  CLASS NAME : MediaCD
+//
+///////////////////////////////////////////////////////////////////
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::MediaCD
+  //  METHOD TYPE : Constructor
+  //
+  //  DESCRIPTION :
+  //
+  MediaCD::MediaCD( const Url &      url_r,
+                    const Pathname & attach_point_hint_r )
+    : MediaHandler( url_r, attach_point_hint_r,
+                    url_r.getPathName(), // urlpath below attachpoint
+                    false ),
+      _lastdev(-1), _lastdev_tried(-1)
+  {
+    MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")"
+        << endl;
+
+    if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
+    {
+      ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
+      ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
+    }
+
+    string devices = _url.getQueryParam("devices");
+    if (!devices.empty())
+    {
+      string::size_type pos;
+      DBG << "parse " << devices << endl;
+      while(!devices.empty())
+      {
+        pos = devices.find(',');
+        string device = devices.substr(0,pos);
+        if (!device.empty())
+        {
+          MediaSource media("cdrom", device, 0, 0);
+          _devices.push_back( media);
+          DBG << "use device (delayed verify)" << device << endl;
+        }
+        if (pos!=string::npos)
+          devices=devices.substr(pos+1);
+        else
+          devices.erase();
+      }
+    }
+    else
+    {
+      DBG << "going to use on-demand device list" << endl;
+      return;
+    }
+
+    if( _devices.empty())
+    {
+      ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
+      ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::openTray
+  //  METHOD TYPE : bool
+  //
+  bool MediaCD::openTray( const std::string & device_r )
+  {
+    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+    int res = -1;
+
+    if ( fd != -1)
+    {
+      res = ::ioctl( fd, CDROMEJECT );
+      ::close( fd );
+    }
+
+    if ( res )
+    {
+      if( fd == -1)
+      {
+        WAR << "Unable to open '" << device_r
+            << "' (" << ::strerror( errno ) << ")" << endl;
+      }
+      else
+      {
+        WAR << "Eject " << device_r
+            << " failed (" << ::strerror( errno ) << ")" << endl;
+      }
+
+#if defined(EJECT_TOOL_PATH)
+      DBG << "Try to eject " << device_r << " using "
+        << EJECT_TOOL_PATH << " utility" << std::endl;
+
+      const char *cmd[3];
+      cmd[0] = EJECT_TOOL_PATH;
+      cmd[1] = device_r.c_str();
+      cmd[2] = NULL;
+      ExternalProgram eject(cmd, ExternalProgram::Stderr_To_Stdout);
+
+      for(std::string out( eject.receiveLine());
+          out.length(); out = eject.receiveLine())
+      {
+        DBG << " " << out;
+      }
+
+      if(eject.close() != 0)
+      {
+        WAR << "Eject of " << device_r << " failed." << std::endl;
+        return false;
+      }
+#else
+      return false;
+#endif
+    }
+    MIL << "Eject of " << device_r << " successful." << endl;
+    return true;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //   METHOD NAME : MediaCD::closeTray
+  //   METHOD TYPE : bool
+  //
+  bool MediaCD::closeTray( const std::string & device_r )
+  {
+    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+    if ( fd == -1 ) {
+      WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
+      return false;
+    }
+    int res = ::ioctl( fd, CDROMCLOSETRAY );
+    ::close( fd );
+    if ( res ) {
+      WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
+      return false;
+    }
+    DBG << "Close tray " << device_r << endl;
+    return true;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::detectDevices
+  //  METHOD TYPE : MediaCD::DeviceList
+  //
+  MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD ) const
+  {
+    DeviceList detected;
+#ifdef HAVE_UDEV
+    // http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/index.html
+    zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
+    if ( ! udev )
+    {
+      ERR << "Can't create udev context." << endl;
+      return DeviceList();
+    }
+
+    zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
+    if ( ! enumerate )
+    {
+      ERR << "Can't create udev list entry." << endl;
+      return DeviceList();
+    }
+
+    ::udev_enumerate_add_match_subsystem( enumerate, "block" );
+    ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
+    ::udev_enumerate_scan_devices( enumerate );
+
+    struct udev_list_entry * entry = 0;
+    udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
+    {
+      zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
+                                                                                     ::udev_list_entry_get_name( entry ) ),
+                                                     ::udev_device_unref );
+      if ( ! device )
+      {
+       ERR << "Can't create udev device." << endl;
+       continue;
+      }
+
+      if ( supportingDVD && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+      {
+       continue;       // looking for dvd only
+      }
+
+      const char * devnodePtr( ::udev_device_get_devnode( device ) );
+      if ( ! devnodePtr )
+      {
+       ERR << "Got NULL devicenode." << endl;
+       continue;
+      }
+
+      // In case we need it someday:
+      //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
+
+      PathInfo devnode( devnodePtr );
+      if ( devnode.isBlk() )
+      {
+       MediaSource media( "cdrom", devnode.path().asString(), devnode.major(), devnode.minor() );
+       DBG << "Found (udev): " << media << std::endl;
+       detected.push_back( media );
+      }
+    }
+    if ( detected.empty() )
+      WAR << "Did not find any CD/DVD device." << endl;
+#elif HAVE_HAL
+    using namespace zypp::target::hal;
+    try
+    {
+      HalContext hal(true);
+
+      std::vector<std::string> drv_udis;
+      drv_udis = hal.findDevicesByCapability("storage.cdrom");
+
+      DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
+      for(size_t d = 0; d < drv_udis.size(); d++)
+      {
+        HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
+
+        if( drv)
+        {
+          bool supportsDVD=false;
+          if( supportingDVD)
+          {
+            std::vector<std::string> caps;
+            try {
+              caps = drv.getCdromCapabilityNames();
+            }
+            catch(const HalException &e)
+            {
+              ZYPP_CAUGHT(e);
+            }
+
+            std::vector<std::string>::const_iterator ci;
+            for( ci=caps.begin(); ci != caps.end(); ++ci)
+            {
+              if( *ci == "dvd")
+                supportsDVD = true;
+            }
+          }
+
+          MediaSource media("cdrom", drv.getDeviceFile(),
+                            drv.getDeviceMajor(),
+                            drv.getDeviceMinor());
+          DBG << "Found " << drv_udis[d] << ": "
+              << media.asString() << std::endl;
+          if( supportingDVD && supportsDVD)
+          {
+            detected.push_front(media);
+          }
+          else
+          {
+            detected.push_back(media);
+          }
+        }
+      }
+    }
+    catch(const zypp::target::hal::HalException &e)
+    {
+      ZYPP_CAUGHT(e);
+    }
+#else // manual way
+#warning Poor CDROM devices detection without udev/HAL
+    WAR << "Cdrom drive detection without HAL! " << std::endl;
+    PathInfo dvdinfo( "/dev/dvd" );
+    PathInfo cdrinfo( "/dev/cdrom" );
+    if ( dvdinfo.isBlk() )
+    {
+      MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.major(), dvdinfo.minor() );
+      DBG << "Found (NO_HAL): " << media << std::endl;
+      detected.push_back( media );
+    }
+    if ( cdrinfo.isBlk()
+         && ! ( cdrinfo.major() == dvdinfo.major() && cdrinfo.minor() == dvdinfo.minor() ) )
+    {
+      MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.major(), cdrinfo.minor() );
+      DBG << "Found (NO_HAL): " << media << std::endl;
+      detected.push_back( media );
+    }
+#endif
+    return detected;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::attachTo
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+  //
+  void MediaCD::attachTo(bool next)
+  {
+    DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
+    if (next && _lastdev == -1)
+      ZYPP_THROW(MediaNotSupportedException(url()));
+
+    DeviceList detected(
+      detectDevices(_url.getScheme() == "dvd" ? true : false));
+
+    if(_devices.empty())
+    {
+      DBG << "creating on-demand device list" << endl;
+      //default is /dev/cdrom; for dvd: /dev/dvd if it exists
+      string device( "/dev/cdrom" );
+      if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
+      {
+        device = "/dev/dvd";
+      }
+
+      PathInfo dinfo(device);
+      if( dinfo.isBlk())
+      {
+        MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
+
+        DeviceList::const_iterator d( detected.begin());
+        for( ; d != detected.end(); ++d)
+        {
+          // /dev/cdrom or /dev/dvd to the front
+          if( media.equals( *d))
+            _devices.push_front( *d);
+          else
+            _devices.push_back( *d);
+        }
+      }
+      else
+      {
+        // no /dev/cdrom or /dev/dvd link
+        _devices = detected;
+      }
+    }
+
+    Mount mount;
+    string mountpoint = attachPoint().asString();
+    bool mountsucceeded = false;
+    int count = 0;
+    MediaMountException merr;
+
+    string options = _url.getQueryParam("mountoptions");
+    if (options.empty())
+    {
+      options="ro";
+    }
+
+    //TODO: make configurable
+    list<string> filesystems;
+
+    // if DVD, try UDF filesystem before iso9660
+    if ( _url.getScheme() == "dvd" )
+      filesystems.push_back("udf");
+
+    filesystems.push_back("iso9660");
+
+    // try all devices in sequence
+    for (DeviceList::iterator it = _devices.begin()
+    ; !mountsucceeded && it != _devices.end()
+    ; ++it, count++ )
+    {
+      DBG << "count " << count << endl;
+      if (next && count <=_lastdev_tried )
+      {
+        DBG << "skipping device " << it->name << endl;
+        continue;
+      }
+
+      _lastdev_tried = count;
+
+      MediaSource temp( *it);
+      bool        valid=false;
+      PathInfo    dinfo(temp.name);
+      if( dinfo.isBlk())
+      {
+        temp.maj_nr = dinfo.major();
+        temp.min_nr = dinfo.minor();
+
+        DeviceList::const_iterator d( detected.begin());
+        for( ; d != detected.end(); ++d)
+        {
+          if( temp.equals( *d))
+          {
+            valid = true;
+            break;
+          }
+        }
+      }
+      if( !valid)
+      {
+        DBG << "skipping invalid device: " << it->name << endl;
+        continue;
+      }
+
+      MediaSourceRef media( new MediaSource(temp));
+      AttachedMedia ret( findAttachedMedia( media));
+
+      if( ret.mediaSource && ret.attachPoint &&
+         !ret.attachPoint->empty())
+      {
+        DBG << "Using a shared media "
+            << ret.mediaSource->name
+            << " attached on "
+            << ret.attachPoint->path
+            << endl;
+        removeAttachPoint();
+        setAttachPoint(ret.attachPoint);
+        setMediaSource(ret.mediaSource);
+        _lastdev = count;
+        mountsucceeded = true;
+        break;
+      }
+
+      {
+        MediaManager  manager;
+        MountEntries  entries( manager.getMountEntries());
+        MountEntries::const_iterator e;
+        for( e = entries.begin(); e != entries.end(); ++e)
+        {
+          bool        is_device = false;
+          std::string dev_path(Pathname(e->src).asString());
+          PathInfo    dev_info;
+
+          if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
+              dev_info(e->src) && dev_info.isBlk())
+          {
+            is_device = true;
+          }
+
+          if( is_device && media->maj_nr == dev_info.major() &&
+                           media->min_nr == dev_info.minor())
+          {
+            AttachPointRef ap( new AttachPoint(e->dir, false));
+            AttachedMedia  am( media, ap);
+            {
+              DBG << "Using a system mounted media "
+                  << media->name
+                  << " attached on "
+                  << ap->path
+                  << endl;
+
+              media->iown = false; // mark attachment as foreign
+
+              setMediaSource(media);
+              setAttachPoint(ap);
+              _lastdev = count;
+              mountsucceeded = true;
+              break;
+            }
+          }
+        }
+        if( mountsucceeded)
+          break;
+      }
+
+      // close tray
+      closeTray( it->name );
+
+      // try all filesystems in sequence
+      for(list<string>::iterator fsit = filesystems.begin()
+          ; !mountsucceeded && fsit != filesystems.end()
+          ; ++fsit)
+      {
+        try
+        {
+          if( !isUseableAttachPoint(Pathname(mountpoint)))
+          {
+            mountpoint = createAttachPoint().asString();
+            setAttachPoint( mountpoint, true);
+            if( mountpoint.empty())
+            {
+              ZYPP_THROW( MediaBadAttachPointException(url()));
+            }
+          }
+
+          mount.mount(it->name, mountpoint, *fsit, options);
+
+          setMediaSource(media);
+
+          // wait for /etc/mtab update ...
+          // (shouldn't be needed)
+          int limit = 5;
+          while( !(mountsucceeded=isAttached()) && --limit)
+          {
+            sleep(1);
+          }
+
+          if( mountsucceeded)
+          {
+            _lastdev = count;
+          }
+          else
+          {
+            setMediaSource(MediaSourceRef());
+            try
+            {
+              mount.umount(attachPoint().asString());
+            }
+            catch (const MediaException & excpt_r)
+            {
+              ZYPP_CAUGHT(excpt_r);
+            }
+            ZYPP_THROW(MediaMountException(
+              "Unable to verify that the media was mounted",
+              it->name, mountpoint
+            ));
+          }
+        }
+        catch (const MediaMountException &e)
+        {
+          merr = e;
+          removeAttachPoint();
+          ZYPP_CAUGHT(e);
+        }
+        catch (const MediaException & excpt_r)
+        {
+          removeAttachPoint();
+          ZYPP_CAUGHT(excpt_r);
+        }
+      } // for filesystems
+    } // for _devices
+
+    if (!mountsucceeded)
+    {
+      _lastdev = -1;
+
+      if( !merr.mountOutput().empty())
+      {
+        ZYPP_THROW(MediaMountException(merr.mountError(),
+                                       _url.asString(),
+                                       mountpoint,
+                                       merr.mountOutput()));
+      }
+      else
+      {
+        ZYPP_THROW(MediaMountException("Mounting media failed",
+                                       _url.asString(), mountpoint));
+      }
+    }
+    DBG << _lastdev << " " << count << endl;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::releaseFrom
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that media is attached.
+  //
+  void MediaCD::releaseFrom( const std::string & ejectDev )
+  {
+    Mount mount;
+    try
+    {
+      AttachedMedia am( attachedMedia());
+      if(am.mediaSource && am.mediaSource->iown)
+        mount.umount(am.attachPoint->path.asString());
+    }
+    catch (const Exception & excpt_r)
+    {
+      ZYPP_CAUGHT(excpt_r);
+      if (!ejectDev.empty())
+      {
+        forceRelaseAllMedia(false);
+        if(openTray( ejectDev ))
+          return;
+      }
+      ZYPP_RETHROW(excpt_r);
+    }
+
+    // eject device
+    if (!ejectDev.empty())
+    {
+      forceRelaseAllMedia(false);
+      if( !openTray( ejectDev ))
+      {
+#if REPORT_EJECT_ERRORS
+        ZYPP_THROW(MediaNotEjectedException(ejectDev));
+#endif
+      }
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::forceEject
+  //  METHOD TYPE : void
+  //
+  // Asserted that media is not attached.
+  //
+  void MediaCD::forceEject(const std::string & ejectDev)
+  {
+    bool ejected=false;
+    if ( !isAttached()) {      // no device mounted in this instance
+
+      DeviceList detected(
+          detectDevices(_url.getScheme() == "dvd" ? true : false));
+
+      if(_devices.empty())
+      {
+        DBG << "creating on-demand device list" << endl;
+        //default is /dev/cdrom; for dvd: /dev/dvd if it exists
+        string device( "/dev/cdrom" );
+        if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
+          device = "/dev/dvd";
+        }
+
+        PathInfo dinfo(device);
+        if( dinfo.isBlk())
+        {
+          MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
+
+          DeviceList::const_iterator d( detected.begin());
+          for( ; d != detected.end(); ++d)
+          {
+            // /dev/cdrom or /dev/dvd to the front
+            if( media.equals( *d))
+              _devices.push_front( *d);
+            else
+              _devices.push_back( *d);
+          }
+        }
+        else
+        {
+          // no /dev/cdrom or /dev/dvd link
+          _devices = detected;
+        }
+      }
+
+      DeviceList::iterator it;
+      for( it = _devices.begin(); it != _devices.end(); ++it ) {
+        MediaSourceRef media( new MediaSource( *it));
+        if (media->name != ejectDev)
+          continue;
+
+        bool        valid=false;
+        PathInfo    dinfo(media->name);
+        if( dinfo.isBlk())
+        {
+          media->maj_nr = dinfo.major();
+          media->min_nr = dinfo.minor();
+
+          DeviceList::const_iterator d( detected.begin());
+          for( ; d != detected.end(); ++d)
+          {
+            if( media->equals( *d))
+            {
+              valid = true;
+              break;
+            }
+          }
+        }
+        if( !valid)
+        {
+          DBG << "skipping invalid device: " << it->name << endl;
+          continue;
+        }
+
+        // FIXME: we have also to check if it is mounted in the system
+        AttachedMedia ret( findAttachedMedia( media));
+        if( !ret.mediaSource)
+        {
+          forceRelaseAllMedia(media, false);
+          if ( openTray( it->name ) )
+          {
+            ejected = true;
+            break; // on 1st success
+          }
+        }
+      }
+    }
+    if( !ejected)
+    {
+#if REPORT_EJECT_ERRORS
+      ZYPP_THROW(MediaNotEjectedException());
+#endif
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  METHOD NAME : MediaCD::isAttached
+  //  METHOD TYPE : bool
+  //
+  //  DESCRIPTION : Override check if media is attached.
+  //
+  bool
+  MediaCD::isAttached() const
+  {
+    return checkAttached(false);
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  METHOD NAME : MediaCD::getFile
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that media is attached.
+  //
+  void MediaCD::getFile( const Pathname & filename ) const
+  {
+    MediaHandler::getFile( filename );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  METHOD NAME : MediaCD::getDir
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that media is attached.
+  //
+  void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
+  {
+    MediaHandler::getDir( dirname, recurse_r );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::getDirInfo
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that media is attached and retlist is empty.
+  //
+  void MediaCD::getDirInfo( std::list<std::string> & retlist,
+                            const Pathname & dirname, bool dots ) const
+  {
+    MediaHandler::getDirInfo( retlist, dirname, dots );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //
+  //  METHOD NAME : MediaCD::getDirInfo
+  //  METHOD TYPE : PMError
+  //
+  //  DESCRIPTION : Asserted that media is attached and retlist is empty.
+  //
+  void MediaCD::getDirInfo( filesystem::DirContent & retlist,
+                            const Pathname & dirname, bool dots ) const
+  {
+    MediaHandler::getDirInfo( retlist, dirname, dots );
+  }
+
+  bool MediaCD::getDoesFileExist( const Pathname & filename ) const
+  {
+    return MediaHandler::getDoesFileExist( filename );
+  }
+
+  bool MediaCD::hasMoreDevices()
+  {
+    if (_devices.size() == 0)
+      return false;
+    else if (_lastdev_tried < 0)
+      return true;
+
+    return (unsigned) _lastdev_tried < _devices.size() - 1;
+  }
+
+  void
+  MediaCD::getDetectedDevices(std::vector<std::string> & devices,
+                              unsigned int & index) const
+  {
+    index = 0;
+    if (!devices.empty())
+      devices.clear();
+
+    for (DeviceList::const_iterator it = _devices.begin();
+         it != _devices.end(); ++it)
+      devices.push_back(it->name);
+
+    if (_lastdev >= 0)
+      index = _lastdev;
+
+    // try to detect again if _devices are empty (maybe this method will be
+    // called before _devices get actually filled)
+    if (devices.empty())
+    {
+      DBG << "no device list so far, trying to detect" << endl;
+
+      DeviceList detected(
+        detectDevices(_url.getScheme() == "dvd" ? true : false));
+
+      for (DeviceList::const_iterator it = detected.begin();
+           it != detected.end(); ++it)
+        devices.push_back(it->name);
+
+      // don't know which one is in use in this case
+      index = 0;
+    }
+
+    MIL << "got " << devices.size() << " detected devices, current: "
+        << (index < devices.size() ? devices[index] : "<none>")
+        << "(" << index << ")" << endl;
+  }
+
+
+  } // namespace media
+} // namespace zypp
+// vim: set ts=8 sts=2 sw=2 ai noet:
diff --git a/zypp/media/MediaCD.h b/zypp/media/MediaCD.h
new file mode 100644 (file)
index 0000000..e93415f
--- /dev/null
@@ -0,0 +1,78 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCD.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIACD_H
+#define ZYPP_MEDIA_MEDIACD_H
+
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/MediaManager.h"
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaCD
+    /**
+     * @short Implementation class for CD/DVD MediaHandler
+     * @see MediaHandler
+     **/
+    class MediaCD : public MediaHandler {
+
+      private:
+
+        typedef std::list<MediaSource> DeviceList;
+        /** list of devices to try to mount */
+        DeviceList _devices;
+
+        /** number of last successful mounted device in list */
+        int        _lastdev;
+        int        _lastdev_tried;
+
+        static bool openTray( const std::string & device_r );
+        static bool closeTray( const std::string & device_r );
+
+       DeviceList  detectDevices(bool supportingDVD) const;
+
+      protected:
+
+       virtual void attachTo (bool next = false);
+        virtual void releaseFrom( const std::string & ejectDev );
+       virtual void getFile( const Pathname & filename ) const;
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+        virtual void forceEject(const std::string & ejectDev);
+
+        virtual bool hasMoreDevices();
+
+        virtual void
+        getDetectedDevices(std::vector<std::string> & devices,
+                           unsigned int & index) const;
+
+      public:
+
+        MediaCD( const Url &      url_r,
+                const Pathname & attach_point_hint_r );
+
+        virtual ~MediaCD() { try { release(); } catch(...) {} }
+
+       virtual bool isAttached() const;
+    };
+
+///////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
+#endif // ZYPP_MEDIA_MEDIACD_H
diff --git a/zypp/media/MediaCIFS.cc b/zypp/media/MediaCIFS.cc
new file mode 100644 (file)
index 0000000..753c4db
--- /dev/null
@@ -0,0 +1,474 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCIFS.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/TmpPath.h"
+#include "zypp/KVMap.h"
+#include "zypp/media/Mount.h"
+#include "zypp/media/MediaUserAuth.h"
+#include "zypp/media/CredentialManager.h"
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/ZConfig.h"
+
+#include "zypp/media/MediaCIFS.h"
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <dirent.h>
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+    /******************************************************************
+    **
+    **
+    ** FUNCTION NAME : getShare
+    ** FUNCTION TYPE : inline Pathname
+    **
+    ** Get the 1st path component (CIFS share name).
+    */
+    inline string getShare( Pathname spath_r )
+    {
+      if ( spath_r.empty() )
+        return string();
+
+      string share( spath_r.absolutename().asString() );
+      string::size_type sep = share.find( "/", 1 );
+      if ( sep == string::npos )
+        share = share.erase( 0, 1 ); // nothing but the share name in spath_r
+      else
+        share = share.substr( 1, sep-1 );
+
+      // deescape %2f in sharename
+      while ( (sep = share.find( "%2f" )) != string::npos ) {
+        share.replace( sep, 3, "/" );
+      }
+
+      return share;
+    }
+
+    /******************************************************************
+    **
+    **
+    ** FUNCTION NAME : stripShare
+    ** FUNCTION TYPE : inline Pathname
+    **
+    ** Strip off the 1st path component (CIFS share name).
+    */
+    inline Pathname stripShare( Pathname spath_r )
+    {
+      if ( spath_r.empty() )
+        return Pathname();
+
+      string striped( spath_r.absolutename().asString() );
+      string::size_type sep = striped.find( "/", 1 );
+      if ( sep == string::npos )
+        return "/"; // nothing but the share name in spath_r
+
+      return striped.substr( sep );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaCIFS
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaCIFS::MediaCIFS
+    // METHOD TYPE : Constructor
+    //
+    // DESCRIPTION :
+    //
+    MediaCIFS::MediaCIFS( const Url &      url_r,
+                       const Pathname & attach_point_hint_r )
+        : MediaHandler( url_r, attach_point_hint_r,
+                   stripShare( url_r.getPathName() ), // urlpath WITHOUT share name at attachpoint
+                   false )       // does_download
+    {
+       MIL << "MediaCIFS::MediaCIFS(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaCIFS::attachTo
+    // METHOD TYPE : PMError
+    /**
+     * Asserted that not already attached, and attachPoint is a directory.
+     *
+     * Authentication: credentials can be specified in the following few ways
+     * (the first has the highest preference).
+     * - URL username:password
+     * - mountoptions URL query parameter (see man mount.cifs)
+     * - CredentialManager - either previously saved credentials will be used
+     *   or the user will be promted for them via AuthenticationReport callback.
+     *
+     * \note The implementation currently serves both, "smb" and
+     *      and "cifs" URL's, but passes "cifs" to the mount command
+     *      in any case.
+     */
+    void MediaCIFS::attachTo(bool next)
+    {
+      if(_url.getHost().empty())
+       ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
+      if(next)
+       ZYPP_THROW(MediaNotSupportedException(_url));
+
+      string path = "//";
+      path += _url.getHost() + "/" + getShare( _url.getPathName() );
+
+      MediaSourceRef media( new MediaSource( "cifs", path));
+      AttachedMedia  ret( findAttachedMedia( media));
+
+      if( ret.mediaSource &&
+         ret.attachPoint &&
+         !ret.attachPoint->empty())
+      {
+       DBG << "Using a shared media "
+           << ret.mediaSource->name
+           << " attached on "
+           << ret.attachPoint->path
+           << endl;
+
+       removeAttachPoint();
+       setAttachPoint(ret.attachPoint);
+       setMediaSource(ret.mediaSource);
+       return;
+      }
+
+      std::string mountpoint = attachPoint().asString();
+      if( !isUseableAttachPoint(attachPoint()))
+      {
+       mountpoint = createAttachPoint().asString();
+       if( mountpoint.empty())
+         ZYPP_THROW( MediaBadAttachPointException(url()));
+       setAttachPoint( mountpoint, true);
+      }
+
+      Mount mount;
+      CredentialManager cm;
+
+      Mount::Options options( _url.getQueryParam("mountoptions") );
+      string username = _url.getUsername();
+      string password = _url.getPassword();
+
+      if ( ! options.has( "rw" ) ) {
+        options["ro"];
+      }
+
+      // look for a workgroup
+      string workgroup = _url.getQueryParam("workgroup");
+      if ( workgroup.empty() )
+        workgroup = _url.getQueryParam("domain");
+      if ( !workgroup.empty() )
+        options["domain"] = workgroup;
+
+      // extract 'username', do not overwrite any _url.username
+
+      Mount::Options::iterator toEnv;
+      toEnv = options.find("username");
+      if ( toEnv != options.end() ) {
+        if ( username.empty() )
+          username = toEnv->second;
+        options.erase( toEnv );
+      }
+
+      toEnv = options.find("user"); // actually cifs specific
+      if ( toEnv != options.end() ) {
+        if ( username.empty() )
+          username = toEnv->second;
+        options.erase( toEnv );
+      }
+
+      // extract 'password', do not overwrite any _url.password
+
+      toEnv = options.find("password");
+      if ( toEnv != options.end() ) {
+        if ( password.empty() )
+          password = toEnv->second;
+        options.erase( toEnv );
+      }
+
+      toEnv = options.find("pass"); // actually cifs specific
+      if ( toEnv != options.end() ) {
+        if ( password.empty() )
+          password = toEnv->second;
+        options.erase( toEnv );
+      }
+
+      if ( username.empty() || password.empty() )
+      {
+        AuthData_Ptr c = cm.getCred(_url);
+        if (c)
+        {
+          username = c->username();
+          password = c->password();
+        }
+      }
+
+      bool firstTry = true;
+      bool authRequired = false;
+      AuthData authdata;
+      do // repeat this while the mount returns "Permission denied" error
+      {
+        // get credentials from authenicate()
+        if ( !firstTry )
+        {
+          username = authdata.username();
+          password = authdata.password();
+        }
+
+        // pass 'username' and 'password' via environment
+        Mount::Environment environment;
+        if ( !username.empty() )
+          environment["USER"] = username;
+        if ( !password.empty() )
+          environment["PASSWD"] = password;
+
+        //////////////////////////////////////////////////////
+        // In case we need a tmpfile, credentials will remove
+        // it in it's destructor after the mout call below.
+        filesystem::TmpPath credentials;
+        if ( !username.empty() || !password.empty() )
+        {
+          filesystem::TmpFile tmp;
+          ofstream outs( tmp.path().asString().c_str() );
+          outs << "username=" <<  username << endl;
+          outs << "password=" <<  password << endl;
+          outs.close();
+
+          credentials = tmp;
+          options["credentials"] = credentials.path().asString();
+        }
+        else
+        {
+          // Use 'guest' option unless explicitly disabled (bnc #547354)
+          if ( options.has( "noguest" ) )
+            options.erase( "noguest" );
+          else
+            // prevent smbmount from asking for password
+            // only add this option if 'credentials' is not used (bnc #560496)
+            options["guest"];
+        }
+
+        //
+        //////////////////////////////////////////////////////
+
+        try
+        {
+          mount.mount( path, mountpoint, "cifs",
+                       options.asString(), environment );
+          setMediaSource(media);
+          break;
+        }
+        catch (const MediaMountException & e)
+        {
+          ZYPP_CAUGHT( e );
+
+          if ( e.mountError() == "Permission denied" )
+            authRequired = authenticate( authdata, firstTry );
+          else
+            ZYPP_RETHROW( e );
+        }
+
+        firstTry = false;
+      }
+      while ( authRequired );
+
+      // wait for /etc/mtab update ...
+      // (shouldn't be needed)
+      int limit = 3;
+      bool mountsucceeded;
+      while( !(mountsucceeded=isAttached()) && --limit)
+        sleep(1);
+
+      if ( !mountsucceeded )
+      {
+        setMediaSource(MediaSourceRef());
+        try
+        {
+          mount.umount(attachPoint().asString());
+        }
+        catch (const MediaException & excpt_r)
+        {
+          ZYPP_CAUGHT(excpt_r);
+        }
+        ZYPP_THROW(MediaMountException(
+          "Unable to verify that the media was mounted",
+          path, mountpoint
+        ));
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaCIFS::isAttached
+    // METHOD TYPE : bool
+    //
+    // DESCRIPTION : Override check if media is attached.
+    //
+    bool
+    MediaCIFS::isAttached() const
+    {
+      return checkAttached(true);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaCIFS::releaseFrom
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaCIFS::releaseFrom( const std::string & ejectDev )
+    {
+      Mount mount;
+      mount.umount(attachPoint().asString());
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaCIFS::getFile
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaCIFS::getFile (const Pathname & filename) const
+    {
+      MediaHandler::getFile( filename );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaCIFS::getDir
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaCIFS::getDir( const Pathname & dirname, bool recurse_r ) const
+    {
+      MediaHandler::getDir( dirname, recurse_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaCIFS::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaCIFS::getDirInfo( std::list<std::string> & retlist,
+                              const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaCIFS::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaCIFS::getDirInfo( filesystem::DirContent & retlist,
+                              const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    bool MediaCIFS::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
+
+    bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const
+    {
+      //! \todo need a way to pass different CredManagerOptions here
+      CredentialManager cm(CredManagerOptions(ZConfig::instance().systemRoot()));
+
+      // get stored credentials
+      AuthData_Ptr cmcred = cm.getCred(_url);
+
+      AuthData_Ptr smbcred;
+      smbcred.reset(new AuthData());
+      callback::SendReport<AuthenticationReport> auth_report;
+
+      // preset the username if present in current url
+      if (!_url.getUsername().empty() && firstTry)
+        smbcred->setUsername(_url.getUsername());
+      // if CM has found some credentials, preset the username from there
+      else if (cmcred)
+        smbcred->setUsername(cmcred->username());
+
+      // indicate we have no good credentials from CM
+      cmcred.reset();
+
+      string prompt_msg = str::form(
+        //!\todo add comma to the message for the next release
+        _("Authentication required for '%s'"), _url.asString().c_str());
+
+      // ask user
+      if (auth_report->prompt(_url, prompt_msg, *smbcred))
+      {
+        DBG << "callback answer: retry" << endl
+            << "AuthData: " << *smbcred << endl;
+
+        if (smbcred->valid())
+        {
+          cmcred = smbcred;
+            // if (credentials->username() != _url.getUsername())
+            //   _url.setUsername(credentials->username());
+            /**
+             *  \todo find a way to save the url with changed username
+             *  back to repoinfo or dont store urls with username
+             *  (and either forbid more repos with the same url and different
+             *  user, or return a set of credentials from CM and try them one
+             *  by one)
+             */
+        }
+      }
+      else
+        DBG << "callback answer: cancel" << endl;
+
+      // set username and password
+      if (cmcred)
+      {
+        authdata.setUsername(cmcred->username());
+        authdata.setPassword(cmcred->password());
+
+        // save the credentials
+        cmcred->setUrl(_url);
+        cm.addCred(*cmcred);
+        cm.save();
+
+        return true;
+      }
+
+      return false;
+    }
+
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaCIFS.h b/zypp/media/MediaCIFS.h
new file mode 100644 (file)
index 0000000..73affb4
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCIFS.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIACIFS_H
+#define ZYPP_MEDIA_MEDIACIFS_H
+
+#include "zypp/media/MediaHandler.h"
+
+namespace zypp {
+  namespace media {
+
+    class AuthData;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaCIFS
+    /**
+     * @short Implementation class for CIFS MediaHandler
+     *
+     * NOTE: The implementation serves both, "smb" and "cifs" URL's,
+     * but passes "cifs" to the mount command in any case.
+     * @see MediaHandler
+     **/
+    class MediaCIFS : public MediaHandler {
+
+    protected:
+
+      virtual void attachTo (bool next = false);
+      virtual void releaseFrom( const std::string & ejectDev );
+      virtual void getFile( const Pathname & filename ) const;
+      virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+      virtual void getDirInfo( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots = true ) const;
+      virtual void getDirInfo( filesystem::DirContent & retlist,
+                               const Pathname & dirname, bool dots = true ) const;
+      virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+    public:
+      MediaCIFS( const Url&       url_r,
+               const Pathname & attach_point_hint_r );
+
+      virtual ~MediaCIFS() { try { release(); } catch(...) {} }
+
+      virtual bool isAttached() const;
+
+    private:
+      bool authenticate( AuthData & authdata, bool firstTry ) const;
+    };
+
+///////////////////////////////////////////////////////////////////A
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIACIFS_H
diff --git a/zypp/media/MediaCurl.cc b/zypp/media/MediaCurl.cc
new file mode 100644 (file)
index 0000000..ce72ce9
--- /dev/null
@@ -0,0 +1,1661 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCurl.cc
+ *
+*/
+
+#include <iostream>
+#include <list>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/Gettext.h"
+
+#include "zypp/media/MediaCurl.h"
+#include "zypp/media/proxyinfo/ProxyInfos.h"
+#include "zypp/media/ProxyInfo.h"
+#include "zypp/media/MediaUserAuth.h"
+#include "zypp/media/CredentialManager.h"
+#include "zypp/media/CurlConfig.h"
+#include "zypp/thread/Once.h"
+#include "zypp/Target.h"
+#include "zypp/ZYppFactory.h"
+
+#include <cstdlib>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <boost/format.hpp>
+
+#define  DETECT_DIR_INDEX       0
+#define  CONNECT_TIMEOUT        60
+#define  TRANSFER_TIMEOUT       60 * 3
+#define  TRANSFER_TIMEOUT_MAX   60 * 60
+
+
+using namespace std;
+using namespace zypp::base;
+
+namespace
+{
+  zypp::thread::OnceFlag g_InitOnceFlag = PTHREAD_ONCE_INIT;
+  zypp::thread::OnceFlag g_FreeOnceFlag = PTHREAD_ONCE_INIT;
+
+  extern "C" void _do_free_once()
+  {
+    curl_global_cleanup();
+  }
+
+  extern "C" void globalFreeOnce()
+  {
+    zypp::thread::callOnce(g_FreeOnceFlag, _do_free_once);
+  }
+
+  extern "C" void _do_init_once()
+  {
+    CURLcode ret = curl_global_init( CURL_GLOBAL_ALL );
+    if ( ret != 0 )
+    {
+      WAR << "curl global init failed" << endl;
+    }
+
+    //
+    // register at exit handler ?
+    // this may cause trouble, because we can protect it
+    // against ourself only.
+    // if the app sets an atexit handler as well, it will
+    // cause a double free while the second of them runs.
+    //
+    //std::atexit( globalFreeOnce);
+  }
+
+  inline void globalInitOnce()
+  {
+    zypp::thread::callOnce(g_InitOnceFlag, _do_init_once);
+  }
+
+  int log_curl(CURL *curl, curl_infotype info,
+               char *ptr, size_t len, void *max_lvl)
+  {
+    std::string pfx(" ");
+    long        lvl = 0;
+    switch( info)
+    {
+      case CURLINFO_TEXT:       lvl = 1; pfx = "*"; break;
+      case CURLINFO_HEADER_IN:  lvl = 2; pfx = "<"; break;
+      case CURLINFO_HEADER_OUT: lvl = 2; pfx = ">"; break;
+      default:                                      break;
+    }
+    if( lvl > 0 && max_lvl != NULL && lvl <= *((long *)max_lvl))
+    {
+      std::string                            msg(ptr, len);
+      std::list<std::string>                 lines;
+      std::list<std::string>::const_iterator line;
+      zypp::str::split(msg, std::back_inserter(lines), "\r\n");
+      for(line = lines.begin(); line != lines.end(); ++line)
+      {
+        DBG << pfx << " " << *line << endl;
+      }
+    }
+    return 0;
+  }
+
+  static size_t
+  log_redirects_curl(
+      void *ptr, size_t size, size_t nmemb, void *stream)
+  {
+    // INT << "got header: " << string((char *)ptr, ((char*)ptr) + size*nmemb) << endl;
+
+    char * lstart = (char *)ptr, * lend = (char *)ptr;
+    size_t pos = 0;
+    size_t max = size * nmemb;
+    while (pos + 1 < max)
+    {
+      // get line
+      for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
+
+      // look for "Location"
+      string line(lstart, lend);
+      if (line.find("Location") != string::npos)
+      {
+        DBG << "redirecting to " << line << endl;
+        return max;
+      }
+
+      // continue with the next line
+      if (pos + 1 < max)
+      {
+        ++lend;
+        ++pos;
+      }
+      else
+        break;
+    }
+
+    return max;
+  }
+}
+
+namespace zypp {
+  namespace media {
+
+  namespace {
+    struct ProgressData
+    {
+      ProgressData(const long _timeout, const zypp::Url &_url = zypp::Url(),
+                   callback::SendReport<DownloadProgressReport> *_report=NULL)
+        : timeout(_timeout)
+        , reached(false)
+        , report(_report)
+        , drate_period(-1)
+        , dload_period(0)
+        , secs(0)
+        , drate_avg(-1)
+        , ltime( time(NULL))
+        , dload( 0)
+        , uload( 0)
+        , url(_url)
+      {}
+      long                                          timeout;
+      bool                                          reached;
+      callback::SendReport<DownloadProgressReport> *report;
+      // download rate of the last period (cca 1 sec)
+      double                                        drate_period;
+      // bytes downloaded at the start of the last period
+      double                                        dload_period;
+      // seconds from the start of the download
+      long                                          secs;
+      // average download rate
+      double                                        drate_avg;
+      // last time the progress was reported
+      time_t                                        ltime;
+      // bytes downloaded at the moment the progress was last reported
+      double                                        dload;
+      // bytes uploaded at the moment the progress was last reported
+      double                                        uload;
+      zypp::Url                                     url;
+    };
+
+    ///////////////////////////////////////////////////////////////////
+
+    inline void escape( string & str_r,
+                        const char char_r, const string & escaped_r ) {
+      for ( string::size_type pos = str_r.find( char_r );
+            pos != string::npos; pos = str_r.find( char_r, pos ) ) {
+              str_r.replace( pos, 1, escaped_r );
+            }
+    }
+
+    inline string escapedPath( string path_r ) {
+      escape( path_r, ' ', "%20" );
+      return path_r;
+    }
+
+    inline string unEscape( string text_r ) {
+      char * tmp = curl_unescape( text_r.c_str(), 0 );
+      string ret( tmp );
+      curl_free( tmp );
+      return ret;
+    }
+
+  }
+
+/**
+ * Fills the settings structure using options passed on the url
+ * for example ?timeout=x&proxy=foo
+ */
+void fillSettingsFromUrl( const Url &url, TransferSettings &s )
+{
+    std::string param(url.getQueryParam("timeout"));
+    if( !param.empty())
+    {
+      long num = str::strtonum<long>(param);
+      if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX)
+          s.setTimeout(num);
+    }
+
+    if ( ! url.getUsername().empty() )
+    {
+        s.setUsername(url.getUsername());
+        if ( url.getPassword().size() )
+            s.setPassword(url.getPassword());
+    }
+    else
+    {
+        // if there is no username, set anonymous auth
+        if ( url.getScheme() == "ftp" && s.username().empty() )
+            s.setAnonymousAuth();
+    }
+
+    if ( url.getScheme() == "https" )
+    {
+        s.setVerifyPeerEnabled(false);
+        s.setVerifyHostEnabled(false);
+
+        std::string verify( url.getQueryParam("ssl_verify"));
+        if( verify.empty() ||
+            verify == "yes")
+        {
+            s.setVerifyPeerEnabled(true);
+            s.setVerifyHostEnabled(true);
+        }
+        else if( verify == "no")
+        {
+            s.setVerifyPeerEnabled(false);
+            s.setVerifyHostEnabled(false);
+        }
+        else
+        {
+            std::vector<std::string>                 flags;
+            std::vector<std::string>::const_iterator flag;
+            str::split( verify, std::back_inserter(flags), ",");
+            for(flag = flags.begin(); flag != flags.end(); ++flag)
+            {
+                if( *flag == "host")
+                    s.setVerifyHostEnabled(true);
+                else if( *flag == "peer")
+                    s.setVerifyPeerEnabled(true);
+                else
+                    ZYPP_THROW(MediaBadUrlException(url, "Unknown ssl_verify flag"));
+            }
+        }
+    }
+
+    Pathname ca_path = Pathname(url.getQueryParam("ssl_capath")).asString();
+    if( ! ca_path.empty())
+    {
+        if( !PathInfo(ca_path).isDir() || !Pathname(ca_path).absolute())
+            ZYPP_THROW(MediaBadUrlException(url, "Invalid ssl_capath path"));
+        else
+            s.setCertificateAuthoritiesPath(ca_path);
+    }
+
+    string proxy = url.getQueryParam( "proxy" );
+    if ( ! proxy.empty() )
+    {
+        if ( proxy == "_none_" ) {
+            s.setProxyEnabled(false);
+        }
+        else {
+            string proxyport( url.getQueryParam( "proxyport" ) );
+            if ( ! proxyport.empty() ) {
+                proxy += ":" + proxyport;
+            }
+            s.setProxy(proxy);
+            s.setProxyEnabled(true);
+        }
+    }
+
+    // HTTP authentication type
+    string use_auth = url.getQueryParam("auth");
+    if (!use_auth.empty() && (url.getScheme() == "http" || url.getScheme() == "https"))
+    {
+        try
+        {
+           CurlAuthData::auth_type_str2long(use_auth); // check if we know it
+        }
+        catch (MediaException & ex_r)
+       {
+           DBG << "Rethrowing as MediaUnauthorizedException.";
+           ZYPP_THROW(MediaUnauthorizedException(url, ex_r.msg(), "", ""));
+       }
+        s.setAuthType(use_auth);
+    }
+
+    // workarounds
+    std::string head_requests( url.getQueryParam("head_requests"));
+    if( !head_requests.empty() && head_requests == "no")
+        s.setHeadRequestsAllowed(false);
+}
+
+/**
+ * Reads the system proxy configuration and fills the settings
+ * structure proxy information
+ */
+void fillSettingsSystemProxy( const Url&url, TransferSettings &s )
+{
+#ifdef _WITH_LIBPROXY_SUPPORT_
+    ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoLibproxy()));
+#else
+    ProxyInfo proxy_info (ProxyInfo::ImplPtr(new ProxyInfoSysconfig("proxy")));
+#endif
+    s.setProxyEnabled( proxy_info.useProxyFor( url ) );
+    if ( s.proxyEnabled() )
+      s.setProxy(proxy_info.proxy(url));
+}
+
+Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
+
+/**
+ * initialized only once, this gets the anonymous id
+ * from the target, which we pass in the http header
+ */
+static const char *const anonymousIdHeader()
+{
+  // we need to add the release and identifier to the
+  // agent string.
+  // The target could be not initialized, and then this information
+  // is guessed.
+  static const std::string _value(
+      str::trim( str::form(
+          "X-ZYpp-AnonymousId: %s",
+          Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
+  );
+  return _value.c_str();
+}
+
+/**
+ * initialized only once, this gets the distribution flavor
+ * from the target, which we pass in the http header
+ */
+static const char *const distributionFlavorHeader()
+{
+  // we need to add the release and identifier to the
+  // agent string.
+  // The target could be not initialized, and then this information
+  // is guessed.
+  static const std::string _value(
+      str::trim( str::form(
+          "X-ZYpp-DistributionFlavor: %s",
+          Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
+  );
+  return _value.c_str();
+}
+
+/**
+ * initialized only once, this gets the agent string
+ * which also includes the curl version
+ */
+static const char *const agentString()
+{
+  // we need to add the release and identifier to the
+  // agent string.
+  // The target could be not initialized, and then this information
+  // is guessed.
+  static const std::string _value(
+    str::form(
+       "ZYpp %s (curl %s) %s"
+       , VERSION
+       , curl_version_info(CURLVERSION_NOW)->version
+       , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
+    )
+  );
+  return _value.c_str();
+}
+
+// we use this define to unbloat code as this C setting option
+// and catching exception is done frequently.
+/** \todo deprecate SET_OPTION and use the typed versions below. */
+#define SET_OPTION(opt,val) do { \
+    ret = curl_easy_setopt ( _curl, opt, val ); \
+    if ( ret != 0) { \
+      ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
+    } \
+  } while ( false )
+
+#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
+#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
+#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
+
+MediaCurl::MediaCurl( const Url &      url_r,
+                      const Pathname & attach_point_hint_r )
+    : MediaHandler( url_r, attach_point_hint_r,
+                    "/", // urlpath at attachpoint
+                    true ), // does_download
+      _curl( NULL ),
+      _customHeaders(0L)
+{
+  _curlError[0] = '\0';
+  _curlDebug = 0L;
+
+  MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+
+  globalInitOnce();
+
+  if( !attachPoint().empty())
+  {
+    PathInfo ainfo(attachPoint());
+    Pathname apath(attachPoint() + "XXXXXX");
+    char    *atemp = ::strdup( apath.asString().c_str());
+    char    *atest = NULL;
+    if( !ainfo.isDir() || !ainfo.userMayRWX() ||
+         atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
+    {
+      WAR << "attach point " << ainfo.path()
+          << " is not useable for " << url_r.getScheme() << endl;
+      setAttachPoint("", true);
+    }
+    else if( atest != NULL)
+      ::rmdir(atest);
+
+    if( atemp != NULL)
+      ::free(atemp);
+  }
+}
+
+Url MediaCurl::clearQueryString(const Url &url) const
+{
+  Url curlUrl (url);
+  curlUrl.setUsername( "" );
+  curlUrl.setPassword( "" );
+  curlUrl.setPathParams( "" );
+  curlUrl.setFragment( "" );
+  curlUrl.delQueryParam("cookies");
+  curlUrl.delQueryParam("proxy");
+  curlUrl.delQueryParam("proxyport");
+  curlUrl.delQueryParam("proxyuser");
+  curlUrl.delQueryParam("proxypass");
+  curlUrl.delQueryParam("ssl_capath");
+  curlUrl.delQueryParam("ssl_verify");
+  curlUrl.delQueryParam("timeout");
+  curlUrl.delQueryParam("auth");
+  curlUrl.delQueryParam("username");
+  curlUrl.delQueryParam("password");
+  curlUrl.delQueryParam("mediahandler");
+  return curlUrl;
+}
+
+TransferSettings & MediaCurl::settings()
+{
+    return _settings;
+}
+
+
+void MediaCurl::setCookieFile( const Pathname &fileName )
+{
+  _cookieFile = fileName;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::checkProtocol(const Url &url) const
+{
+  curl_version_info_data *curl_info = NULL;
+  curl_info = curl_version_info(CURLVERSION_NOW);
+  // curl_info does not need any free (is static)
+  if (curl_info->protocols)
+  {
+    const char * const *proto;
+    std::string        scheme( url.getScheme());
+    bool               found = false;
+    for(proto=curl_info->protocols; !found && *proto; ++proto)
+    {
+      if( scheme == std::string((const char *)*proto))
+        found = true;
+    }
+    if( !found)
+    {
+      std::string msg("Unsupported protocol '");
+      msg += scheme;
+      msg += "'";
+      ZYPP_THROW(MediaBadUrlException(_url, msg));
+    }
+  }
+}
+
+void MediaCurl::setupEasy()
+{
+  {
+    char *ptr = getenv("ZYPP_MEDIA_CURL_DEBUG");
+    _curlDebug = (ptr && *ptr) ? str::strtonum<long>( ptr) : 0L;
+    if( _curlDebug > 0)
+    {
+      curl_easy_setopt( _curl, CURLOPT_VERBOSE, 1L);
+      curl_easy_setopt( _curl, CURLOPT_DEBUGFUNCTION, log_curl);
+      curl_easy_setopt( _curl, CURLOPT_DEBUGDATA, &_curlDebug);
+    }
+  }
+
+  curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
+  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
+  if ( ret != 0 ) {
+    ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
+  }
+
+  SET_OPTION(CURLOPT_FAILONERROR, 1L);
+  SET_OPTION(CURLOPT_NOSIGNAL, 1L);
+
+  // create non persistant settings
+  // so that we don't add headers twice
+  TransferSettings vol_settings(_settings);
+
+  // add custom headers
+  vol_settings.addHeader(anonymousIdHeader());
+  vol_settings.addHeader(distributionFlavorHeader());
+  vol_settings.addHeader("Pragma:");
+
+  _settings.setTimeout(TRANSFER_TIMEOUT);
+  _settings.setConnectTimeout(CONNECT_TIMEOUT);
+
+  _settings.setUserAgentString(agentString());
+
+  // fill some settings from url query parameters
+  try
+  {
+      fillSettingsFromUrl(_url, _settings);
+  }
+  catch ( const MediaException &e )
+  {
+      disconnectFrom();
+      ZYPP_RETHROW(e);
+  }
+
+  // if the proxy was not set by url, then look
+  if ( _settings.proxy().empty() )
+  {
+      // at the system proxy settings
+      fillSettingsSystemProxy(_url, _settings);
+  }
+
+  DBG << "Proxy: " << (_settings.proxy().empty() ? "-none-" : _settings.proxy()) << endl;
+
+ /**
+  * Connect timeout
+  */
+  SET_OPTION(CURLOPT_CONNECTTIMEOUT, _settings.connectTimeout());
+
+  // follow any Location: header that the server sends as part of
+  // an HTTP header (#113275)
+  SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
+  // 3 redirects seem to be too few in some cases (bnc #465532)
+  SET_OPTION(CURLOPT_MAXREDIRS, 6L);
+
+  if ( _url.getScheme() == "https" )
+  {
+#if LIBCURL_VERSION_NUMBER >= 0x071904
+    // restrict following of redirections from https to https only
+    SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
+#endif
+
+    if( _settings.verifyPeerEnabled() ||
+        _settings.verifyHostEnabled() )
+    {
+      SET_OPTION(CURLOPT_CAPATH, _settings.certificateAuthoritiesPath().c_str());
+    }
+
+    SET_OPTION(CURLOPT_SSL_VERIFYPEER, _settings.verifyPeerEnabled() ? 1L : 0L);
+    SET_OPTION(CURLOPT_SSL_VERIFYHOST, _settings.verifyHostEnabled() ? 2L : 0L);
+  }
+
+  SET_OPTION(CURLOPT_USERAGENT, _settings.userAgentString().c_str() );
+
+  /*---------------------------------------------------------------*
+   CURLOPT_USERPWD: [user name]:[password]
+
+   Url::username/password -> CURLOPT_USERPWD
+   If not provided, anonymous FTP identification
+   *---------------------------------------------------------------*/
+
+  if ( _settings.userPassword().size() )
+  {
+    SET_OPTION(CURLOPT_USERPWD, _settings.userPassword().c_str());
+    string use_auth = _settings.authType();
+    if (use_auth.empty())
+      use_auth = "digest,basic";       // our default
+    long auth = CurlAuthData::auth_type_str2long(use_auth);
+    if( auth != CURLAUTH_NONE)
+    {
+      DBG << "Enabling HTTP authentication methods: " << use_auth
+         << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
+      SET_OPTION(CURLOPT_HTTPAUTH, auth);
+    }
+  }
+
+  if ( _settings.proxyEnabled() )
+  {
+    if ( ! _settings.proxy().empty() )
+    {
+      SET_OPTION(CURLOPT_PROXY, _settings.proxy().c_str());
+      /*---------------------------------------------------------------*
+        CURLOPT_PROXYUSERPWD: [user name]:[password]
+
+        Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
+        If not provided, $HOME/.curlrc is evaluated
+        *---------------------------------------------------------------*/
+
+      string proxyuserpwd = _settings.proxyUserPassword();
+
+      if ( proxyuserpwd.empty() )
+      {
+        CurlConfig curlconf;
+        CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
+        if (curlconf.proxyuserpwd.empty())
+          DBG << "~/.curlrc does not contain the proxy-user option" << endl;
+        else
+        {
+          proxyuserpwd = curlconf.proxyuserpwd;
+          DBG << "using proxy-user from ~/.curlrc" << endl;
+        }
+      }
+
+      proxyuserpwd = unEscape( proxyuserpwd );
+      if ( ! proxyuserpwd.empty() )
+        SET_OPTION(CURLOPT_PROXYUSERPWD, proxyuserpwd.c_str());
+    }
+  }
+  else
+  {
+#if LIBCURL_VERSION_NUMBER >= 0x071904
+      SET_OPTION(CURLOPT_NOPROXY, "*");
+#endif
+  }
+
+  /** Speed limits */
+  if ( _settings.minDownloadSpeed() != 0 )
+  {
+      SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, _settings.minDownloadSpeed());
+      // default to 10 seconds at low speed
+      SET_OPTION(CURLOPT_LOW_SPEED_TIME, 10L);
+  }
+
+#if LIBCURL_VERSION_NUMBER >= 0x071505
+  if ( _settings.maxDownloadSpeed() != 0 )
+      SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, _settings.maxDownloadSpeed());
+#endif
+
+  /*---------------------------------------------------------------*
+   *---------------------------------------------------------------*/
+
+  _currentCookieFile = _cookieFile.asString();
+  if ( str::strToBool( _url.getQueryParam( "cookies" ), true ) )
+    SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
+  else
+    MIL << "No cookies requested" << endl;
+  SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
+  SET_OPTION(CURLOPT_PROGRESSFUNCTION, &progressCallback );
+  SET_OPTION(CURLOPT_NOPROGRESS, 0L);
+
+#if LIBCURL_VERSION_NUMBER >= 0x071800
+  // bnc #306272
+    SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
+#endif
+  // append settings custom headers to curl
+  for ( TransferSettings::Headers::const_iterator it = vol_settings.headersBegin();
+        it != vol_settings.headersEnd();
+        ++it )
+  {
+      MIL << "HEADER " << *it << std::endl;
+
+      _customHeaders = curl_slist_append(_customHeaders, it->c_str());
+      if ( !_customHeaders )
+          ZYPP_THROW(MediaCurlInitException(_url));
+  }
+
+  SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
+}
+
+///////////////////////////////////////////////////////////////////
+
+
+void MediaCurl::attachTo (bool next)
+{
+  if ( next )
+    ZYPP_THROW(MediaNotSupportedException(_url));
+
+  if ( !_url.isValid() )
+    ZYPP_THROW(MediaBadUrlException(_url));
+
+  checkProtocol(_url);
+  if( !isUseableAttachPoint(attachPoint()))
+  {
+    std::string mountpoint = createAttachPoint().asString();
+
+    if( mountpoint.empty())
+      ZYPP_THROW( MediaBadAttachPointException(url()));
+
+    setAttachPoint( mountpoint, true);
+  }
+
+  disconnectFrom(); // clean _curl if needed
+  _curl = curl_easy_init();
+  if ( !_curl ) {
+    ZYPP_THROW(MediaCurlInitException(_url));
+  }
+  try
+    {
+      setupEasy();
+    }
+  catch (Exception & ex)
+    {
+      disconnectFrom();
+      ZYPP_RETHROW(ex);
+    }
+
+  // FIXME: need a derived class to propelly compare url's
+  MediaSourceRef media( new MediaSource(_url.getScheme(), _url.asString()));
+  setMediaSource(media);
+}
+
+bool
+MediaCurl::checkAttachPoint(const Pathname &apoint) const
+{
+  return MediaHandler::checkAttachPoint( apoint, true, true);
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::disconnectFrom()
+{
+  if ( _customHeaders )
+  {
+    curl_slist_free_all(_customHeaders);
+    _customHeaders = 0L;
+  }
+
+  if ( _curl )
+  {
+    curl_easy_cleanup( _curl );
+    _curl = NULL;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::releaseFrom( const std::string & ejectDev )
+{
+  disconnect();
+}
+
+Url MediaCurl::getFileUrl(const Pathname & filename) const
+{
+  Url newurl(_url);
+  string path = _url.getPathName();
+  if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
+       filename.absolute() )
+  {
+    // If url has a path with trailing slash, remove the leading slash from
+    // the absolute file name
+    path += filename.asString().substr( 1, filename.asString().size() - 1 );
+  }
+  else if ( filename.relative() )
+  {
+    // Add trailing slash to path, if not already there
+    if (path.empty()) path = "/";
+    else if (*path.rbegin() != '/' ) path += "/";
+    // Remove "./" from begin of relative file name
+    path += filename.asString().substr( 2, filename.asString().size() - 2 );
+  }
+  else
+  {
+    path += filename.asString();
+  }
+
+  newurl.setPathName(path);
+  return newurl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::getFile( const Pathname & filename ) const
+{
+    // Use absolute file name to prevent access of files outside of the
+    // hierarchy below the attach point.
+    getFileCopy(filename, localPath(filename).absolutename());
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
+{
+  callback::SendReport<DownloadProgressReport> report;
+
+  Url fileurl(getFileUrl(filename));
+
+  bool retry = false;
+
+  do
+  {
+    try
+    {
+      doGetFileCopy(filename, target, report);
+      retry = false;
+    }
+    // retry with proper authentication data
+    catch (MediaUnauthorizedException & ex_r)
+    {
+      if(authenticate(ex_r.hint(), !retry))
+        retry = true;
+      else
+      {
+        report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
+        ZYPP_RETHROW(ex_r);
+      }
+    }
+    // unexpected exception
+    catch (MediaException & excpt_r)
+    {
+      // FIXME: error number fix
+      report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserHistory());
+      ZYPP_RETHROW(excpt_r);
+    }
+  }
+  while (retry);
+
+  report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
+}
+
+///////////////////////////////////////////////////////////////////
+
+bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
+{
+  bool retry = false;
+
+  do
+  {
+    try
+    {
+      return doGetDoesFileExist( filename );
+    }
+    // authentication problem, retry with proper authentication data
+    catch (MediaUnauthorizedException & ex_r)
+    {
+      if(authenticate(ex_r.hint(), !retry))
+        retry = true;
+      else
+        ZYPP_RETHROW(ex_r);
+    }
+    // unexpected exception
+    catch (MediaException & excpt_r)
+    {
+      ZYPP_RETHROW(excpt_r);
+    }
+  }
+  while (retry);
+
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::evaluateCurlCode( const Pathname &filename,
+                                  CURLcode code,
+                                  bool timeout_reached ) const
+{
+  if ( code != 0 )
+  {
+    Url url;
+    if (filename.empty())
+      url = _url;
+    else
+      url = getFileUrl(filename);
+    std::string err;
+    try
+    {
+      switch ( code )
+      {
+      case CURLE_UNSUPPORTED_PROTOCOL:
+      case CURLE_URL_MALFORMAT:
+      case CURLE_URL_MALFORMAT_USER:
+          err = " Bad URL";
+          break;
+      case CURLE_LOGIN_DENIED:
+          ZYPP_THROW(
+              MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
+          break;
+      case CURLE_HTTP_RETURNED_ERROR:
+      {
+        long httpReturnCode = 0;
+        CURLcode infoRet = curl_easy_getinfo( _curl,
+                                              CURLINFO_RESPONSE_CODE,
+                                              &httpReturnCode );
+        if ( infoRet == CURLE_OK )
+        {
+          string msg = "HTTP response: " + str::numstring( httpReturnCode );
+          switch ( httpReturnCode )
+          {
+          case 401:
+          {
+            string auth_hint = getAuthHint();
+
+            DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
+            DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
+
+            ZYPP_THROW(MediaUnauthorizedException(
+                           url, "Login failed.", _curlError, auth_hint
+                           ));
+          }
+
+          case 503: // service temporarily unavailable (bnc #462545)
+            ZYPP_THROW(MediaTemporaryProblemException(url));
+          case 504: // gateway timeout
+            ZYPP_THROW(MediaTimeoutException(url));
+          case 403:
+          {
+            string msg403;
+            if (url.asString().find("novell.com") != string::npos)
+              msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
+            ZYPP_THROW(MediaForbiddenException(url, msg403));
+          }
+          case 404:
+              ZYPP_THROW(MediaFileNotFoundException(_url, filename));
+          }
+
+          DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
+          ZYPP_THROW(MediaCurlException(url, msg, _curlError));
+        }
+        else
+        {
+          string msg = "Unable to retrieve HTTP response:";
+          DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
+          ZYPP_THROW(MediaCurlException(url, msg, _curlError));
+        }
+      }
+      break;
+      case CURLE_FTP_COULDNT_RETR_FILE:
+#if LIBCURL_VERSION_NUMBER >= 0x071600
+      case CURLE_REMOTE_FILE_NOT_FOUND:
+#endif
+      case CURLE_FTP_ACCESS_DENIED:
+        err = "File not found";
+        ZYPP_THROW(MediaFileNotFoundException(_url, filename));
+        break;
+      case CURLE_BAD_PASSWORD_ENTERED:
+      case CURLE_FTP_USER_PASSWORD_INCORRECT:
+          err = "Login failed";
+          break;
+      case CURLE_COULDNT_RESOLVE_PROXY:
+      case CURLE_COULDNT_RESOLVE_HOST:
+      case CURLE_COULDNT_CONNECT:
+      case CURLE_FTP_CANT_GET_HOST:
+        err = "Connection failed";
+        break;
+      case CURLE_WRITE_ERROR:
+        err = "Write error";
+        break;
+      case CURLE_PARTIAL_FILE:
+      case CURLE_ABORTED_BY_CALLBACK:
+      case CURLE_OPERATION_TIMEDOUT:
+        if( timeout_reached)
+        {
+          err  = "Timeout reached";
+          ZYPP_THROW(MediaTimeoutException(url));
+        }
+        else
+        {
+          err = "User abort";
+        }
+        break;
+      case CURLE_SSL_PEER_CERTIFICATE:
+      default:
+        err = "Unrecognized error";
+        break;
+      }
+
+      // uhm, no 0 code but unknown curl exception
+      ZYPP_THROW(MediaCurlException(url, err, _curlError));
+    }
+    catch (const MediaException & excpt_r)
+    {
+      ZYPP_RETHROW(excpt_r);
+    }
+  }
+  else
+  {
+    // actually the code is 0, nothing happened
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+
+bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
+{
+  DBG << filename.asString() << endl;
+
+  if(!_url.isValid())
+    ZYPP_THROW(MediaBadUrlException(_url));
+
+  if(_url.getHost().empty())
+    ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
+
+  Url url(getFileUrl(filename));
+
+  DBG << "URL: " << url.asString() << endl;
+    // Use URL without options and without username and passwd
+    // (some proxies dislike them in the URL).
+    // Curl seems to need the just scheme, hostname and a path;
+    // the rest was already passed as curl options (in attachTo).
+  Url curlUrl( clearQueryString(url) );
+
+  //
+    // See also Bug #154197 and ftp url definition in RFC 1738:
+    // The url "ftp://user@host/foo/bar/file" contains a path,
+    // that is relative to the user's home.
+    // The url "ftp://user@host//foo/bar/file" (or also with
+    // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
+    // contains an absolute path.
+  //
+  string urlBuffer( curlUrl.asString());
+  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
+                                   urlBuffer.c_str() );
+  if ( ret != 0 ) {
+    ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+  }
+
+  // instead of returning no data with NOBODY, we return
+  // little data, that works with broken servers, and
+  // works for ftp as well, because retrieving only headers
+  // ftp will return always OK code ?
+  // See http://curl.haxx.se/docs/knownbugs.html #58
+  if (  (_url.getScheme() == "http" ||  _url.getScheme() == "https") &&
+        _settings.headRequestsAllowed() )
+    ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
+  else
+    ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
+
+  if ( ret != 0 ) {
+    curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
+    curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
+    /* yes, this is why we never got to get NOBODY working before,
+       because setting it changes this option too, and we also
+       need to reset it
+       See: http://curl.haxx.se/mail/archive-2005-07/0073.html
+    */
+    curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
+    ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+  }
+
+  FILE *file = ::fopen( "/dev/null", "w" );
+  if ( !file ) {
+      ERR << "fopen failed for /dev/null" << endl;
+      curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
+      curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
+      /* yes, this is why we never got to get NOBODY working before,
+       because setting it changes this option too, and we also
+       need to reset it
+       See: http://curl.haxx.se/mail/archive-2005-07/0073.html
+      */
+      curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
+      if ( ret != 0 ) {
+          ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+      }
+      ZYPP_THROW(MediaWriteException("/dev/null"));
+  }
+
+  ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
+  if ( ret != 0 ) {
+      ::fclose(file);
+      std::string err( _curlError);
+      curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
+      curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
+      /* yes, this is why we never got to get NOBODY working before,
+       because setting it changes this option too, and we also
+       need to reset it
+       See: http://curl.haxx.se/mail/archive-2005-07/0073.html
+      */
+      curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
+      if ( ret != 0 ) {
+          ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+      }
+      ZYPP_THROW(MediaCurlSetOptException(url, err));
+  }
+
+  CURLcode ok = curl_easy_perform( _curl );
+  MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
+
+  // reset curl settings
+  if (  _url.getScheme() == "http" ||  _url.getScheme() == "https" )
+  {
+    curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
+    if ( ret != 0 ) {
+      ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+    }
+
+    /* yes, this is why we never got to get NOBODY working before,
+       because setting it changes this option too, and we also
+       need to reset it
+       See: http://curl.haxx.se/mail/archive-2005-07/0073.html
+    */
+    curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L);
+    if ( ret != 0 ) {
+      ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+    }
+
+  }
+  else
+  {
+    // for FTP we set different options
+    curl_easy_setopt( _curl, CURLOPT_RANGE, NULL);
+    if ( ret != 0 ) {
+      ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+    }
+  }
+
+  // if the code is not zero, close the file
+  if ( ok != 0 )
+      ::fclose(file);
+
+  // as we are not having user interaction, the user can't cancel
+  // the file existence checking, a callback or timeout return code
+  // will be always a timeout.
+  try {
+      evaluateCurlCode( filename, ok, true /* timeout */);
+  }
+  catch ( const MediaFileNotFoundException &e ) {
+      // if the file did not exist then we can return false
+      return false;
+  }
+  catch ( const MediaException &e ) {
+      // some error, we are not sure about file existence, rethrw
+      ZYPP_RETHROW(e);
+  }
+  // exists
+  return ( ok == CURLE_OK );
+}
+
+///////////////////////////////////////////////////////////////////
+
+
+#if DETECT_DIR_INDEX
+bool MediaCurl::detectDirIndex() const
+{
+  if(_url.getScheme() != "http" && _url.getScheme() != "https")
+    return false;
+  //
+  // try to check the effective url and set the not_a_file flag
+  // if the url path ends with a "/", what usually means, that
+  // we've received a directory index (index.html content).
+  //
+  // Note: This may be dangerous and break file retrieving in
+  //       case of some server redirections ... ?
+  //
+  bool      not_a_file = false;
+  char     *ptr = NULL;
+  CURLcode  ret = curl_easy_getinfo( _curl,
+                                    CURLINFO_EFFECTIVE_URL,
+                                    &ptr);
+  if ( ret == CURLE_OK && ptr != NULL)
+  {
+    try
+    {
+      Url         eurl( ptr);
+      std::string path( eurl.getPathName());
+      if( !path.empty() && path != "/" && *path.rbegin() == '/')
+      {
+       DBG << "Effective url ("
+           << eurl
+           << ") seems to provide the index of a directory"
+           << endl;
+       not_a_file = true;
+      }
+    }
+    catch( ... )
+    {}
+  }
+  return not_a_file;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+{
+    Pathname dest = target.absolutename();
+    if( assert_dir( dest.dirname() ) )
+    {
+      DBG << "assert_dir " << dest.dirname() << " failed" << endl;
+      Url url(getFileUrl(filename));
+      ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
+    }
+    string destNew = target.asString() + ".new.zypp.XXXXXX";
+    char *buf = ::strdup( destNew.c_str());
+    if( !buf)
+    {
+      ERR << "out of memory for temp file name" << endl;
+      Url url(getFileUrl(filename));
+      ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
+    }
+
+    int tmp_fd = ::mkstemp( buf );
+    if( tmp_fd == -1)
+    {
+      free( buf);
+      ERR << "mkstemp failed for file '" << destNew << "'" << endl;
+      ZYPP_THROW(MediaWriteException(destNew));
+    }
+    destNew = buf;
+    free( buf);
+
+    FILE *file = ::fdopen( tmp_fd, "w" );
+    if ( !file ) {
+      ::close( tmp_fd);
+      filesystem::unlink( destNew );
+      ERR << "fopen failed for file '" << destNew << "'" << endl;
+      ZYPP_THROW(MediaWriteException(destNew));
+    }
+
+    DBG << "dest: " << dest << endl;
+    DBG << "temp: " << destNew << endl;
+
+    // set IFMODSINCE time condition (no download if not modified)
+    if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
+    {
+      curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
+      curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
+    }
+    else
+    {
+      curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+      curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+    }
+    try
+    {
+      doGetFileCopyFile(filename, dest, file, report, options);
+    }
+    catch (Exception &e)
+    {
+      ::fclose( file );
+      filesystem::unlink( destNew );
+      curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+      curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+      ZYPP_RETHROW(e);
+    }
+
+    long httpReturnCode = 0;
+    CURLcode infoRet = curl_easy_getinfo(_curl,
+                                         CURLINFO_RESPONSE_CODE,
+                                         &httpReturnCode);
+    bool modified = true;
+    if (infoRet == CURLE_OK)
+    {
+      DBG << "HTTP response: " + str::numstring(httpReturnCode);
+      if ( httpReturnCode == 304
+           || ( httpReturnCode == 213 && _url.getScheme() == "ftp" ) ) // not modified
+      {
+        DBG << " Not modified.";
+        modified = false;
+      }
+      DBG << endl;
+    }
+    else
+    {
+      WAR << "Could not get the reponse code." << endl;
+    }
+
+    if (modified || infoRet != CURLE_OK)
+    {
+      // apply umask
+      if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
+      {
+        ERR << "Failed to chmod file " << destNew << endl;
+      }
+      if (::fclose( file ))
+      {
+        ERR << "Fclose failed for file '" << destNew << "'" << endl;
+        ZYPP_THROW(MediaWriteException(destNew));
+      }
+      // move the temp file into dest
+      if ( rename( destNew, dest ) != 0 ) {
+        ERR << "Rename failed" << endl;
+        ZYPP_THROW(MediaWriteException(dest));
+      }
+    }
+    else
+    {
+      // close and remove the temp file
+      ::fclose( file );
+      filesystem::unlink( destNew );
+    }
+
+    DBG << "done: " << PathInfo(dest) << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+{
+    DBG << filename.asString() << endl;
+
+    if(!_url.isValid())
+      ZYPP_THROW(MediaBadUrlException(_url));
+
+    if(_url.getHost().empty())
+      ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
+
+    Url url(getFileUrl(filename));
+
+    DBG << "URL: " << url.asString() << endl;
+    // Use URL without options and without username and passwd
+    // (some proxies dislike them in the URL).
+    // Curl seems to need the just scheme, hostname and a path;
+    // the rest was already passed as curl options (in attachTo).
+    Url curlUrl( clearQueryString(url) );
+
+    //
+    // See also Bug #154197 and ftp url definition in RFC 1738:
+    // The url "ftp://user@host/foo/bar/file" contains a path,
+    // that is relative to the user's home.
+    // The url "ftp://user@host//foo/bar/file" (or also with
+    // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
+    // contains an absolute path.
+    //
+    string urlBuffer( curlUrl.asString());
+    CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
+                                     urlBuffer.c_str() );
+    if ( ret != 0 ) {
+      ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+    }
+
+    ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
+    if ( ret != 0 ) {
+      ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
+    }
+
+    // Set callback and perform.
+    ProgressData progressData(_settings.timeout(), url, &report);
+    if (!(options & OPTION_NO_REPORT_START))
+      report->start(url, dest);
+    if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
+      WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
+    }
+
+    ret = curl_easy_perform( _curl );
+
+    if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
+      WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
+    }
+
+    if ( ret != 0 )
+    {
+      ERR << "curl error: " << ret << ": " << _curlError
+          << ", temp file size " << ftell(file)
+          << " bytes." << endl;
+
+      // the timeout is determined by the progress data object
+      // which holds wheter the timeout was reached or not,
+      // otherwise it would be a user cancel
+      try {
+        evaluateCurlCode( filename, ret, progressData.reached);
+      }
+      catch ( const MediaException &e ) {
+        // some error, we are not sure about file existence, rethrw
+        ZYPP_RETHROW(e);
+      }
+    }
+
+#if DETECT_DIR_INDEX
+    if (!ret && detectDirIndex())
+      {
+       ZYPP_THROW(MediaNotAFileException(_url, filename));
+      }
+#endif // DETECT_DIR_INDEX
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
+{
+  filesystem::DirContent content;
+  getDirInfo( content, dirname, /*dots*/false );
+
+  for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
+      Pathname filename = dirname + it->name;
+      int res = 0;
+
+      switch ( it->type ) {
+      case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
+      case filesystem::FT_FILE:
+        getFile( filename );
+        break;
+      case filesystem::FT_DIR: // newer directory.yast contain at least directory info
+        if ( recurse_r ) {
+          getDir( filename, recurse_r );
+        } else {
+          res = assert_dir( localPath( filename ) );
+          if ( res ) {
+            WAR << "Ignore error (" << res <<  ") on creating local directory '" << localPath( filename ) << "'" << endl;
+          }
+        }
+        break;
+      default:
+        // don't provide devices, sockets, etc.
+        break;
+      }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::getDirInfo( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots ) const
+{
+  getDirectoryYast( retlist, dirname, dots );
+}
+
+///////////////////////////////////////////////////////////////////
+
+void MediaCurl::getDirInfo( filesystem::DirContent & retlist,
+                            const Pathname & dirname, bool dots ) const
+{
+  getDirectoryYast( retlist, dirname, dots );
+}
+
+///////////////////////////////////////////////////////////////////
+
+int MediaCurl::progressCallback( void *clientp,
+                                 double dltotal, double dlnow,
+                                 double ultotal, double ulnow)
+{
+  ProgressData *pdata = reinterpret_cast<ProgressData *>(clientp);
+  if( pdata)
+  {
+    time_t now   = time(NULL);
+    if( now > 0)
+    {
+       // reset time of last change in case initial time()
+       // failed or the time was adjusted (goes backward)
+       if( pdata->ltime <= 0 || pdata->ltime > now)
+       {
+         pdata->ltime = now;
+       }
+
+       // start time counting as soon as first data arrives
+       // (skip the connection / redirection time at begin)
+       time_t dif = 0;
+       if (dlnow > 0 || ulnow > 0)
+       {
+         dif = (now - pdata->ltime);
+         dif = dif > 0 ? dif : 0;
+
+         pdata->secs += dif;
+       }
+
+       // update the drate_avg and drate_period only after a second has passed
+       // (this callback is called much more often than a second)
+       // otherwise the values would be far from accurate when measuring
+       // the time in seconds
+       //! \todo more accurate download rate computationn, e.g. compute average value from last 5 seconds, or work with milliseconds instead of seconds
+
+        if ( pdata->secs > 1 && (dif > 0 || dlnow == dltotal ))
+          pdata->drate_avg = (dlnow / pdata->secs);
+
+       if ( dif > 0 )
+       {
+         pdata->drate_period = ((dlnow - pdata->dload_period) / dif);
+         pdata->dload_period = dlnow;
+       }
+    }
+
+    // send progress report first, abort transfer if requested
+    if( pdata->report)
+    {
+      if (!(*(pdata->report))->progress(int( dltotal ? dlnow * 100 / dltotal : 0 ),
+                                       pdata->url,
+                                       pdata->drate_avg,
+                                       pdata->drate_period))
+      {
+        return 1; // abort transfer
+      }
+    }
+
+    // check if we there is a timeout set
+    if( pdata->timeout > 0)
+    {
+      if( now > 0)
+      {
+        bool progress = false;
+
+        // update download data if changed, mark progress
+        if( dlnow != pdata->dload)
+        {
+          progress     = true;
+          pdata->dload = dlnow;
+          pdata->ltime = now;
+        }
+        // update upload data if changed, mark progress
+        if( ulnow != pdata->uload)
+        {
+          progress     = true;
+          pdata->uload = ulnow;
+          pdata->ltime = now;
+        }
+
+        if( !progress && (now >= (pdata->ltime + pdata->timeout)))
+        {
+          pdata->reached = true;
+          return 1; // aborts transfer
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////
+
+string MediaCurl::getAuthHint() const
+{
+  long auth_info = CURLAUTH_NONE;
+
+  CURLcode infoRet =
+    curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
+
+  if(infoRet == CURLE_OK)
+  {
+    return CurlAuthData::auth_type_long2str(auth_info);
+  }
+
+  return "";
+}
+
+///////////////////////////////////////////////////////////////////
+
+bool MediaCurl::authenticate(const string & availAuthTypes, bool firstTry) const
+{
+  //! \todo need a way to pass different CredManagerOptions here
+  Target_Ptr target = zypp::getZYpp()->getTarget();
+  CredentialManager cm(CredManagerOptions(target ? target->root() : ""));
+  CurlAuthData_Ptr credentials;
+
+  // get stored credentials
+  AuthData_Ptr cmcred = cm.getCred(_url);
+
+  if (cmcred && firstTry)
+  {
+    credentials.reset(new CurlAuthData(*cmcred));
+    DBG << "got stored credentials:" << endl << *credentials << endl;
+  }
+  // if not found, ask user
+  else
+  {
+
+    CurlAuthData_Ptr curlcred;
+    curlcred.reset(new CurlAuthData());
+    callback::SendReport<AuthenticationReport> auth_report;
+
+    // preset the username if present in current url
+    if (!_url.getUsername().empty() && firstTry)
+      curlcred->setUsername(_url.getUsername());
+    // if CM has found some credentials, preset the username from there
+    else if (cmcred)
+      curlcred->setUsername(cmcred->username());
+
+    // indicate we have no good credentials from CM
+    cmcred.reset();
+
+    string prompt_msg = boost::str(boost::format(
+      //!\todo add comma to the message for the next release
+      _("Authentication required for '%s'")) % _url.asString());
+
+    // set available authentication types from the exception
+    // might be needed in prompt
+    curlcred->setAuthType(availAuthTypes);
+
+    // ask user
+    if (auth_report->prompt(_url, prompt_msg, *curlcred))
+    {
+      DBG << "callback answer: retry" << endl
+          << "CurlAuthData: " << *curlcred << endl;
+
+      if (curlcred->valid())
+      {
+        credentials = curlcred;
+          // if (credentials->username() != _url.getUsername())
+          //   _url.setUsername(credentials->username());
+          /**
+           *  \todo find a way to save the url with changed username
+           *  back to repoinfo or dont store urls with username
+           *  (and either forbid more repos with the same url and different
+           *  user, or return a set of credentials from CM and try them one
+           *  by one)
+           */
+      }
+    }
+    else
+    {
+      DBG << "callback answer: cancel" << endl;
+    }
+  }
+
+  // set username and password
+  if (credentials)
+  {
+    // HACK, why is this const?
+    const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
+    const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
+
+    // set username and password
+    CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
+    if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
+
+    // set available authentication types from the exception
+    if (credentials->authType() == CURLAUTH_NONE)
+      credentials->setAuthType(availAuthTypes);
+
+    // set auth type (seems this must be set _after_ setting the userpwd)
+    if (credentials->authType() != CURLAUTH_NONE)
+    {
+      // FIXME: only overwrite if not empty?
+      const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
+      ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
+      if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
+    }
+
+    if (!cmcred)
+    {
+      credentials->setUrl(_url);
+      cm.addCred(*credentials);
+      cm.save();
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+
+  } // namespace media
+} // namespace zypp
+//
diff --git a/zypp/media/MediaCurl.h b/zypp/media/MediaCurl.h
new file mode 100644 (file)
index 0000000..48a8dcf
--- /dev/null
@@ -0,0 +1,182 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaCurl.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIACURL_H
+#define ZYPP_MEDIA_MEDIACURL_H
+
+#include "zypp/base/Flags.h"
+#include "zypp/media/TransferSettings.h"
+#include "zypp/media/MediaHandler.h"
+#include "zypp/ZYppCallbacks.h"
+
+#include <curl/curl.h>
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaCurl
+/**
+ * @short Implementation class for FTP, HTTP and HTTPS MediaHandler
+ * @see MediaHandler
+ **/
+class MediaCurl : public MediaHandler
+{
+  public:
+    enum RequestOption
+    {
+        /** Defaults */
+        OPTION_NONE = 0x0,
+        /** retrieve only a range of the file */
+        OPTION_RANGE = 0x1,
+        /** only issue a HEAD (or equivalent) request */
+        OPTION_HEAD = 0x02,
+        /** to not add a IFMODSINCE header if target exists */
+        OPTION_NO_IFMODSINCE = 0x04,
+        /** do not send a start ProgressReport */
+        OPTION_NO_REPORT_START = 0x08,
+    };
+    ZYPP_DECLARE_FLAGS(RequestOptions,RequestOption);
+
+  protected:
+
+    Url clearQueryString(const Url &url) const;
+
+    virtual void attachTo (bool next = false);
+    virtual void releaseFrom( const std::string & ejectDev );
+    virtual void getFile( const Pathname & filename ) const;
+    virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+    virtual void getDirInfo( std::list<std::string> & retlist,
+                             const Pathname & dirname, bool dots = true ) const;
+    virtual void getDirInfo( filesystem::DirContent & retlist,
+                             const Pathname & dirname, bool dots = true ) const;
+    /**
+     * Repeatedly calls doGetDoesFileExist() until it successfully returns,
+     * fails unexpectedly, or user cancels the operation. This is used to
+     * handle authentication or similar retry scenarios on media level.
+     */
+    virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+    /**
+     * \see MediaHandler::getDoesFileExist
+     */
+    virtual bool doGetDoesFileExist( const Pathname & filename ) const;
+
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void disconnectFrom();
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void getFileCopy( const Pathname & srcFilename, const Pathname & targetFilename) const;
+
+    /**
+     *
+     * \throws MediaException
+     *
+     */
+    virtual void doGetFileCopy( const Pathname & srcFilename, const Pathname & targetFilename, callback::SendReport<DownloadProgressReport> & _report, RequestOptions options = OPTION_NONE ) const;
+
+
+    virtual bool checkAttachPoint(const Pathname &apoint) const;
+
+  public:
+
+    MediaCurl( const Url &      url_r,
+              const Pathname & attach_point_hint_r );
+
+    virtual ~MediaCurl() { try { release(); } catch(...) {} }
+
+    TransferSettings & settings();
+
+    static void setCookieFile( const Pathname & );
+
+    class Callbacks
+    {
+      public:
+       virtual ~Callbacks() {}
+        virtual bool progress( int percent ) = 0;
+    };
+
+  protected:
+
+    static int progressCallback( void *clientp, double dltotal, double dlnow,
+                                 double ultotal, double ulnow );
+    /**
+     * check the url is supported by the curl library
+     * \throws MediaBadUrlException if there is a problem
+     **/
+    void checkProtocol(const Url &url) const;
+
+    /**
+     * initializes the curl easy handle with the data from the url
+     * \throws MediaCurlSetOptException if there is a problem
+     **/
+    virtual void setupEasy();
+    /**
+     * concatenate the attach url and the filename to a complete
+     * download url
+     **/
+    Url getFileUrl(const Pathname & filename) const;
+
+    /**
+     * Evaluates a curl return code and throws the right MediaException
+     * \p filename Filename being downloaded
+     * \p code Code curl returnes
+     * \p timeout Whether we reached timeout, which we need to differentiate
+     *    in case the codes aborted-by-callback or timeout are returned by curl
+     *    Otherwise we can't differentiate abort from timeout. Here you may
+     *    want to pass the progress data object timeout-reached value, or
+     *    just true if you are not doing user interaction.
+     *
+     * \throws MediaException If there is a problem
+     */
+    void evaluateCurlCode( const zypp::Pathname &filename, CURLcode code, bool timeout ) const;
+
+    void doGetFileCopyFile( const Pathname & srcFilename, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & _report, RequestOptions options = OPTION_NONE ) const;
+
+  private:
+    /**
+     * Return a comma separated list of available authentication methods
+     * supported by server.
+     */
+    std::string getAuthHint() const;
+
+    bool authenticate(const std::string & availAuthTypes, bool firstTry) const;
+
+    bool detectDirIndex() const;
+
+  private:
+    long _curlDebug;
+
+    std::string _currentCookieFile;
+    static Pathname _cookieFile;
+
+  protected:
+    CURL *_curl;
+    char _curlError[ CURL_ERROR_SIZE ];
+    curl_slist *_customHeaders;
+    TransferSettings _settings;
+};
+ZYPP_DECLARE_OPERATORS_FOR_FLAGS(MediaCurl::RequestOptions);
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIACURL_H
diff --git a/zypp/media/MediaDIR.cc b/zypp/media/MediaDIR.cc
new file mode 100644 (file)
index 0000000..4f8288e
--- /dev/null
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaDIR.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/media/MediaDIR.h"
+
+#include <sys/mount.h>
+#include <errno.h>
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaDIR
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::MediaDIR
+    // METHOD TYPE : Constructor
+    //
+    // DESCRIPTION : Attach point is always url_r.getPathName(),
+    //                as files are not copied.
+    //                Thus attach_point_hint_r is ignored.
+    //
+    MediaDIR::MediaDIR( const Url &      url_r,
+                       const Pathname & /*attach_point_hint_r*/ )
+        : MediaHandler( url_r, url_r.getPathName(),
+                   "/",    // urlpath below attachpoint
+                   false ) // does_download
+    {
+       MIL << "MediaDIR::MediaDIR(" << url_r << ")" << endl;
+       if( !url_r.getHost().empty())
+       {
+         ZYPP_THROW(MediaBadUrlException(url_r,
+           "Hostname not allowed in the Url"
+         ));
+       }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::attachTo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+    //
+    void MediaDIR::attachTo(bool next)
+    {
+      if(next)
+       ZYPP_THROW(MediaNotSupportedException(url()));
+
+      // fetch attach point from url again if needed ...
+      // it may happen that attachPointHint (and attachPoint())
+      // does not contain any path, because the directory has
+      // not existed while the handler was constructed.
+      if( attachPoint().empty() && !url().getPathName().empty())
+      {
+       Pathname real( getRealPath(url().getPathName()));
+
+       PathInfo adir( real);
+       if( adir.isDir())
+       {
+         // set attachpoint only if the dir exists
+         setAttachPoint( real, false);
+       }
+       else
+       {
+          ZYPP_THROW(MediaBadUrlException(url(),
+            "Specified path '" + url().getPathName() + "' is not a directory"
+         ));
+       }
+      }
+
+      // attach point is same as source path... we do not mount here
+      if(attachPoint().empty())
+      {
+        ZYPP_THROW(MediaBadUrlException(url(),
+          "The media URL does not provide any useable directory path"
+       ));
+      }
+      else
+      if(!PathInfo(attachPoint()).isDir())
+      {
+        ZYPP_THROW(MediaBadUrlException(url(),
+         "Specified path '" + attachPoint().asString() + "' is not a directory"
+       ));
+      }
+
+      MediaSourceRef media(new MediaSource("dir", attachPoint().asString()));
+      setMediaSource(media);
+    }
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::releaseFrom
+    // METHOD TYPE : void
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDIR::releaseFrom( const std::string & ejectDev )
+    {
+      return;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::getFile
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDIR::getFile( const Pathname & filename ) const
+    {
+      MediaHandler::getFile( filename );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaDIR::getDir
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDIR::getDir( const Pathname & dirname, bool recurse_r ) const
+    {
+      MediaHandler::getDir( dirname, recurse_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaDIR::getDirInfo( std::list<std::string> & retlist,
+                              const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDIR::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaDIR::getDirInfo( filesystem::DirContent & retlist,
+                              const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    bool MediaDIR::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaDIR.h b/zypp/media/MediaDIR.h
new file mode 100644 (file)
index 0000000..2f99de8
--- /dev/null
@@ -0,0 +1,55 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaDIR.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIADIR_H
+#define ZYPP_MEDIA_MEDIADIR_H
+
+#include "zypp/media/MediaHandler.h"
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaDIR
+
+    /**
+     * @short Implementation class for DIR MediaHandler
+     * @see MediaHandler
+     **/
+    class MediaDIR : public MediaHandler {
+
+      protected:
+
+       virtual void attachTo (bool next = false);
+        virtual void releaseFrom( const std::string & ejectDev );
+       virtual void getFile( const Pathname & filename ) const;
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+      public:
+
+        MediaDIR( const Url &      url_r,
+                 const Pathname & attach_point_hint_r );
+
+        virtual ~MediaDIR() { try { release(); } catch(...) {} }
+    };
+
+    ///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIADIR_H
diff --git a/zypp/media/MediaDISK.cc b/zypp/media/MediaDISK.cc
new file mode 100644 (file)
index 0000000..eac00d5
--- /dev/null
@@ -0,0 +1,411 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaDISK.cc
+ *
+*/
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/media/Mount.h"
+#include "zypp/media/MediaDISK.h"
+#include "zypp/media/MediaManager.h"
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <dirent.h>
+
+/*
+** verify devices names as late as possible (while attach)
+*/
+#define DELAYED_VERIFY           1
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaDISK
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDISK::MediaDISK
+    // METHOD TYPE : Constructor
+    //
+    // DESCRIPTION :
+    //
+    MediaDISK::MediaDISK( const Url &      url_r,
+                         const Pathname & attach_point_hint_r )
+        : MediaHandler( url_r, attach_point_hint_r,
+                   url_r.getPathName(), // urlpath below attachpoint
+                   false ) // does_download
+    {
+      MIL << "MediaDISK::MediaDISK(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+
+      _device = Pathname(_url.getQueryParam("device")).asString();
+      if( _device.empty())
+      {
+       ERR << "Media url does not contain a device specification" << std::endl;
+       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+      }
+#if DELAYED_VERIFY
+      DBG << "Verify of " << _device << " delayed" << std::endl;
+#else
+      if( !verifyIfDiskVolume( _device))
+      {
+       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+      }
+#endif
+
+      _filesystem = _url.getQueryParam("filesystem");
+      if(_filesystem.empty())
+       _filesystem="auto";
+
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaDISK::verifyIfDiskVolume
+    // METHOD TYPE : void
+    //
+    // DESCRIPTION : Check if specified device file name is
+    //                a disk volume device or throw an error.
+    //
+    bool MediaDISK::verifyIfDiskVolume(const Pathname &dev_name)
+    {
+      if( dev_name.empty() ||
+         dev_name.asString().compare(0, sizeof("/dev/")-1, "/dev/"))
+      {
+       ERR << "Specified device name " << dev_name
+           << " is not allowed" << std::endl;
+       return false;
+      }
+
+      PathInfo dev_info(dev_name);
+      if( !dev_info.isBlk())
+      {
+       ERR << "Specified device name " << dev_name
+           << " is not a block device" << std::endl;
+       return false;
+      }
+
+      // check if a volume using /dev/disk/by-uuid links first
+      {
+       Pathname            dpath("/dev/disk/by-uuid");
+       std::list<Pathname> dlist;
+       if( zypp::filesystem::readdir(dlist, dpath) == 0)
+       {
+         std::list<Pathname>::const_iterator it;
+         for(it = dlist.begin(); it != dlist.end(); ++it)
+         {
+           PathInfo vol_info(*it);
+           if( vol_info.isBlk() && vol_info.major() == dev_info.major() &&
+                                   vol_info.minor() == dev_info.minor())
+           {
+             DBG << "Specified device name " << dev_name
+                 << " is a volume (disk/by-uuid link "
+                 << vol_info.path() << ")"
+                 << std::endl;
+             return true;
+           }
+         }
+       }
+      }
+
+      // check if a volume using /dev/disk/by-label links
+      // (e.g. vbd mapped volumes in a XEN vm)
+      {
+       Pathname            dpath("/dev/disk/by-label");
+       std::list<Pathname> dlist;
+       if( zypp::filesystem::readdir(dlist, dpath) == 0)
+       {
+         std::list<Pathname>::const_iterator it;
+         for(it = dlist.begin(); it != dlist.end(); ++it)
+         {
+           PathInfo vol_info(*it);
+           if( vol_info.isBlk() && vol_info.major() == dev_info.major() &&
+                                   vol_info.minor() == dev_info.minor())
+           {
+             DBG << "Specified device name " << dev_name
+                 << " is a volume (disk/by-label link "
+                 << vol_info.path() << ")"
+                 << std::endl;
+             return true;
+           }
+         }
+       }
+      }
+
+      // check if a filesystem volume using the 'blkid' tool
+      // (there is no /dev/disk link for some of them)
+      ExternalProgram::Arguments args;
+      args.push_back( "blkid" );
+      args.push_back( "-p" );
+      args.push_back( dev_name.asString() );
+
+      ExternalProgram cmd( args, ExternalProgram::Stderr_To_Stdout );
+      cmd >> DBG;
+      if ( cmd.close() != 0 )
+      {
+       ERR << cmd.execError() << endl
+           << "Specified device name " << dev_name
+           << " is not a usable disk volume"
+           << std::endl;
+       return false;
+      }
+      return true;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDISK::attachTo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+    //
+    void MediaDISK::attachTo(bool next)
+    {
+      if(next)
+       ZYPP_THROW(MediaNotSupportedException(url()));
+      // FIXME
+      // do mount --bind <partition>/<dir> to <to>
+      //   mount /dev/<partition> /tmp_mount
+      //   mount /tmp_mount/<dir> <to> --bind -o ro
+      // FIXME: try all filesystems
+
+      if(_device.empty())
+       ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
+
+      PathInfo dev_info(_device);
+      if(!dev_info.isBlk())
+        ZYPP_THROW(MediaBadUrlEmptyDestinationException(url()));
+#if DELAYED_VERIFY
+      DBG << "Verifying " << _device << " ..." << std::endl;
+      if( !verifyIfDiskVolume( _device))
+      {
+       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+      }
+#endif
+
+      if(_filesystem.empty())
+       ZYPP_THROW(MediaBadUrlEmptyFilesystemException(url()));
+
+      MediaSourceRef media( new MediaSource(
+       "disk", _device, dev_info.major(), dev_info.minor()
+      ));
+      AttachedMedia  ret( findAttachedMedia( media));
+
+      if( ret.mediaSource &&
+         ret.attachPoint &&
+         !ret.attachPoint->empty())
+      {
+       DBG << "Using a shared media "
+           << ret.mediaSource->name
+           << " attached on "
+           << ret.attachPoint->path
+           << endl;
+
+       removeAttachPoint();
+       setAttachPoint(ret.attachPoint);
+       setMediaSource(ret.mediaSource);
+       return;
+      }
+
+      MediaManager  manager;
+      MountEntries  entries( manager.getMountEntries());
+      MountEntries::const_iterator e;
+      for( e = entries.begin(); e != entries.end(); ++e)
+      {
+       bool        is_device = false;
+       std::string dev_path(Pathname(e->src).asString());
+       PathInfo    dev_info;
+
+       if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
+           dev_info(e->src) && dev_info.isBlk())
+       {
+         is_device = true;
+       }
+
+       if( is_device && media->maj_nr == dev_info.major() &&
+                        media->min_nr == dev_info.minor())
+       {
+         AttachPointRef ap( new AttachPoint(e->dir, false));
+         AttachedMedia  am( media, ap);
+         {
+           DBG << "Using a system mounted media "
+               << media->name
+               << " attached on "
+               << ap->path
+               << endl;
+
+           media->iown = false; // mark attachment as foreign
+
+           setMediaSource(media);
+           setAttachPoint(ap);
+           return;
+         }
+       }
+      }
+
+      Mount mount;
+      std::string mountpoint = attachPoint().asString();
+      if( !isUseableAttachPoint(attachPoint()))
+      {
+       mountpoint = createAttachPoint().asString();
+       if( mountpoint.empty())
+         ZYPP_THROW( MediaBadAttachPointException(url()));
+       setAttachPoint( mountpoint, true);
+      }
+
+      string options = _url.getQueryParam("mountoptions");
+      if(options.empty())
+      {
+       options = "ro";
+      }
+
+      if( !media->bdir.empty())
+      {
+       options += ",bind";
+       mount.mount(media->bdir, mountpoint, "none", options);
+      }
+      else
+      {
+       mount.mount(_device, mountpoint, _filesystem, options);
+      }
+
+      setMediaSource(media);
+
+      // wait for /etc/mtab update ...
+      // (shouldn't be needed)
+      int limit = 3;
+      bool mountsucceeded;
+      while( !(mountsucceeded=isAttached()) && --limit)
+      {
+        sleep(1);
+      }
+
+      if( !mountsucceeded)
+      {
+        setMediaSource(MediaSourceRef());
+        try
+        {
+          mount.umount(attachPoint().asString());
+        }
+        catch (const MediaException & excpt_r)
+        {
+          ZYPP_CAUGHT(excpt_r);
+        }
+        ZYPP_THROW(MediaMountException(
+          "Unable to verify that the media was mounted",
+         _device, mountpoint
+        ));
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaDISK::isAttached
+    // METHOD TYPE : bool
+    //
+    // DESCRIPTION : Override check if media is attached.
+    //
+    bool
+    MediaDISK::isAttached() const
+    {
+      return checkAttached(false);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDISK::releaseFrom
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDISK::releaseFrom( const std::string & ejectDev )
+    {
+      AttachedMedia am( attachedMedia());
+      if(am.mediaSource && am.mediaSource->iown)
+      {
+        Mount mount;
+        mount.umount(attachPoint().asString());
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaDISK::getFile
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDISK::getFile (const Pathname & filename) const
+    {
+      MediaHandler::getFile( filename );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaDISK::getDir
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaDISK::getDir( const Pathname & dirname, bool recurse_r ) const
+    {
+      MediaHandler::getDir( dirname, recurse_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDISK::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaDISK::getDirInfo( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaDISK::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaDISK::getDirInfo( filesystem::DirContent & retlist,
+                               const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    bool MediaDISK::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
+
+  } // namespace media
+} // namespace zypp
+// vim: set ts=8 sts=2 sw=2 ai noet:
diff --git a/zypp/media/MediaDISK.h b/zypp/media/MediaDISK.h
new file mode 100644 (file)
index 0000000..8427611
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaDISK.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIADISK_H
+#define ZYPP_MEDIA_MEDIADISK_H
+
+#include "zypp/media/MediaHandler.h"
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaDISK
+    /**
+     * @short Implementation class for DISK MediaHandler
+     * @see MediaHandler
+     **/
+    class MediaDISK : public MediaHandler {
+
+      private:
+
+        unsigned long _mountflags;
+
+        std::string _device;
+        std::string _filesystem;
+
+      protected:
+
+       virtual void attachTo (bool next = false);
+        virtual void releaseFrom( const std::string & ejectDev );
+       virtual void getFile( const Pathname & filename ) const;
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+      public:
+
+        MediaDISK( const Url &      url_r,
+                  const Pathname & attach_point_hint_r );
+
+        virtual ~MediaDISK() { try { release(); } catch(...) {} }
+
+        virtual bool isAttached() const;
+
+        bool    verifyIfDiskVolume(const Pathname &name);
+    };
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIADISK_H
diff --git a/zypp/media/MediaException.cc b/zypp/media/MediaException.cc
new file mode 100644 (file)
index 0000000..9cf3839
--- /dev/null
@@ -0,0 +1,292 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaException.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+
+#include "zypp/media/MediaException.h"
+
+using namespace std;
+using zypp::str::form;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace media {
+  /////////////////////////////////////////////////////////////////
+
+    std::ostream & MediaMountException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_("Failed to mount %s on %s"), _source.c_str(), _target.c_str());
+      if( !_cmdout.empty())
+       str << ": " << _error << " (" << _cmdout << ")" << endl;
+      else
+       str << ": " << _error << endl;
+      return str;
+    }
+
+    std::ostream & MediaUnmountException::dumpOn( std::ostream & str ) const
+    {
+      return str << form(_("Failed to unmount %s"), _path.c_str())
+       << " : " << _error << endl;
+    }
+
+    std::ostream & MediaBadFilenameException::dumpOn( std::ostream & str ) const
+    {
+      return str << form(_("Bad file name: %s"), _filename.c_str()) << endl;
+    }
+
+    std::ostream & MediaNotOpenException::dumpOn( std::ostream & str ) const
+    {
+      return str << form(
+        _("Medium not opened when trying to perform action '%s'."), _action.c_str())
+       << endl;
+    }
+
+    std::ostream & MediaFileNotFoundException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        _("File '%s' not found on medium '%s'"),
+        _filename.c_str(), _url.c_str())
+        << endl;
+    }
+
+    std::ostream & MediaWriteException::dumpOn( std::ostream & str) const
+    {
+      return str << form(_("Cannot write file '%s'."), _filename.c_str()) << endl;
+    }
+
+    std::ostream & MediaNotAttachedException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Medium not attached") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaBadAttachPointException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Bad media attach point") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaCurlInitException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Download (curl) initialization failed for '%s'"), _url.c_str())
+        << endl;
+    }
+
+   std::ostream & MediaMetalinkInitException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Download (Metalink curl) initialization failed for '%s'"), _url.c_str())
+        << endl;
+    }
+
+   std::ostream & MediaAria2cInitException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Download (Metalink curl) initialization failed for '%s'"), _url.c_str())
+        << endl;
+    }
+
+    std::ostream & MediaSystemException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        _("System exception '%s' on medium '%s'."),
+        _message.c_str(), _url.c_str()) << endl;
+    }
+
+    std::ostream & MediaNotAFileException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        _("Path '%s' on medium '%s' is not a file."),
+        _path.c_str(), _url.c_str())
+        << endl;
+    }
+
+    std::ostream & MediaNotADirException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        _("Path '%s' on medium '%s' is not a directory."),
+        _path.c_str(), _url.c_str())
+        << endl;
+    }
+
+    std::ostream & MediaBadUrlException::dumpOn( std::ostream & str) const
+    {
+      if( _msg.empty())
+      {
+       return str << _("Malformed URI") << ": " << _url << endl;
+      }
+      else
+      {
+       return str << _msg << ": " << _url << endl;
+      }
+    }
+
+    std::ostream & MediaBadUrlEmptyHostException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Empty host name in URI") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaBadUrlEmptyFilesystemException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Empty filesystem in URI") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaBadUrlEmptyDestinationException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Empty destination in URI") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaUnsupportedUrlSchemeException::dumpOn( std::ostream & str) const
+    {
+      return str << form(_("Unsupported URI scheme in '%s'."), _url.c_str()) << endl;
+    }
+
+    std::ostream & MediaNotSupportedException::dumpOn( std::ostream & str) const
+    {
+      return str << _("Operation not supported by medium") << ": " << _url << endl;
+    }
+
+    std::ostream & MediaCurlException::dumpOn( std::ostream & str) const
+    {
+      // TranslatorExplanation: curl is the name of a library, don't translate
+      return str << form(_(
+        "Download (curl) error for '%s':\n"
+        "Error code: %s\n"
+        "Error message: %s\n"), _url.c_str(), _err.c_str(), _msg.c_str());
+    }
+
+    std::ostream & MediaCurlSetOptException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Error occurred while setting download (curl) options for '%s':"),
+        _url.c_str())
+       << endl << _msg << endl;
+    }
+
+   std::ostream & MediaMetalinkException::dumpOn( std::ostream & str) const
+    {
+      // TranslatorExplanation: curl is the name of a library, don't translate
+      return str << form(_(
+        "Download (metalink curl) error for '%s':\n"
+        "Error code: %s\n"
+        "Error message: %s\n"), _url.c_str(), _err.c_str(), _msg.c_str());
+    }
+
+    std::ostream & MediaMetalinkSetOptException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Error occurred while setting download (metalink curl) options for '%s':"),
+        _url.c_str())
+       << endl << _msg << endl;
+    }
+
+   std::ostream & MediaAria2cException::dumpOn( std::ostream & str) const
+    {
+      // TranslatorExplanation: curl is the name of a library, don't translate
+      return str << form(_(
+        "Download (metalink curl) error for '%s':\n"
+        "Error code: %s\n"
+        "Error message: %s\n"), _url.c_str(), _err.c_str(), _msg.c_str());
+    }
+
+    std::ostream & MediaAria2cSetOptException::dumpOn( std::ostream & str) const
+    {
+      return str << form(
+        // TranslatorExplanation: curl is the name of a library, don't translate
+        _("Error occurred while setting download (metalink curl) options for '%s':"),
+        _url.c_str())
+       << endl << _msg << endl;
+    }
+
+    std::ostream & MediaNotDesiredException::dumpOn( std::ostream & str ) const
+    {
+      return str << form(
+        _("Media source '%s' does not contain the desired medium"), _url.c_str())
+        << endl;
+    }
+
+    std::ostream & MediaIsSharedException::dumpOn( std::ostream & str ) const
+    {
+      return str << form(_("Medium '%s' is in use by another instance"), _name.c_str())
+                 << endl;
+    }
+
+    std::ostream & MediaNotEjectedException::dumpOn( std::ostream & str ) const
+    {
+      if( _name.empty())
+       return str << _("Cannot eject any media") << endl;
+      else
+       return str << form(_("Cannot eject media '%s'"), _name.c_str()) << endl;
+    }
+
+    std::ostream & MediaUnauthorizedException::dumpOn( std::ostream & str ) const
+    {
+      str << msg();
+      if( !_url.asString().empty())
+        str << " (" << _url << ")";
+      if( !_err.empty())
+        str << ": " << _err;
+      return str;
+    }
+
+    std::ostream & MediaForbiddenException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_("Permission to access '%s' denied."), _url.c_str()) << endl;
+      if ( !_msg.empty() )
+        str << endl << _msg << endl;
+      return str;
+    }
+
+    std::ostream & MediaTimeoutException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_("Timeout exceeded when accessing '%s'."), _url.c_str()) << endl;
+      if ( !_msg.empty() )
+        str << endl << _msg << endl;
+      return str;
+    }
+
+    std::ostream & MediaTemporaryProblemException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_("Location '%s' is temporarily unaccessible."), _url.c_str()) << endl;
+      if ( !_msg.empty() )
+        str << endl << _msg << endl;
+      return str;
+    }
+
+    std::ostream & MediaBadCAException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_(" SSL certificate problem, verify that the CA cert is OK for '%s'."), _url.c_str()) << endl;
+      if ( !_msg.empty() )
+        str << endl << _msg << endl;
+      return str;
+    }
+
+    std::ostream & MediaNoLoopDeviceException::dumpOn( std::ostream & str ) const
+    {
+      str << form(_("Cannot find available loop device to mount the image file from '%s'"), _url.c_str()) << endl;
+      if ( !_msg.empty() )
+        str << endl << _msg << endl;
+      return str;
+    }
+
+  /////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/media/MediaException.h b/zypp/media/MediaException.h
new file mode 100644 (file)
index 0000000..40b4c73
--- /dev/null
@@ -0,0 +1,640 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaException.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAEXCEPTION_H
+#define ZYPP_MEDIA_MEDIAEXCEPTION_H
+
+#include <iosfwd>
+
+#include <string>
+#include <vector>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace media {
+    ///////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaException
+    /** Just inherits Exception to separate media exceptions
+     *
+     **/
+    class MediaException : public Exception
+    {
+    public:
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      MediaException() : Exception( "Media Exception" )
+      {}
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      MediaException( const std::string & msg_r )
+      : Exception( msg_r )
+      {}
+
+      /** Dtor. */
+      virtual ~MediaException() throw() {};
+    };
+
+    class MediaMountException : public MediaException
+    {
+    public:
+      MediaMountException()
+      : MediaException( "Media Mount Exception" )
+      {}
+
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      MediaMountException( const std::string & error_r,
+                          const std::string & source_r,
+                          const std::string & target_r,
+                          const std::string & cmdout_r="")
+      : MediaException()
+      , _error(error_r)
+      , _source(source_r)
+      , _target(target_r)
+      , _cmdout(cmdout_r)
+      {}
+      /** Dtor. */
+      virtual ~MediaMountException() throw() {};
+
+      const std::string & mountError() const
+      { return _error;  }
+      const std::string & mountSource() const
+      { return _source; }
+      const std::string & mountTarget() const
+      { return _target; }
+      const std::string & mountOutput() const
+      { return _cmdout; }
+
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _error;
+      std::string _source;
+      std::string _target;
+      std::string _cmdout;
+    };
+
+    class MediaUnmountException : public MediaException
+    {
+    public:
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      MediaUnmountException( const std::string & error_r,
+                            const std::string & path_r )
+      : MediaException()
+      , _error(error_r)
+      , _path(path_r)
+      {}
+      /** Dtor. */
+      virtual ~MediaUnmountException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _error;
+      std::string _path;
+    };
+
+    class MediaBadFilenameException : public MediaException
+    {
+    public:
+      MediaBadFilenameException(const std::string & filename_r)
+      : MediaException()
+      , _filename(filename_r)
+      {}
+      virtual ~MediaBadFilenameException() throw() {};
+      std::string filename() const { return _filename; }
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _filename;
+    };
+
+    class MediaNotOpenException : public MediaException
+    {
+    public:
+      MediaNotOpenException(const std::string & action_r)
+      : MediaException()
+      , _action(action_r)
+      {}
+      virtual ~MediaNotOpenException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _action;
+    };
+
+    class MediaFileNotFoundException : public MediaException
+    {
+    public:
+      MediaFileNotFoundException(const Url & url_r,
+                                const Pathname & filename_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _filename(filename_r.asString())
+      {}
+      virtual ~MediaFileNotFoundException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+      std::string _filename;
+    };
+
+    class MediaWriteException : public MediaException
+    {
+    public:
+      MediaWriteException(const Pathname & filename_r)
+      : MediaException()
+      , _filename(filename_r.asString())
+      {}
+      virtual ~MediaWriteException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _filename;
+    };
+
+    class MediaNotAttachedException : public MediaException
+    {
+    public:
+      MediaNotAttachedException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaNotAttachedException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+    };
+
+    class MediaBadAttachPointException : public MediaException
+    {
+    public:
+      MediaBadAttachPointException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaBadAttachPointException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+    };
+
+    class MediaCurlInitException : public MediaException
+    {
+    public:
+      MediaCurlInitException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaCurlInitException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+    };
+
+class MediaMetalinkInitException : public MediaException
+    {
+    public:
+      MediaMetalinkInitException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaMetalinkInitException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+    };
+
+class MediaAria2cInitException : public MediaException
+    {
+    public:
+      MediaAria2cInitException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaAria2cInitException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+    };
+
+    class MediaSystemException : public MediaException
+    {
+    public:
+      MediaSystemException(const Url & url_r,
+                          const std::string & message_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _message(message_r)
+      {}
+      virtual ~MediaSystemException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+      std::string _message;
+    };
+
+    class MediaNotAFileException : public MediaException
+    {
+    public:
+      MediaNotAFileException(const Url & url_r,
+                            const Pathname & path_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _path(path_r.asString())
+      {}
+      virtual ~MediaNotAFileException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+      std::string _path;
+    };
+
+    class MediaNotADirException : public MediaException
+    {
+    public:
+      MediaNotADirException(const Url & url_r,
+                           const Pathname & path_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _path(path_r.asString())
+      {}
+      virtual ~MediaNotADirException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _url;
+      std::string _path;
+    };
+
+    class MediaBadUrlException : public MediaException
+    {
+    public:
+      MediaBadUrlException(const Url & url_r,
+                           const std::string &msg_r = std::string())
+      : MediaException()
+      , _url(url_r.asString())
+      , _msg(msg_r)
+      {}
+      virtual ~MediaBadUrlException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaBadUrlEmptyHostException : public MediaBadUrlException
+    {
+    public:
+      MediaBadUrlEmptyHostException(const Url & url_r)
+      : MediaBadUrlException(url_r)
+      {}
+      virtual ~MediaBadUrlEmptyHostException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    };
+
+    class MediaBadUrlEmptyFilesystemException : public MediaBadUrlException
+    {
+    public:
+      MediaBadUrlEmptyFilesystemException(const Url & url_r)
+      : MediaBadUrlException(url_r)
+      {}
+      virtual ~MediaBadUrlEmptyFilesystemException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    };
+
+    class MediaBadUrlEmptyDestinationException : public MediaBadUrlException
+    {
+    public:
+      MediaBadUrlEmptyDestinationException(const Url & url_r)
+      : MediaBadUrlException(url_r)
+      {}
+      virtual ~MediaBadUrlEmptyDestinationException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    };
+
+    class MediaUnsupportedUrlSchemeException : public MediaBadUrlException
+    {
+    public:
+      MediaUnsupportedUrlSchemeException(const Url & url_r)
+      : MediaBadUrlException(url_r)
+      {}
+      virtual ~MediaUnsupportedUrlSchemeException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    };
+
+    class MediaNotSupportedException : public MediaException
+    {
+    public:
+      MediaNotSupportedException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaNotSupportedException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+    };
+
+    class MediaCurlException : public MediaException
+    {
+    public:
+      MediaCurlException(const Url & url_r,
+                        const std::string & err_r,
+                        const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _err(err_r)
+      , _msg(msg_r)
+      {}
+      virtual ~MediaCurlException() throw() {};
+      std::string errstr() const { return _err; }
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _err;
+      std::string _msg;
+    };
+
+    class MediaCurlSetOptException : public MediaException
+    {
+    public:
+      MediaCurlSetOptException(const Url & url_r, const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _msg(msg_r)
+      {}
+      virtual ~MediaCurlSetOptException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaMetalinkException : public MediaException
+    {
+    public:
+      MediaMetalinkException(const Url & url_r,
+                        const std::string & err_r,
+                        const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _err(err_r)
+      , _msg(msg_r)
+      {}
+      virtual ~MediaMetalinkException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _err;
+      std::string _msg;
+    };
+
+    class MediaMetalinkSetOptException : public MediaException
+    {
+    public:
+      MediaMetalinkSetOptException(const Url & url_r, const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _msg(msg_r)
+      {}
+      virtual ~MediaMetalinkSetOptException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaAria2cException : public MediaException
+    {
+    public:
+      MediaAria2cException(const Url & url_r,
+                        const std::string & err_r,
+                        const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _err(err_r)
+      , _msg(msg_r)
+      {}
+      virtual ~MediaAria2cException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _err;
+      std::string _msg;
+    };
+
+    class MediaAria2cSetOptException : public MediaException
+    {
+    public:
+      MediaAria2cSetOptException(const Url & url_r, const std::string & msg_r)
+      : MediaException()
+      , _url(url_r.asString())
+      , _msg(msg_r)
+      {}
+      virtual ~MediaAria2cSetOptException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaNotDesiredException : public MediaException
+    {
+    public:
+      MediaNotDesiredException(const Url & url_r)
+      : MediaException()
+      , _url(url_r.asString())
+      {}
+      virtual ~MediaNotDesiredException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string  _url;
+    };
+
+    class MediaIsSharedException : public MediaException
+    {
+    public:
+      /**
+       * \param name A media source as string (see MediaSource class).
+       */
+      MediaIsSharedException(const std::string &name)
+      : MediaException()
+      , _name(name)
+      {}
+      virtual ~MediaIsSharedException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _name;
+    };
+
+    class MediaNotEjectedException: public MediaException
+    {
+    public:
+      MediaNotEjectedException()
+      : MediaException("Can't eject any media")
+      , _name("")
+      {}
+
+      MediaNotEjectedException(const std::string &name)
+      : MediaException("Can't eject media")
+      , _name(name)
+      {}
+      virtual ~MediaNotEjectedException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      std::string _name;
+    };
+
+    class MediaUnauthorizedException: public MediaException
+    {
+    public:
+      MediaUnauthorizedException()
+      : MediaException("Unauthorized media access")
+      , _url("")
+      , _err("")
+      , _hint("")
+      {}
+
+      MediaUnauthorizedException(const Url         &url_r,
+                                 const std::string &msg_r,
+                                 const std::string &err_r,
+                                 const std::string &hint_r)
+      : MediaException(msg_r)
+      , _url(url_r)
+      , _err(err_r)
+      , _hint(hint_r)
+      {}
+
+      virtual ~MediaUnauthorizedException() throw() {};
+
+      const Url         & url()  const { return _url;  }
+      const std::string & err()  const { return _err;  }
+      /** comma separated list of available authentication types */
+      const std::string & hint() const { return _hint; }
+
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+      Url         _url;
+      std::string _err;
+      std::string _hint;
+    };
+
+    class MediaForbiddenException : public MediaException
+    {
+    public:
+      MediaForbiddenException(const Url & url_r, const std::string & msg = "")
+      : MediaException(msg)
+      , _url(url_r.asString()), _msg(msg)
+      {}
+      virtual ~MediaForbiddenException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaTimeoutException : public MediaException
+    {
+    public:
+      MediaTimeoutException(const Url & url_r, const std::string & msg = "")
+      : MediaException(msg)
+      , _url(url_r.asString()), _msg(msg)
+      {}
+      virtual ~MediaTimeoutException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    /** For HTTP 503 and similar. */
+    class MediaTemporaryProblemException : public MediaException
+    {
+    public:
+      MediaTemporaryProblemException(const Url & url_r, const std::string & msg = "")
+      : MediaException(msg)
+      , _url(url_r.asString()), _msg(msg)
+      {}
+      virtual ~MediaTemporaryProblemException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    class MediaBadCAException : public MediaException
+    {
+    public:
+      MediaBadCAException(const Url & url_r, const std::string & msg = "")
+      : MediaException(msg)
+      , _url(url_r.asString()), _msg(msg)
+      {}
+      virtual ~MediaBadCAException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+
+    /**
+     * Thrown if /sbin/losetup fails to find an unused loop device for mounting
+     * an .iso image.
+     *
+     * UI hint: tell user to check permissions to read /dev/loop# or enablement
+     * of support for loop devices.
+     *
+     * \see MediaISO
+     */
+    class MediaNoLoopDeviceException : public MediaException
+    {
+    public:
+      MediaNoLoopDeviceException(const Url & url_r, const std::string & msg = "")
+        : MediaException(msg)
+        , _url(url_r.asString()), _msg(msg)
+      {}
+      virtual ~MediaNoLoopDeviceException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+      std::string _url;
+      std::string _msg;
+    };
+  /////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_MEDIAEXCEPTION_H
diff --git a/zypp/media/MediaHandler.cc b/zypp/media/MediaHandler.cc
new file mode 100644 (file)
index 0000000..70b4ac0
--- /dev/null
@@ -0,0 +1,1368 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaHandler.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include "zypp/TmpPath.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/media/Mount.h"
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+using namespace std;
+
+// use directory.yast on every media (not just via ftp/http)
+#define NONREMOTE_DIRECTORY_YAST 1
+
+namespace zypp {
+  namespace media {
+
+  Pathname MediaHandler::_attachPrefix("");
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaHandler
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::MediaHandler
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+MediaHandler::MediaHandler ( const Url &      url_r,
+                            const Pathname & attach_point_r,
+                            const Pathname & urlpath_below_attachpoint_r,
+                            const bool       does_download_r )
+    : _mediaSource()
+    , _attachPoint( new AttachPoint())
+    , _AttachPointHint()
+    , _relativeRoot( urlpath_below_attachpoint_r)
+    , _does_download( does_download_r )
+    , _attach_mtime(0)
+    , _url( url_r )
+    , _parentId(0)
+{
+  Pathname real_attach_point( getRealPath(attach_point_r.asString()));
+
+  if ( !real_attach_point.empty() ) {
+    ///////////////////////////////////////////////////////////////////
+    // check if provided attachpoint is usable.
+    ///////////////////////////////////////////////////////////////////
+
+    PathInfo adir( real_attach_point );
+    //
+    // The verify if attach_point_r isn't a mountpoint of another
+    // device is done in the particular media handler (if needed).
+    //
+    // We just verify, if attach_point_r is a directory and for
+    // schemes other than "file" and "dir", if it is absolute.
+    //
+    if ( !adir.isDir()
+        || (_url.getScheme() != "file"
+            && _url.getScheme() != "dir"
+            && !real_attach_point.absolute()) )
+    {
+      ERR << "Provided attach point is not a absolute directory: "
+          << adir << endl;
+    }
+    else {
+      attachPointHint( real_attach_point, false);
+      setAttachPoint( real_attach_point, false);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::~MediaHandler
+//     METHOD TYPE : Destructor
+//
+//     DESCRIPTION :
+//
+MediaHandler::~MediaHandler()
+{
+  try
+    {
+      removeAttachPoint();
+    }
+  catch(...) {}
+}
+
+void
+MediaHandler::resetParentId()
+{
+  _parentId = 0;
+}
+
+std::string
+MediaHandler::getRealPath(const std::string &path)
+{
+  std::string real;
+  if( !path.empty())
+  {
+#if __GNUC__ > 2
+    /** GNU extension */
+    char *ptr = ::realpath(path.c_str(), NULL);
+    if( ptr != NULL)
+    {
+      real = ptr;
+      free( ptr);
+    }
+    else
+    /** the SUSv2 way */
+    if( EINVAL == errno)
+    {
+      char buff[PATH_MAX + 2];
+      memset(buff, '\0', sizeof(buff));
+      if( ::realpath(path.c_str(), buff) != NULL)
+      {
+       real = buff;
+      }
+    }
+#else
+    char buff[PATH_MAX + 2];
+    memset(buff, '\0', sizeof(buff));
+    if( ::realpath(path.c_str(), buff) != NULL)
+    {
+      real = buff;
+    }
+#endif
+  }
+  return real;
+}
+
+zypp::Pathname
+MediaHandler::getRealPath(const Pathname &path)
+{
+  return zypp::Pathname(getRealPath(path.asString()));
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::removeAttachPoint
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void
+MediaHandler::removeAttachPoint()
+{
+  if ( _mediaSource ) {
+    INT << "MediaHandler deleted with media attached." << endl;
+    return; // no cleanup if media still mounted!
+  }
+
+  DBG << "MediaHandler - checking if to remove attach point" << endl;
+  if ( _attachPoint.unique() &&
+       _attachPoint->temp    &&
+       !_attachPoint->path.empty() &&
+       PathInfo(_attachPoint->path).isDir())
+  {
+    Pathname path(_attachPoint->path);
+
+    setAttachPoint("", true);
+
+    int res = recursive_rmdir( path );
+    if ( res == 0 ) {
+      MIL << "Deleted default attach point " << path << endl;
+    } else {
+      ERR << "Failed to Delete default attach point " << path
+       << " errno(" << res << ")" << endl;
+    }
+  }
+  else
+  {
+    if( !_attachPoint->path.empty() && !_attachPoint->temp)
+      DBG << "MediaHandler - attachpoint is not temporary" << endl;
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachPoint
+//     METHOD TYPE : Pathname
+//
+//     DESCRIPTION :
+//
+Pathname
+MediaHandler::attachPoint() const
+{
+  return _attachPoint->path;
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachPoint
+//     METHOD TYPE :
+//
+//     DESCRIPTION :
+//
+void
+MediaHandler::setAttachPoint(const Pathname &path, bool temporary)
+{
+  _attachPoint.reset( new AttachPoint(path, temporary));
+}
+
+Pathname
+MediaHandler::localRoot() const
+{
+  if( _attachPoint->path.empty())
+    return Pathname();
+  else
+    return _attachPoint->path + _relativeRoot;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachPoint
+//     METHOD TYPE :
+//
+//     DESCRIPTION :
+//
+void
+MediaHandler::setAttachPoint(const AttachPointRef &ref)
+{
+  if( ref)
+    AttachPointRef(ref).swap(_attachPoint);
+  else
+    _attachPoint.reset( new AttachPoint());
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachPointHint
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void
+MediaHandler::attachPointHint(const Pathname &path, bool temporary)
+{
+  _AttachPointHint.path = path;
+  _AttachPointHint.temp = temporary;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachPointHint
+//     METHOD TYPE : AttachPoint
+//
+//     DESCRIPTION :
+//
+AttachPoint
+MediaHandler::attachPointHint() const
+{
+  return _AttachPointHint;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::findAttachedMedia
+//     METHOD TYPE : AttachedMedia
+//
+//     DESCRIPTION :
+//
+AttachedMedia
+MediaHandler::findAttachedMedia(const MediaSourceRef &media) const
+{
+       return MediaManager().findAttachedMedia(media);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::setAttachPrefix
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+bool
+MediaHandler::setAttachPrefix(const Pathname &attach_prefix)
+{
+  if( attach_prefix.empty())
+  {
+    MIL << "Reseting to built-in attach point prefixes."
+        << std::endl;
+    MediaHandler::_attachPrefix = attach_prefix;
+    return true;
+  }
+  else
+  if( MediaHandler::checkAttachPoint(attach_prefix, false, true))
+  {
+    MIL << "Setting user defined attach point prefix: "
+        << attach_prefix << std::endl;
+    MediaHandler::_attachPrefix = attach_prefix;
+    return true;
+  }
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attach
+//     METHOD TYPE : Pathname
+//
+//     DESCRIPTION :
+//
+Pathname
+MediaHandler::createAttachPoint() const
+{
+  /////////////////////////////////////////////////////////////////
+  // provide a default (temporary) attachpoint
+  /////////////////////////////////////////////////////////////////
+  const char * defmounts[] = {
+      "/var/adm/mount", filesystem::TmpPath::defaultLocation().c_str(), /**/NULL/**/
+  };
+
+  Pathname apoint;
+  Pathname aroot( MediaHandler::_attachPrefix);
+
+  if( !aroot.empty())
+  {
+    apoint = createAttachPoint(aroot);
+  }
+  for ( const char ** def = defmounts; *def && apoint.empty(); ++def ) {
+    aroot = *def;
+    if( aroot.empty())
+      continue;
+
+    apoint = createAttachPoint(aroot);
+  }
+
+  if ( aroot.empty() ) {
+    ERR << "Create attach point: Can't find a writable directory to create an attach point" << std::endl;
+    return aroot;
+  }
+
+  if ( !apoint.empty() ) {
+    MIL << "Created default attach point " << apoint << std::endl;
+  }
+  return apoint;
+}
+
+Pathname
+MediaHandler::createAttachPoint(const Pathname &attach_root) const
+{
+  Pathname apoint;
+
+  if( attach_root.empty() || !attach_root.absolute()) {
+    ERR << "Create attach point: invalid attach root: '"
+        << attach_root << "'" << std::endl;
+    return apoint;
+  }
+
+  PathInfo adir( attach_root);
+  if( !adir.isDir() || (getuid() != 0 && !adir.userMayRWX())) {
+    DBG << "Create attach point: attach root is not a writable directory: '"
+        << attach_root << "'" << std::endl;
+    return apoint;
+  }
+
+  DBG << "Trying to create attach point in " << attach_root << std::endl;
+
+  //
+  // FIXME: use mkdtemp?
+  //
+#warning Use class TmpDir from TmpPath.h
+  Pathname abase( attach_root + "AP_" );
+  //        ma and sh need more than 42 for debugging :-)
+  //        since the readonly fs are handled now, ...
+  for ( unsigned i = 1; i < 1000; ++i ) {
+    adir( Pathname::extend( abase, str::hexstring( i ) ) );
+    if ( ! adir.isExist() ) {
+      int err = mkdir( adir.path() );
+      if (err == 0 ) {
+        apoint = getRealPath(adir.asString());
+       if( apoint.empty())
+       {
+         ERR << "Unable to resolve a real path for "
+             << adir.path() << std::endl;
+         rmdir(adir.path());
+       }
+        break;
+      }
+      else
+      if (err != EEXIST)       // readonly fs or other, dont try further
+        break;
+    }
+  }
+
+  if ( apoint.empty()) {
+    ERR << "Unable to create an attach point below of "
+        << attach_root << std::endl;
+  }
+  return apoint;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::isUseableAttachPoint
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool
+MediaHandler::isUseableAttachPoint(const Pathname &path, bool mtab) const
+{
+  MediaManager  manager;
+  return manager.isUseableAttachPoint(path, mtab);
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::setMediaSource
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void
+MediaHandler::setMediaSource(const MediaSourceRef &ref)
+{
+  _mediaSource.reset();
+  if( ref && !ref->type.empty() && !ref->name.empty())
+    _mediaSource = ref;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attachedMedia
+//     METHOD TYPE : AttachedMedia
+//
+//     DESCRIPTION :
+//
+AttachedMedia
+MediaHandler::attachedMedia() const
+{
+  if ( _mediaSource && _attachPoint)
+    return AttachedMedia(_mediaSource, _attachPoint);
+  else
+    return AttachedMedia();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::isSharedMedia
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool
+MediaHandler::isSharedMedia() const
+{
+  return !_mediaSource.unique();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::checkAttached
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool
+MediaHandler::checkAttached(bool matchMountFs) const
+{
+  bool _isAttached = false;
+
+  AttachedMedia ref( attachedMedia());
+  if( ref.mediaSource )
+  {
+    time_t old_mtime = _attach_mtime;
+    _attach_mtime = MediaManager::getMountTableMTime();
+    if( !(old_mtime <= 0 || _attach_mtime != old_mtime) )
+    {
+      // OK, skip the check (we've seen it at least once)
+      _isAttached = true;
+    }
+    else
+    {
+      if( old_mtime > 0)
+        DBG << "Mount table changed - rereading it" << std::endl;
+      else
+        DBG << "Forced check of the mount table" << std::endl;
+
+      MountEntries entries( MediaManager::getMountEntries());
+      for_( e, entries.begin(), entries.end() )
+      {
+        bool        is_device = false;
+        PathInfo    dev_info;
+
+        if( str::hasPrefix( Pathname(e->src).asString(), "/dev/" ) &&
+            dev_info(e->src) && dev_info.isBlk())
+        {
+          is_device = true;
+        }
+
+        if( is_device &&  (ref.mediaSource->maj_nr &&
+                          ref.mediaSource->bdir.empty()))
+        {
+          std::string mtype(matchMountFs ? e->type : ref.mediaSource->type);
+          MediaSource media(mtype, e->src, dev_info.major(), dev_info.minor());
+
+          if( ref.mediaSource->equals( media) &&
+              ref.attachPoint->path == Pathname(e->dir))
+          {
+            DBG << "Found media device "
+                << ref.mediaSource->asString()
+                << " in the mount table as " << e->src << std::endl;
+            _isAttached = true;
+            break;
+          }
+          // differs
+        }
+        else
+        if(!is_device && (!ref.mediaSource->maj_nr ||
+                         !ref.mediaSource->bdir.empty()))
+        {
+          std::string mtype(matchMountFs ? e->type : ref.mediaSource->type);
+         if( ref.mediaSource->bdir.empty())
+         {
+           MediaSource media(mtype, e->src);
+
+           if( ref.mediaSource->equals( media) &&
+                ref.attachPoint->path == Pathname(e->dir))
+           {
+             DBG << "Found media name "
+                  << ref.mediaSource->asString()
+                  << " in the mount table as " << e->src << std::endl;
+             _isAttached = true;
+             break;
+           }
+         }
+         else
+         {
+           if(ref.mediaSource->bdir == e->src &&
+              ref.attachPoint->path == Pathname(e->dir))
+           {
+             DBG << "Found bound media "
+                 << ref.mediaSource->asString()
+                 << " in the mount table as " << e->src << std::endl;
+             _isAttached = true;
+             break;
+           }
+         }
+          // differs
+        }
+      }
+
+      if( !_isAttached)
+      {
+        MIL << "Looking for " << ref << endl;
+       if( entries.empty() )
+       {
+         ERR << "Unable to find any entry in the /etc/mtab file" << std::endl;
+       }
+       else
+       {
+          dumpRange( DBG << "MountEntries: ", entries.begin(), entries.end() ) << endl;
+       }
+       if( old_mtime > 0 )
+       {
+          ERR << "Attached media not in mount table any more - forcing reset!"
+              << std::endl;
+
+         _mediaSource.reset();
+       }
+       else
+       {
+          WAR << "Attached media not in mount table ..." << std::endl;
+       }
+
+        // reset the mtime and force a new check to make sure,
+        // that we've found the media at least once in the mtab.
+        _attach_mtime = 0;
+      }
+    }
+  }
+  return _isAttached;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::attach
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::attach( bool next )
+{
+  if ( isAttached() )
+    return;
+
+  // reset it in case of overloaded isAttached()
+  // that checks the media against /etc/mtab ...
+  setMediaSource(MediaSourceRef());
+
+  AttachPoint ap( attachPointHint());
+  setAttachPoint(ap.path, ap.temp);
+
+  try
+  {
+    attachTo( next ); // pass to concrete handler
+  }
+  catch(const MediaException &e)
+  {
+    removeAttachPoint();
+    ZYPP_RETHROW(e);
+  }
+  MIL << "Attached: " << *this << endl;
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::localPath
+//     METHOD TYPE : Pathname
+//
+Pathname MediaHandler::localPath( const Pathname & pathname ) const
+{
+    Pathname _localRoot( localRoot());
+    if ( _localRoot.empty() )
+        return _localRoot;
+
+    // we must check maximum file name length
+    // this is important for fetching the suseservers, the
+    // url with all parameters can get too long (bug #42021)
+
+    return _localRoot + pathname.absolutename();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::disconnect
+//     METHOD TYPE : PMError
+//
+void MediaHandler::disconnect()
+{
+  if ( !isAttached() )
+    return;
+
+  disconnectFrom(); // pass to concrete handler
+  MIL << "Disconnected: " << *this << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::release
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::release( const std::string & ejectDev )
+{
+  if ( !isAttached() ) {
+    DBG << "Request to release media - not attached; eject '" << ejectDev << "'"
+        << std::endl;
+    if ( !ejectDev.empty() )
+      forceEject(ejectDev);
+    return;
+  }
+
+  DBG << "Request to release attached media "
+      << _mediaSource->asString()
+      << ", use count=" << _mediaSource.use_count()
+      << std::endl;
+
+  if( _mediaSource.unique())
+  {
+    DBG << "Releasing media " << _mediaSource->asString() << std::endl;
+    try {
+      releaseFrom( ejectDev ); // pass to concrete handler
+    }
+    catch(const MediaNotEjectedException &e)
+    {
+      // not ejected because the media
+      // is mounted by somebody else
+      // (if our attach point is busy,
+      //  we get an umount exception)
+      _mediaSource.reset(NULL);
+      removeAttachPoint();
+      // OK, retrow now
+      ZYPP_RETHROW(e);
+    }
+    _mediaSource.reset(NULL);
+    removeAttachPoint();
+  }
+  else if( !ejectDev.empty() ) {
+    //
+    // Can't eject a shared media
+    //
+    //ZYPP_THROW(MediaIsSharedException(_mediaSource->asString()));
+
+    MediaSourceRef media( new MediaSource(*_mediaSource));
+    _mediaSource.reset(NULL);
+
+    MediaManager manager;
+    manager.forceReleaseShared(media);
+
+    setMediaSource(media);
+    DBG << "Releasing media (forced) " << _mediaSource->asString() << std::endl;
+    try {
+      releaseFrom( ejectDev ); // pass to concrete handler
+    }
+    catch(const MediaNotEjectedException &e)
+    {
+      // not ejected because the media
+      // is mounted by somebody else
+      // (if our attach point is busy,
+      //  we get an umount exception)
+      _mediaSource.reset(NULL);
+      removeAttachPoint();
+      // OK, retrow now
+      ZYPP_RETHROW(e);
+    }
+    _mediaSource.reset(NULL);
+    removeAttachPoint();
+  }
+  else {
+    DBG << "Releasing shared media reference only" << std::endl;
+    _mediaSource.reset(NULL);
+    setAttachPoint("", true);
+  }
+  MIL << "Released: " << *this << endl;
+}
+
+void MediaHandler::forceRelaseAllMedia(bool matchMountFs)
+{
+  forceRelaseAllMedia( attachedMedia().mediaSource, matchMountFs);
+}
+
+void MediaHandler::forceRelaseAllMedia(const MediaSourceRef &ref,
+                                       bool                  matchMountFs)
+{
+  if( !ref)
+    return;
+
+  MountEntries  entries( MediaManager::getMountEntries());
+  MountEntries::const_iterator e;
+  for( e = entries.begin(); e != entries.end(); ++e)
+  {
+    bool        is_device = false;
+    PathInfo    dev_info;
+
+    if( str::hasPrefix( Pathname(e->src).asString(), "/dev/" ) &&
+        dev_info(e->src) && dev_info.isBlk())
+    {
+      is_device = true;
+    }
+
+    if( is_device &&  ref->maj_nr)
+    {
+      std::string mtype(matchMountFs ? e->type : ref->type);
+      MediaSource media(mtype, e->src, dev_info.major(), dev_info.minor());
+
+      if( ref->equals( media) && e->type != "subfs")
+      {
+        DBG << "Forcing release of media device "
+            << ref->asString()
+            << " in the mount table as "
+           << e->src << std::endl;
+       try {
+         Mount mount;
+         mount.umount(e->dir);
+       }
+       catch (const Exception &e)
+       {
+         ZYPP_CAUGHT(e);
+       }
+      }
+    }
+    else
+    if(!is_device && !ref->maj_nr)
+    {
+      std::string mtype(matchMountFs ? e->type : ref->type);
+      MediaSource media(mtype, e->src);
+      if( ref->equals( media))
+      {
+       DBG << "Forcing release of media name "
+           << ref->asString()
+           << " in the mount table as "
+           << e->src << std::endl;
+       try {
+         Mount mount;
+         mount.umount(e->dir);
+       }
+       catch (const Exception &e)
+       {
+         ZYPP_CAUGHT(e);
+       }
+      }
+    }
+  }
+}
+
+bool
+MediaHandler::checkAttachPoint(const Pathname &apoint) const
+{
+  return MediaHandler::checkAttachPoint( apoint, true, false);
+}
+
+// STATIC
+bool
+MediaHandler::checkAttachPoint(const Pathname &apoint,
+                              bool            emptydir,
+                              bool            writeable)
+{
+  if( apoint.empty() || !apoint.absolute())
+  {
+    ERR << "Attach point '" << apoint << "' is not absolute"
+        << std::endl;
+    return false;
+  }
+  if( apoint == "/")
+  {
+    ERR << "Attach point '" << apoint << "' is not allowed"
+        << std::endl;
+    return false;
+  }
+
+  PathInfo ainfo(apoint);
+  if( !ainfo.isDir())
+  {
+    ERR << "Attach point '" << apoint << "' is not a directory"
+        << std::endl;
+    return false;
+  }
+
+  if( emptydir)
+  {
+    if( 0 != zypp::filesystem::is_empty_dir(apoint))
+    {
+      ERR << "Attach point '" << apoint << "' is not a empty directory"
+          << std::endl;
+      return false;
+    }
+  }
+
+  if( writeable)
+  {
+    Pathname apath(apoint + "XXXXXX");
+    char    *atemp = ::strdup( apath.asString().c_str());
+    char    *atest = NULL;
+    if( !ainfo.userMayRWX() || atemp == NULL ||
+        (atest=::mkdtemp(atemp)) == NULL)
+    {
+      if( atemp != NULL)
+       ::free(atemp);
+
+      ERR << "Attach point '" << ainfo.path()
+          << "' is not a writeable directory" << std::endl;
+      return false;
+    }
+    else if( atest != NULL)
+      ::rmdir(atest);
+
+    if( atemp != NULL)
+      ::free(atemp);
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : MediaHandler::dependsOnParent
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool
+MediaHandler::dependsOnParent()
+{
+  return _parentId != 0;
+}
+
+bool
+MediaHandler::dependsOnParent(MediaAccessId parentId, bool exactIdMatch)
+{
+  if( _parentId != 0)
+  {
+    if(parentId == _parentId)
+      return true;
+
+    if( !exactIdMatch)
+    {
+      MediaManager mm;
+      AttachedMedia am1 = mm.getAttachedMedia(_parentId);
+      AttachedMedia am2 = mm.getAttachedMedia(parentId);
+      if( am1.mediaSource && am2.mediaSource)
+      {
+       return am1.mediaSource->equals( *(am2.mediaSource));
+      }
+    }
+  }
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideFile
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideFileCopy( Pathname srcFilename,
+                                       Pathname targetFilename ) const
+{
+  if ( !isAttached() ) {
+    INT << "Media not_attached on provideFileCopy(" << srcFilename
+        << "," << targetFilename << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getFileCopy( srcFilename, targetFilename ); // pass to concrete handler
+  DBG << "provideFileCopy(" << srcFilename << "," << targetFilename  << ")" << endl;
+}
+
+void MediaHandler::provideFile( Pathname filename ) const
+{
+  if ( !isAttached() ) {
+    INT << "Error: Not attached on provideFile(" << filename << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getFile( filename ); // pass to concrete handler
+  DBG << "provideFile(" << filename << ")" << endl;
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideDir
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideDir( Pathname dirname ) const
+{
+  if ( !isAttached() ) {
+    INT << "Error: Not attached on provideDir(" << dirname << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getDir( dirname, /*recursive*/false ); // pass to concrete handler
+  MIL << "provideDir(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::provideDirTree
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::provideDirTree( Pathname dirname ) const
+{
+  if ( !isAttached() ) {
+    INT << "Error Not attached on provideDirTree(" << dirname << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getDir( dirname, /*recursive*/true ); // pass to concrete handler
+  MIL << "provideDirTree(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::releasePath
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::releasePath( Pathname pathname ) const
+{
+  if ( ! _does_download || _attachPoint->empty() )
+    return;
+
+  PathInfo info( localPath( pathname ) );
+
+  if ( info.isFile() ) {
+    unlink( info.path() );
+  } else if ( info.isDir() ) {
+    if ( info.path() != localRoot() ) {
+      recursive_rmdir( info.path() );
+    } else {
+      clean_dir( info.path() );
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::dirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::dirInfo( std::list<std::string> & retlist,
+                            const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !isAttached() ) {
+    INT << "Error: Not attached on dirInfo(" << dirname << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getDirInfo( retlist, dirname, dots ); // pass to concrete handler
+  MIL << "dirInfo(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::dirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void MediaHandler::dirInfo( filesystem::DirContent & retlist,
+                            const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  if ( !isAttached() ) {
+    INT << "Error: Not attached on dirInfo(" << dirname << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+
+  getDirInfo( retlist, dirname, dots ); // pass to concrete handler
+  MIL << "dirInfo(" << dirname << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//      METHOD NAME : MediaHandler::doesFileExist
+//      METHOD TYPE : PMError
+//
+//      DESCRIPTION :
+//
+bool MediaHandler::doesFileExist( const Pathname & filename ) const
+{
+  // TODO do some logging
+  if ( !isAttached() ) {
+    INT << "Error Not attached on doesFileExist(" << filename << ")" << endl;
+    ZYPP_THROW(MediaNotAttachedException(url()));
+  }
+  return getDoesFileExist( filename );
+  MIL << "doesFileExist(" << filename << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirectoryYast
+//     METHOD TYPE : PMError
+//
+void MediaHandler::getDirectoryYast( std::list<std::string> & retlist,
+                                       const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  filesystem::DirContent content;
+  getDirectoryYast( content, dirname, dots );
+
+  // convert to std::list<std::string>
+  for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
+    retlist.push_back( it->name );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirectoryYast
+//     METHOD TYPE : PMError
+//
+void MediaHandler::getDirectoryYast( filesystem::DirContent & retlist,
+                                     const Pathname & dirname, bool dots ) const
+{
+  retlist.clear();
+
+  // look for directory.yast
+  Pathname dirFile = dirname + "directory.yast";
+  getFile( dirFile );
+  DBG << "provideFile(" << dirFile << "): " << "OK" << endl;
+
+  // using directory.yast
+  ifstream dir( localPath( dirFile ).asString().c_str() );
+  if ( dir.fail() ) {
+    ERR << "Unable to load '" << localPath( dirFile ) << "'" << endl;
+    ZYPP_THROW(MediaSystemException(url(),
+      "Unable to load '" + localPath( dirFile ).asString() + "'"));
+  }
+
+  string line;
+  while( getline( dir, line ) ) {
+    if ( line.empty() ) continue;
+    if ( line == "directory.yast" ) continue;
+
+    // Newer directory.yast append '/' to directory names
+    // Remaining entries are unspecified, although most probabely files.
+    filesystem::FileType type = filesystem::FT_NOT_AVAIL;
+    if ( *line.rbegin() == '/' ) {
+      line.erase( line.end()-1 );
+      type = filesystem::FT_DIR;
+    }
+
+    if ( dots ) {
+      if ( line == "." || line == ".." ) continue;
+    } else {
+      if ( *line.begin() == '.' ) continue;
+    }
+
+    retlist.push_back( filesystem::DirEntry( line, type ) );
+  }
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const MediaHandler & obj )
+{
+  str << obj.url() << ( obj.isAttached() ? "" : " not" )
+    << " attached; localRoot \"" << obj.localRoot() << "\"";
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getFile
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getFile( const Pathname & filename ) const
+{
+    PathInfo info( localPath( filename ) );
+    if( info.isFile() ) {
+        return;
+    }
+
+    if (info.isExist())
+      ZYPP_THROW(MediaNotAFileException(url(), localPath(filename)));
+    else
+      ZYPP_THROW(MediaFileNotFoundException(url(), filename));
+}
+
+
+void MediaHandler::getFileCopy ( const Pathname & srcFilename, const Pathname & targetFilename ) const
+{
+  getFile(srcFilename);
+
+  if ( copy( localPath( srcFilename ), targetFilename ) != 0 ) {
+    ZYPP_THROW(MediaWriteException(targetFilename));
+  }
+}
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDir
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDir( const Pathname & dirname, bool recurse_r ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( info.isDir() ) {
+    return;
+  }
+
+  if (info.isExist())
+    ZYPP_THROW(MediaNotADirException(url(), localPath(dirname)));
+  else
+    ZYPP_THROW(MediaFileNotFoundException(url(), dirname));
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDirInfo( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( ! info.isDir() ) {
+    ZYPP_THROW(MediaNotADirException(url(), localPath(dirname)));
+  }
+
+#if NONREMOTE_DIRECTORY_YAST
+  // use directory.yast if available
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#endif
+
+    // readdir
+    int res = readdir( retlist, info.path(), dots );
+    if ( res )
+    {
+      MediaSystemException nexcpt(url(), "readdir failed");
+#if NONREMOTE_DIRECTORY_YAST
+      nexcpt.remember(excpt_r);
+#endif
+      ZYPP_THROW(nexcpt);
+    }
+
+#if NONREMOTE_DIRECTORY_YAST
+  }
+#endif
+
+  return;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : MediaHandler::getDirInfo
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION : Asserted that media is attached and retlist is empty.
+//                    Default implementation of pure virtual.
+//
+void MediaHandler::getDirInfo( filesystem::DirContent & retlist,
+                               const Pathname & dirname, bool dots ) const
+{
+  PathInfo info( localPath( dirname ) );
+  if( ! info.isDir() ) {
+    ZYPP_THROW(MediaNotADirException(url(), localPath(dirname)));
+  }
+
+#if NONREMOTE_DIRECTORY_YAST
+  // use directory.yast if available
+  try {
+    getDirectoryYast( retlist, dirname, dots );
+  }
+  catch (const MediaException & excpt_r)
+  {
+#endif
+
+    // readdir
+    int res = readdir( retlist, info.path(), dots );
+    if ( res )
+    {
+       MediaSystemException nexcpt(url(), "readdir failed");
+#if NONREMOTE_DIRECTORY_YAST
+       nexcpt.remember(excpt_r);
+#endif
+       ZYPP_THROW(nexcpt);
+    }
+#if NONREMOTE_DIRECTORY_YAST
+  }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//      METHOD NAME : MediaHandler::getDoesFileExist
+//      METHOD TYPE : PMError
+//
+//      DESCRIPTION : Asserted that file is not a directory
+//                    Default implementation of pure virtual.
+//
+bool MediaHandler::getDoesFileExist( const Pathname & filename ) const
+{
+  PathInfo info( localPath( filename ) );
+  if( info.isDir() ) {
+    ZYPP_THROW(MediaNotAFileException(url(), localPath(filename)));
+  }
+  return info.isExist();
+}
+
+bool MediaHandler::hasMoreDevices()
+{
+  return false;
+}
+
+void MediaHandler::getDetectedDevices(std::vector<std::string> & devices,
+                                      unsigned int & index) const
+{
+  // clear the vector by default
+  if (!devices.empty())
+    devices.clear();
+  index = 0;
+
+  DBG << "No devices for this medium" << endl;
+}
+
+void MediaHandler::setDeltafile( const Pathname & filename ) const
+{
+  _deltafile = filename;
+}
+
+Pathname MediaHandler::deltafile() const {
+  return _deltafile;
+}
+
+  } // namespace media
+} // namespace zypp
+// vim: set ts=8 sts=2 sw=2 ai noet:
diff --git a/zypp/media/MediaHandler.h b/zypp/media/MediaHandler.h
new file mode 100644 (file)
index 0000000..7f7c95d
--- /dev/null
@@ -0,0 +1,721 @@
+
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaHandler.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAHANDLERL_H
+#define ZYPP_MEDIA_MEDIAHANDLERL_H
+
+#include <iosfwd>
+#include <string>
+#include <list>
+
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Url.h"
+
+#include "zypp/media/MediaSource.h"
+#include "zypp/media/MediaException.h"
+#include "zypp/base/Deprecated.h"
+
+namespace zypp {
+  namespace media {
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : MediaHandler
+/**
+ * @short Abstract base class for 'physical' MediaHandler like MediaCD, etc.
+ *
+ * Handles the requests forwarded by @ref MediaAccess. The public interface
+ * contains nonvirtual methods, which should do common sanitychecks and
+ * logging. For the real action they call virtual methods overloaded by the
+ * concrete handler.
+ **/
+class MediaHandler {
+    friend std::ostream & operator<<( std::ostream & str, const MediaHandler & obj );
+
+    public:
+       typedef shared_ptr<MediaHandler> Ptr;
+       typedef shared_ptr<const MediaHandler> constPtr;
+
+       static bool setAttachPrefix(const Pathname &attach_prefix);
+
+       static std::string getRealPath(const std::string &path);
+       static Pathname    getRealPath(const Pathname    &path);
+
+    private:
+        /**
+        * User defined default attach point prefix.
+        */
+       static Pathname _attachPrefix;
+
+       /**
+        * The attached media source description reference.
+        */
+       mutable
+       MediaSourceRef  _mediaSource;
+
+       /**
+        * This is where the media will be actually attached ("mounted").
+        * All files are provided bellow this + _relativeRoot directory.
+        **/
+       AttachPointRef  _attachPoint;
+
+       /**
+        * The user provided attach preferred point. It may contain
+        * following values:
+        *
+        *      "",  true  => create temporary attach point bellow of
+        *                    _attachPrefix or a built-in default and
+        *                    remove it if not needed any more.
+        *
+        *      dir, false => user specified attach point (not removed)
+        */
+       AttachPoint     _AttachPointHint;
+
+       /**
+        * The relative root directory of the data on the media.
+        * See also localRoot() and urlpath_below_attachpoint_r
+        * constructor argument.
+        */
+       Pathname        _relativeRoot;
+
+       /**
+        * True if concrete handler downloads files to the local
+        * filesystem. If true releaseFile/Dir will delete them.
+        **/
+       bool            _does_download;
+
+        /** timestamp of the the last attach verification */
+        mutable time_t  _attach_mtime;
+
+       /** file usable for delta downloads */
+       mutable Pathname _deltafile;
+
+    protected:
+        /**
+        * Url to handle
+        **/
+       const Url        _url;
+
+       /**
+        * Access Id of media handler we depend on.
+        */
+       MediaAccessId    _parentId;
+
+        /**
+        * MediaAccess (MediaManager) needs access to the attachedMedia()
+        * function to deliver a shared media source and its attach point
+        * to the media manager and then to other media handler instances.
+        * Further, is needs to be able to forward the dependsOnParent()
+        * and resetParentId() functions to the media manager.
+        */
+       friend class MediaAccess;
+
+       /**
+        * Check if the current media handler depends on an
+        * another handler specified by media access id.
+        * \param parentId The id of the parent handler to check against.
+        * \return true if it depends, false if not.
+        */
+       bool             dependsOnParent(MediaAccessId parentId,
+                                        bool exactIdMatch);
+       bool             dependsOnParent();
+
+       /**
+        * Called in case, where the media manager takes over the
+        * destruction of the parent id (e.g. while destruction
+        * of the media manager).
+        */
+       void             resetParentId();
+
+        /**
+        * Return the currently used attach point.
+        **/
+       Pathname         attachPoint() const;
+
+       /**
+        * Set a new attach point.
+        * \param path  The attach point directory path.
+        * \param temp  If to remove the attach point while cleanup.
+        */
+       void             setAttachPoint(const Pathname &path, bool temp);
+
+       /**
+        * Set a (shared) attach point.
+        * \param ref New attach point reference.
+        */
+       void             setAttachPoint(const AttachPointRef &ref);
+
+       /**
+        * Get the actual attach point hint.
+        */
+       AttachPoint      attachPointHint() const;
+
+       /**
+        * Set the attach point hint as specified by the user.
+        * \param path  The attach point directory path.
+        * \param temp  If to remove the attach point while cleanup.
+        */
+       void             attachPointHint(const Pathname &path, bool temp);
+
+       /**
+        * Try to create a default / temporary attach point.
+        * It trys to create it in attachPrefix if avaliable,
+        * then in built-in directories.
+        * \return The name of the new attach point or empty path name.
+        */
+       Pathname         createAttachPoint() const;
+       /**
+        * Try to create a temporary attach point in specified root.
+        * \param attach_root The attach root dir where to create the
+        *                    attach point in.
+        * \return The name of the new attach point or empty path name.
+        */
+        Pathname         createAttachPoint(const Pathname &attach_root) const;
+
+       /**
+        * Remove unused attach point. If the attach point is temporary,
+        * the attach point directory and all it content will be removed.
+        */
+       void             removeAttachPoint();
+
+        /**
+        * Verify if the specified directory as attach point (root)
+        * as requires by the particular media handler implementation.
+        * \param apoint The directory to check.
+        * \return True, if the directory checks succeeded.
+        */
+       virtual bool     checkAttachPoint(const Pathname &apoint) const;
+
+       /**
+        * Verify if the specified directory as attach point (root)
+        * using requested checks.
+        * \param apoint The directory to check.
+        * \param empty_dir Check if the directory is empty.
+        * \param writeable Check if the directory is writeable.
+        * \return True, if the directory checks succeeded.
+        */
+       static bool      checkAttachPoint(const Pathname &apoint,
+                                         bool            empty_dir,
+                                         bool            writeable);
+
+       /**
+        * Ask media manager, if the specified path is already used
+        * as attach point or if there are another attach points
+        * bellow of it.
+        * \param path The attach point path to check.
+        * \param mtab Whether to check against the mtab, too.
+        * \return True, if the path can be used as attach point.
+        */
+        bool             isUseableAttachPoint(const Pathname &path,
+                                             bool            mtab=true) const;
+
+       /**
+        * Get the media source name or an empty string.
+        * \return media source name or empty string.
+        */
+       std::string      mediaSourceName() const
+       {
+         return _mediaSource ? _mediaSource->name : "";
+       }
+
+       /**
+        * Set new media source reference.
+        * \param ref The new reference.
+        */
+       void             setMediaSource(const MediaSourceRef &ref);
+
+       /**
+        * Ask the media manager if specified media source
+        * is already attached.
+        */
+       AttachedMedia
+       findAttachedMedia(const MediaSourceRef &media) const;
+
+       /**
+        * Returns the attached media. Used by MediaManager
+        * to find other handlers using the same source.
+        * \note This function increments reference counters
+        *       on the mediaSource and attachPoint references
+        *       it contains, for the life time of the returned
+        *       object. That is, it enables a (temporary) sharing
+        *       of them.
+        * \return The AttachedMedia struct containing (shared)
+        *         references to media source and attach point.
+        */
+       AttachedMedia    attachedMedia() const;
+
+       /**
+        * Returns a hint if the media is shared or not.
+        * \return true, if media is shared.
+        */
+       bool             isSharedMedia() const;
+
+       /**
+        * Check actual mediaSource attachment against the current
+        * mount table of the system. Used to implement isAttached().
+        * \param matchMountFs If to use the filesystem type from the
+        *        mount table (nfs, smb and cifs) or from mediaSource
+        *        while compare of a mount entry with mediaSource.
+        * \return true, if the media appears in the mount table.
+        */
+       bool             checkAttached(bool matchMountFs) const;
+
+       /**
+        * Call to this function will try to release all media matching
+        * the currenlty attached media source, that it is able to find
+        * in the mount table. This means also foreign (user) mounts!
+        * \param matchMountFs If to use the filesystem type from the
+        *        mount table (nfs, smb and cifs) or from mediaSource
+        *        while compare of a mount entry with mediaSource.
+        */
+       void             forceRelaseAllMedia(bool matchMountFs);
+       void             forceRelaseAllMedia(const MediaSourceRef &ref,
+                                            bool matchMountFs);
+
+    protected:
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        // Real action interface to be overloaded by concrete handler.
+        //
+        ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Call concrete handler to attach the media.
+        *
+        * Asserted that not already attached, and attachPoint is a directory.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void attachTo(bool next = false) = 0;
+
+        /**
+         * Call concrete handler to disconnect media.
+        *
+        * Asserted that media is attached.
+        *
+         * This is useful for media which e.g. holds open a connection to a
+         * server like FTP. After calling disconnect() the media object still is
+         * valid and files are present.
+        *
+         * After calling disconnect() it's not possible to call provideFile() or
+         * provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void disconnectFrom() { return; }
+
+       /**
+        * Call concrete handler to release the media.
+        *
+        * If eject is true, and the media is used in one handler
+        * instance only, physically eject the media (i.e. CD-ROM).
+        *
+        * Asserted that media is attached.
+        * \param ejectDev Device to eject. None if empty.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void releaseFrom( const std::string & ejectDev = "" ) = 0;
+
+       /**
+        * Call concrete handler to physically eject the media (i.e. CD-ROM)
+        * in case the media is not attached..
+        *
+        * Asserted that media is not attached.
+        **/
+       virtual void forceEject( const std::string & device ) {}
+
+       /**
+        * Call concrete handler to provide file below attach point.
+        *
+        * Default implementation provided, that returns whether a file
+        * is located at 'localRoot + filename'.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void getFile( const Pathname & filename ) const = 0;
+
+        /**
+         * Call concrete handler to provide a file under a different place
+         * in the file system (usually not under attach point) as a copy.
+         * Media must be attached before by callee.
+         *
+         * Default implementation provided that calls getFile(srcFilename)
+         * and copies the result around.
+        *
+        * \throws MediaException
+        *
+         **/
+        virtual void getFileCopy( const Pathname & srcFilename, const Pathname & targetFilename ) const;
+
+
+       /**
+        * Call concrete handler to provide directory content (not recursive!)
+        * below attach point.
+        *
+        * Return E_not_supported_by_media if media does not support retrieval of
+        * directory content.
+        *
+        * Default implementation provided, that returns whether a directory
+        * is located at 'localRoot + dirname'.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const = 0;
+
+       /**
+        * Call concrete handler to provide a content list of directory on media
+        * via retlist. If dots is false entries starting with '.' are not reported.
+        *
+        * Return E_not_supported_by_media if media does not support retrieval of
+        * directory content.
+        *
+        * Default implementation provided, that returns the content of a
+        * directory at 'localRoot + dirnname' retrieved via 'readdir'.
+        *
+        * Asserted that media is attached and retlist is empty.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const = 0;
+
+       /**
+        * Basically the same as getDirInfo above. The content list is returned as
+        * filesystem::DirContent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * Asserted that media is attached and retlist is empty.
+        *
+        * \throws MediaException
+        *
+        **/
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const = 0;
+
+        /**
+         * check if a file exists
+         *
+         * Asserted that url is a file and not a dir.
+         *
+         * \throws MediaException
+         *
+         **/
+        virtual bool getDoesFileExist( const Pathname & filename ) const = 0;
+
+  protected:
+
+        /**
+        * Retrieve and if available scan dirname/directory.yast.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+        void getDirectoryYast( std::list<std::string> & retlist,
+                               const Pathname & dirname, bool dots = true ) const;
+
+        /**
+        * Retrieve and if available scan dirname/directory.yast.
+        *
+        * Asserted that media is attached.
+        *
+        * \throws MediaException
+        *
+        **/
+        void getDirectoryYast( filesystem::DirContent & retlist,
+                               const Pathname & dirname, bool dots = true ) const;
+
+  public:
+
+       /**
+        * If the concrete media handler provides a nonempty
+        * attach_point, it must be an existing directory.
+        *
+        * On an empty attach_point, MediaHandler will create
+        * a temporay directory, which will be erased from
+        * destructor.
+        *
+        * On any error, the attach_point is set to an empty Pathname,
+        * which should lead to E_bad_attachpoint.
+        **/
+       MediaHandler ( const Url&       url_r,
+                      const Pathname & attach_point_r,
+                      const Pathname & urlpath_below_attachpoint_r,
+                      const bool       does_download_r );
+
+       /**
+        * Contolling MediaAccess takes care, that attached media is released
+        * prior to deleting this.
+        **/
+       virtual ~MediaHandler();
+
+    public:
+
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        // MediaAccess interface. Does common checks and logging.
+        // Invokes real action if necessary.
+        //
+        ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Hint if files are downloaded or not.
+        */
+       bool        downloads() const { return _does_download; }
+
+        /**
+        * Protocol hint for MediaAccess.
+        **/
+        std::string protocol() const { return _url.getScheme(); }
+
+       /**
+        * Url used.
+        **/
+        Url url() const { return _url; }
+
+       /**
+        * Use concrete handler to attach the media.
+        *
+        * @param next try next available device in turn until end of device
+        * list is reached (for media which are accessible through multiple
+        * devices like cdroms).
+        *
+        * \throws MediaException
+        *
+        **/
+       void attach(bool next);
+
+       /**
+        * True if media is attached.
+        **/
+       virtual bool isAttached() const { return _mediaSource; }
+
+       /**
+        * Return the local directory that corresponds to medias url,
+        * no matter if media isAttached or not. Files requested will
+        * be available at 'localRoot() + filename' or better
+        * 'localPath( filename )'.
+        *
+        * Returns empty pathname if E_bad_attachpoint
+        **/
+       Pathname localRoot() const;
+
+       /**
+        * Files provided will be available at 'localPath(filename)'.
+        *
+        * Returns empty pathname if E_bad_attachpoint
+        **/
+         Pathname localPath( const Pathname & pathname ) const;
+
+        /**
+        * Use concrete handler to isconnect media.
+        *
+        * This is useful for media which e.g. holds open a connection to a
+        * server like FTP. After calling disconnect() the media object still is
+        * valid and files are present.
+        *
+        * After calling disconnect() it's not possible to call provideFile() or
+        * provideDir() anymore.
+        *
+        * \throws MediaException
+        *
+        **/
+        void disconnect();
+
+       /**
+        * Use concrete handler to release the media.
+        * @param eject Device to physically eject. None if empty.
+        *
+        * \throws MediaException
+        **/
+       void release( const std::string & ejectDev = "" );
+
+       /**
+        * Use concrete handler to provide file denoted by path below
+        * 'localRoot'. Filename is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideFile( Pathname filename ) const;
+
+       /**
+        * Call concrete handler to provide a copy of a file under a different place
+         * in the file system (usually not under attach point) as a copy.
+         * Media must be attached before by callee.
+         *
+         * @param srcFilename    Filename of source file on the media
+         * @param targetFilename Filename for the target in the file system
+        *
+        * \throws MediaException
+        *
+        **/
+        void provideFileCopy( Pathname srcFilename, Pathname targetFilename) const;
+
+       /**
+        * Use concrete handler to provide directory denoted
+        * by path below 'localRoot' (not recursive!).
+        * dirname is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDir( Pathname dirname ) const;
+
+       /**
+        * Use concrete handler to provide directory tree denoted
+        * by path below 'localRoot' (recursive!!).
+        * dirname is interpreted relative to the
+        * attached url and a path prefix is preserved.
+        *
+        * \throws MediaException
+        *
+        **/
+       void provideDirTree( Pathname dirname ) const;
+
+       /**
+        * Remove filename below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseFile( const Pathname & filename ) const { return releasePath( filename ); }
+
+       /**
+        * Remove directory tree below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releaseDir( const Pathname & dirname ) const { return releasePath( dirname ); }
+
+       /**
+        * Remove pathname below localRoot IFF handler downloads files
+        * to the local filesystem. Never remove anything from media.
+        *
+        * If pathname denotes a directory it is recursively removed.
+        * If pathname is empty or '/' everything below the localRoot
+        * is recursively removed.
+        * If pathname denotes a file it is unlinked.
+        *
+        * \throws MediaException
+        *
+        **/
+       void releasePath( Pathname pathname ) const;
+
+        /*
+         * set a deltafile to be used in the next download
+         */
+       void setDeltafile( const Pathname &filename = Pathname()) const;
+
+       /*
+        * return the deltafile set with setDeltafile()
+        */
+       Pathname deltafile () const;
+   
+    public:
+
+       /**
+        * Return content of directory on media via retlist. If dots is false
+        * entries starting with '.' are not reported.
+        *
+        * The request is forwarded to the concrete handler,
+        * which may atempt to retieve the content e.g. via 'readdir'
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+        void dirInfo( std::list<std::string> & retlist,
+                      const Pathname & dirname, bool dots = true ) const;
+
+       /**
+        * Basically the same as dirInfo above. The content is returned as
+        * filesystem::DirContent, which includes name and filetype of each directory
+        * entry. Retrieving the filetype usg. requires an additional ::stat call for
+        * each entry, thus it's more expensive than a simple readdir.
+        *
+        * <B>Caution:</B> This is not supported by all media types.
+        * Be prepared to handle E_not_supported_by_media.
+        *
+        * \throws MediaException
+        *
+        **/
+       void dirInfo( filesystem::DirContent & retlist,
+                      const Pathname & dirname, bool dots = true ) const;
+
+        /**
+         * check if a file exists
+         *
+         * Asserted that url is a file and not a dir.
+         *
+         * \throws MediaException
+         *
+         **/
+        bool doesFileExist( const Pathname & filename ) const;
+
+        /**
+         * Check if the media has one more device available for attach(true).
+         */
+        virtual bool hasMoreDevices();
+
+        /**
+         * Fill in a vector of detected ejectable devices and the index of the
+         * currently attached device within the vector. The contents of the vector
+         * are the device names (/dev/cdrom and such).
+         *
+         * \param devices  vector to load with the device names
+         * \param index    index of the currently used device in the devices vector
+         */
+        virtual void
+        getDetectedDevices(std::vector<std::string> & devices,
+                           unsigned int & index) const;
+};
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+
+#endif // ZYPP_MEDIA_MEDIAHANDLERL_H
+
+
diff --git a/zypp/media/MediaISO.cc b/zypp/media/MediaISO.cc
new file mode 100644 (file)
index 0000000..9accb70
--- /dev/null
@@ -0,0 +1,362 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaISO.cc
+ *
+ */
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/media/Mount.h"
+
+#include "zypp/media/MediaISO.h"
+
+
+#define LOSETUP_TOOL_PATH "/sbin/losetup"
+
+using std::string;
+using std::endl;
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // MediaISO Url:
+    //
+    //   Schema: iso
+    //   Path name: subdir to the location of desired files inside
+    //              of the ISO.
+    //   Query parameters:
+    //     url:        The iso filename source media url pointing
+    //                 to a directory containing the ISO file.
+    //     mnt:        Prefered attach point for source media url.
+    //     iso:        The name of the iso file.
+    //     filesystem: Optional, defaults to "auto".
+    //
+    ///////////////////////////////////////////////////////////////////
+    MediaISO::MediaISO(const Url      &url_r,
+                       const Pathname &attach_point_hint_r)
+      : MediaHandler(url_r, attach_point_hint_r,
+                     url_r.getPathName(), // urlpath below attachpoint
+                     false)               // does_download
+    {
+      MIL << "MediaISO::MediaISO(" << url_r << ", "
+          << attach_point_hint_r << ")" << std::endl;
+
+      _isofile    = _url.getQueryParam("iso");
+      if( _isofile.empty())
+      {
+        ERR << "Media url does not contain iso filename" << std::endl;
+        ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+      }
+
+      _filesystem = _url.getQueryParam("filesystem");
+      if( _filesystem.empty())
+        _filesystem = "auto";
+
+      std::string arg;
+      zypp::Url   src;
+      try
+      {
+        // this percent-decodes the query parameter, it must be later encoded
+        // again before used in a Url object
+        arg = _url.getQueryParam("url");
+        if( arg.empty() && _isofile.dirname().absolute())
+        {
+          src = std::string("dir:///");
+          src.setPathName(url::encode(_isofile.dirname().asString(), URL_SAFE_CHARS));
+          _isofile = _isofile.basename();
+        }
+        else
+        {
+          src = url::encode(arg, URL_SAFE_CHARS);
+        }
+      }
+      catch(const zypp::url::UrlException &e)
+      {
+        ZYPP_CAUGHT(e);
+        ERR << "Unable to parse iso filename source media url" << std::endl;
+       MediaBadUrlException ne(_url);
+       ne.remember(e);
+       ZYPP_THROW(ne);
+      }
+      if( !src.isValid())
+      {
+        ERR << "Invalid iso filename source media url" << std::endl;
+        ZYPP_THROW(MediaBadUrlException(src));
+      }
+      if( src.getScheme() == "iso")
+      {
+        ERR << "ISO filename source media url with iso scheme (nested iso): "
+            << src.asString() << std::endl;
+        ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
+      }
+      else
+      if( !(src.getScheme() == "hd"   ||
+            src.getScheme() == "dir"  ||
+            src.getScheme() == "file" ||
+            src.getScheme() == "nfs"  ||
+            src.getScheme() == "nfs4" ||
+            src.getScheme() == "smb"  ||
+            src.getScheme() == "cifs"))
+      {
+        ERR << "ISO filename source media url scheme is not supported: "
+            << src.asString() << std::endl;
+        ZYPP_THROW(MediaUnsupportedUrlSchemeException(src));
+      }
+
+      MediaManager manager;
+
+      _parentId = manager.open(src, _url.getQueryParam("mnt"));
+    }
+
+    // ---------------------------------------------------------------
+    MediaISO::~MediaISO()
+    {
+      try
+      {
+        release();
+
+        if( _parentId)
+        {
+          DBG << "Closing parent handler..." << std::endl;
+          MediaManager manager;
+          if(manager.isOpen(_parentId))
+            manager.close(_parentId);
+          _parentId = 0;
+        }
+      }
+      catch( ... )
+      {}
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaISO::isAttached() const
+    {
+      return checkAttached(false);
+    }
+
+    // ---------------------------------------------------------------
+    string MediaISO::findUnusedLoopDevice()
+    {
+      const char* argv[] =
+      {
+        LOSETUP_TOOL_PATH,
+        "-f",
+        NULL
+      };
+      ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
+
+      string out = losetup.receiveLine();
+      string device = out.substr(0, out.size() - 1); // remove the trailing endl
+      for(; out.length(); out = losetup.receiveLine())
+        DBG << "losetup: " << out;
+
+      if (losetup.close() != 0)
+      {
+        ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
+        ZYPP_THROW(MediaNoLoopDeviceException(_url));
+      }
+
+      DBG << "found " << device << endl;
+      return device;
+    }
+
+    // ---------------------------------------------------------------
+    void MediaISO::attachTo(bool next)
+    {
+      if(next)
+        ZYPP_THROW(MediaNotSupportedException(_url));
+
+      MediaManager manager;
+      manager.attach(_parentId);
+
+      try
+      {
+        manager.provideFile(_parentId, _isofile);
+      }
+      catch(const MediaException &e1)
+      {
+        ZYPP_CAUGHT(e1);
+        try
+        {
+          manager.release(_parentId);
+        }
+        catch(const MediaException &e2)
+        {
+          ZYPP_CAUGHT(e2);
+        }
+
+       MediaMountException e3(
+          "Unable to find iso filename on source media",
+          _url.asString(), attachPoint().asString()
+        );
+       e3.remember(e1);
+       ZYPP_THROW(e3);
+      }
+
+      // if the provided file is a symlink, expand it (#274651)
+      // (this will probably work only for file/dir and cd/dvd schemes)
+      Pathname isofile = expandlink(manager.localPath(_parentId, _isofile));
+      if( isofile.empty() || !PathInfo(isofile).isFile())
+      {
+        ZYPP_THROW(MediaNotSupportedException(_url));
+      }
+
+      //! \todo make this thread-safe - another thread might pick up the same device
+      string loopdev = findUnusedLoopDevice(); // (bnc #428009)
+
+      MediaSourceRef media( new MediaSource("iso",  loopdev));
+      PathInfo dinfo(loopdev);
+      if( dinfo.isBlk())
+      {
+        media->maj_nr = dinfo.major();
+        media->min_nr = dinfo.minor();
+      }
+      else
+        ERR << loopdev << " is not a block device" << endl;
+
+      AttachedMedia  ret( findAttachedMedia( media));
+      if( ret.mediaSource &&
+          ret.attachPoint &&
+          !ret.attachPoint->empty())
+      {
+        DBG << "Using a shared media "
+            << ret.mediaSource->name
+            << " attached on "
+            << ret.attachPoint->path
+            << std::endl;
+        removeAttachPoint();
+        setAttachPoint(ret.attachPoint);
+        setMediaSource(ret.mediaSource);
+        return;
+      }
+
+      std::string mountpoint = attachPoint().asString();
+      if( !isUseableAttachPoint(attachPoint()))
+      {
+        mountpoint = createAttachPoint().asString();
+        if( mountpoint.empty())
+          ZYPP_THROW( MediaBadAttachPointException(url()));
+        setAttachPoint( mountpoint, true);
+      }
+
+      std::string mountopts("ro,loop=" + loopdev);
+
+      Mount mount;
+      mount.mount(isofile.asString(), mountpoint,
+                  _filesystem, mountopts);
+
+      setMediaSource(media);
+
+      // wait for /etc/mtab update ...
+      // (shouldn't be needed)
+      int limit = 3;
+      bool mountsucceeded;
+      while( !(mountsucceeded=isAttached()) && --limit)
+      {
+        sleep(1);
+      }
+
+      if( !mountsucceeded)
+      {
+        setMediaSource(MediaSourceRef());
+        try
+        {
+          mount.umount(attachPoint().asString());
+          manager.release(_parentId);
+        }
+        catch (const MediaException & excpt_r)
+        {
+          ZYPP_CAUGHT(excpt_r);
+        }
+        ZYPP_THROW(MediaMountException(
+          "Unable to verify that the media was mounted",
+          isofile.asString(), mountpoint
+        ));
+      }
+    }
+
+    // ---------------------------------------------------------------
+
+    void MediaISO::releaseFrom(const std::string & ejectDev)
+    {
+      Mount mount;
+      mount.umount(attachPoint().asString());
+
+      if( _parentId)
+      {
+        // Unmounting the iso already succeeded,
+        // so don't let exceptions escape.
+        MediaManager manager;
+        try
+        {
+          manager.release(_parentId);
+        }
+        catch ( const Exception & excpt_r )
+        {
+          ZYPP_CAUGHT( excpt_r );
+          WAR << "Not been able to cleanup the parent mount." << endl;
+        }
+      }
+      // else:
+      // the media manager has reset the _parentId
+      // and will destroy the parent handler itself.
+    }
+
+    // ---------------------------------------------------------------
+    void MediaISO::getFile(const Pathname &filename) const
+    {
+      MediaHandler::getFile(filename);
+    }
+
+    // ---------------------------------------------------------------
+    void MediaISO::getDir(const Pathname &dirname,
+                           bool            recurse_r) const
+    {
+      MediaHandler::getDir(dirname, recurse_r);
+    }
+
+    // ---------------------------------------------------------------
+    void MediaISO::getDirInfo(std::list<std::string> &retlist,
+                               const Pathname         &dirname,
+                               bool                    dots) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    // ---------------------------------------------------------------
+    void MediaISO::getDirInfo(filesystem::DirContent &retlist,
+                               const Pathname         &dirname,
+                               bool                    dots) const
+    {
+      MediaHandler::getDirInfo(retlist, dirname, dots);
+    }
+
+    bool MediaISO::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+// vim: set ts=2 sts=2 sw=2 ai et:
+
diff --git a/zypp/media/MediaISO.h b/zypp/media/MediaISO.h
new file mode 100644 (file)
index 0000000..79715bd
--- /dev/null
@@ -0,0 +1,81 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaISO.h
+ *
+ */
+#ifndef ZYPP_MEDIA_MEDIAISO_H
+#define ZYPP_MEDIA_MEDIAISO_H
+
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/MediaManager.h"
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaISO
+    //
+    /**
+     * @short Implementation class for ISO MediaHandler
+     * @see MediaHandler
+     **/
+    class MediaISO : public MediaHandler
+    {
+      private:
+        Pathname      _isofile;
+        MediaAccessId _isosource;
+        std::string   _filesystem;
+
+      private:
+        std::string findUnusedLoopDevice();
+
+      protected:
+
+       virtual void attachTo (bool next = false);
+        virtual void releaseFrom( const std::string & ejectDev = "" );
+       virtual void getFile( const Pathname & filename ) const;
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+      public:
+
+        MediaISO(const Url      &url_r,
+                 const Pathname &attach_point_hint_r);
+
+        virtual
+        ~MediaISO();
+
+        virtual bool
+        isAttached() const;
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_MEDIA_MEDIAISO_H
+
+// vim: set ts=2 sts=2 sw=2 ai et:
+
diff --git a/zypp/media/MediaManager.cc b/zypp/media/MediaManager.cc
new file mode 100644 (file)
index 0000000..2097f96
--- /dev/null
@@ -0,0 +1,997 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaManager.cc
+ *
+*/
+#include <map>
+#include <list>
+#include <iostream>
+#include <typeinfo>
+
+#include "zypp/media/MediaException.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/Mount.h"
+#include "zypp/thread/Mutex.h"
+#include "zypp/thread/MutexLock.h"
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+    using zypp::thread::Mutex;
+    using zypp::thread::MutexLock;
+
+    //////////////////////////////////////////////////////////////////
+    namespace // anonymous
+    { ////////////////////////////////////////////////////////////////
+
+
+      // -------------------------------------------------------------
+      // STATIC
+      static Mutex  g_Mutex;
+
+
+      // -------------------------------------------------------------
+      struct ManagedMedia
+      {
+        ~ManagedMedia()
+        {}
+
+        ManagedMedia()
+          : desired (false)
+        {}
+
+        ManagedMedia(const ManagedMedia &m)
+          : desired (m.desired)
+          , handler (m.handler)
+          , verifier(m.verifier)
+        {}
+
+        ManagedMedia(const MediaAccessRef &h, const MediaVerifierRef &v)
+          : desired (false)
+          , handler (h)
+          , verifier(v)
+        {}
+
+        inline void
+        checkAttached(MediaAccessId id)
+        {
+          if( !handler->isAttached())
+          {
+            DBG << "checkAttached(" << id << ") not attached" << std::endl;
+            desired = false;
+            ZYPP_THROW(MediaNotAttachedException(
+              handler->url()
+            ));
+          }
+        }
+
+        inline void
+        checkDesired(MediaAccessId id)
+        {
+          checkAttached(id);
+
+          if( !desired)
+          {
+            try {
+              desired = verifier->isDesiredMedia(handler);
+            }
+            catch(const zypp::Exception &e) {
+              ZYPP_CAUGHT(e);
+              desired = false;
+            }
+
+            if( !desired)
+            {
+              DBG << "checkDesired(" << id << "): not desired (report by "
+                  << verifier->info() << ")" << std::endl;
+              ZYPP_THROW(MediaNotDesiredException(
+                handler->url()
+              ));
+            }
+
+            DBG << "checkDesired(" << id << "): desired (report by "
+                << verifier->info() << ")" << std::endl;
+          } else {
+            DBG << "checkDesired(" << id << "): desired (cached)" << std::endl;
+          }
+        }
+
+        bool             desired;
+        MediaAccessRef   handler;
+        MediaVerifierRef verifier;
+      };
+
+
+      // -------------------------------------------------------------
+      typedef std::map<MediaAccessId, ManagedMedia> ManagedMediaMap;
+
+      ////////////////////////////////////////////////////////////////
+    } // anonymous
+    //////////////////////////////////////////////////////////////////
+
+
+    //////////////////////////////////////////////////////////////////
+    std::string
+    MediaVerifierBase::info() const
+    {
+      return std::string(typeid((*this)).name());
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+    std::string
+    NoVerifier::info() const
+    {
+      return std::string("zypp::media::NoVerifier");
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+    class MediaManager_Impl
+    {
+    private:
+      friend class MediaManager;
+
+      MediaAccessId       last_accessid;
+      ManagedMediaMap     mediaMap;
+
+      MediaManager_Impl()
+        : last_accessid(0)
+      {}
+
+    public:
+      ~MediaManager_Impl()
+      {
+        MutexLock glock(g_Mutex);
+
+        try
+        {
+          // remove depending (iso) handlers first
+          ManagedMediaMap::iterator it;
+          bool found;
+          do
+          {
+            found = false;
+            for(it = mediaMap.begin(); it != mediaMap.end(); /**/)
+            {
+              if( it->second.handler->dependsOnParent())
+              {
+                found = true;
+                // let it forget its parent, we will
+                // destroy it later (in clear())...
+                it->second.handler->resetParentId();
+                mediaMap.erase( it++ ); // postfix! Incrementing before erase
+              } else {
+                ++it;
+              }
+            }
+          } while(found);
+
+          // remove all other handlers
+          mediaMap.clear();
+        }
+        catch( ... )
+        {}
+      }
+
+      inline MediaAccessId
+      nextAccessId()
+      {
+        return ++last_accessid;
+      }
+
+      inline bool
+      hasId(MediaAccessId accessId) const
+      {
+        return mediaMap.find(accessId) != mediaMap.end();
+      }
+
+      inline ManagedMedia &
+      findMM(MediaAccessId accessId)
+      {
+        ManagedMediaMap::iterator it( mediaMap.find(accessId));
+        if( it == mediaMap.end())
+        {
+          ZYPP_THROW(MediaNotOpenException(
+            "Invalid media access id " + str::numstring(accessId)
+          ));
+        }
+        return it->second;
+      }
+
+      static inline time_t
+      getMountTableMTime()
+      {
+        time_t mtime = zypp::PathInfo("/etc/mtab").mtime();
+        if( mtime <= 0)
+        {
+          WAR << "Failed to retrieve modification time of '/etc/mtab'"
+              << std::endl;
+        }
+        return mtime;
+      }
+
+      static inline MountEntries
+      getMountEntries()
+      {
+        // use "/etc/mtab" by default,
+        // fallback to "/proc/mounts"
+        return Mount::getEntries(/* "/etc/mtab" */);
+      }
+
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+    // STATIC
+    zypp::RW_pointer<MediaManager_Impl> MediaManager::m_impl(NULL);
+
+
+    //////////////////////////////////////////////////////////////////
+    MediaManager::MediaManager()
+    {
+      MutexLock glock(g_Mutex);
+      if( !m_impl)
+      {
+        m_impl.reset( new MediaManager_Impl());
+      }
+    }
+
+    // ---------------------------------------------------------------
+    MediaManager::~MediaManager()
+    {
+    }
+
+    // ---------------------------------------------------------------
+    MediaAccessId
+    MediaManager::open(const Url &url, const Pathname &preferred_attach_point)
+    {
+      MutexLock glock(g_Mutex);
+
+      // create new access handler for it
+      MediaAccessRef handler( new MediaAccess());
+      MediaVerifierRef verifier( new NoVerifier());
+      ManagedMedia tmp( handler, verifier);
+
+      tmp.handler->open(url, preferred_attach_point);
+
+      MediaAccessId nextId = m_impl->nextAccessId();
+
+      m_impl->mediaMap[nextId] = tmp;
+
+      DBG << "Opened new media access using id " << nextId
+          << " to " << url.asString() << std::endl;
+      return nextId;
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::close(MediaAccessId accessId)
+    {
+      MutexLock glock(g_Mutex);
+
+      //
+      // The MediaISO handler internally requests an accessId
+      // of a "parent" handler providing the iso file.
+      // The parent handler accessId is private to MediaISO,
+      // but the attached media source may be shared reference.
+      // This means, that if the accessId exactly matches the
+      // parent handler id, close was used on uninitialized
+      // accessId variable (or the accessId was guessed) and
+      // the close request to this id will be rejected here.
+      //
+      ManagedMediaMap::iterator m(m_impl->mediaMap.begin());
+      for( ; m != m_impl->mediaMap.end(); ++m)
+      {
+        if( m->second.handler->dependsOnParent(accessId, true))
+        {
+          ZYPP_THROW(MediaIsSharedException(
+            m->second.handler->url().asString()
+          ));
+        }
+      }
+
+      DBG << "Close to access handler using id "
+          << accessId << " requested" << std::endl;
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+      ref.handler->close();
+
+      m_impl->mediaMap.erase(accessId);
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isOpen(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMediaMap::iterator it( m_impl->mediaMap.find(accessId));
+      return it != m_impl->mediaMap.end() &&
+             it->second.handler->isOpen();
+    }
+
+    // ---------------------------------------------------------------
+    std::string
+    MediaManager::protocol(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->protocol();
+    }
+
+    // ---------------------------------------------------------------
+         bool
+    MediaManager::downloads(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->downloads();
+    }
+
+    // ---------------------------------------------------------------
+    Url
+    MediaManager::url(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->url();
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::addVerifier(MediaAccessId           accessId,
+                              const MediaVerifierRef &verifier)
+    {
+      MutexLock glock(g_Mutex);
+
+      if( !verifier)
+        ZYPP_THROW(MediaException("Invalid verifier reference"));
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.desired = false;
+      MediaVerifierRef(verifier).swap(ref.verifier);
+
+      DBG << "MediaVerifier change: id=" << accessId << ", verifier="
+          << verifier->info() << std::endl;
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::delVerifier(MediaAccessId accessId)
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      MediaVerifierRef verifier( new NoVerifier());
+      ref.desired  = false;
+      ref.verifier.swap(verifier);
+
+      DBG << "MediaVerifier change: id=" << accessId << ", verifier="
+          << verifier->info() << std::endl;
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::setAttachPrefix(const Pathname &attach_prefix)
+    {
+      MutexLock glock(g_Mutex);
+
+      return MediaHandler::setAttachPrefix(attach_prefix);
+    }
+
+    // ---------------------------------------------------------------
+    void MediaManager::attach(MediaAccessId accessId)
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      DBG << "attach(id=" << accessId << ")" << std::endl;
+
+      // try first mountable/mounted device
+      ref.handler->attach(false);
+      try
+      {
+        ref.checkDesired(accessId);
+        return;
+      }
+      catch (const MediaException & ex)
+      {
+        ZYPP_CAUGHT(ex);
+
+        if (!ref.handler->hasMoreDevices())
+          ZYPP_RETHROW(ex);
+
+        if (ref.handler->isAttached())
+          ref.handler->release();
+      }
+
+      MIL << "checkDesired(" << accessId << ") of first device failed,"
+        " going to try others with attach(true)" << std::endl;
+
+      while (ref.handler->hasMoreDevices())
+      {
+        try
+        {
+          // try to attach next device
+          ref.handler->attach(true);
+          ref.checkDesired(accessId);
+          return;
+        }
+        catch (const MediaNotDesiredException & ex)
+        {
+          ZYPP_CAUGHT(ex);
+
+          if (!ref.handler->hasMoreDevices())
+          {
+            MIL << "No desired media found after trying all detected devices." << std::endl;
+            ZYPP_RETHROW(ex);
+          }
+
+          AttachedMedia media(ref.handler->attachedMedia());
+          DBG << "Skipping " << media.mediaSource->asString() << ": not desired media." << std::endl;
+
+          ref.handler->release();
+        }
+        catch (const MediaException & ex)
+        {
+          ZYPP_CAUGHT(ex);
+
+          if (!ref.handler->hasMoreDevices())
+            ZYPP_RETHROW(ex);
+
+          AttachedMedia media(ref.handler->attachedMedia());
+          DBG << "Skipping " << media.mediaSource->asString() << " because of exception thrown by attach(true)" << std::endl;
+
+          if (ref.handler->isAttached()) ref.handler->release();
+        }
+      }
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::release(MediaAccessId accessId, const std::string & ejectDev)
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      DBG << "release(id=" << accessId;
+      if (!ejectDev.empty())
+        DBG << ", " << ejectDev;
+      DBG << ")" << std::endl;
+
+      if(!ejectDev.empty())
+      {
+        //
+        // release MediaISO handlers, that are using the one
+        // specified with accessId, because it provides the
+        // iso file and it will disappear now (forced release
+        // with eject).
+        //
+        ManagedMediaMap::iterator m(m_impl->mediaMap.begin());
+        for( ; m != m_impl->mediaMap.end(); ++m)
+        {
+          if( m->second.handler->dependsOnParent(accessId, false))
+          {
+            try
+            {
+              DBG << "Forcing release of handler depending on access id "
+                  << accessId << std::endl;
+              m->second.desired  = false;
+              m->second.handler->release();
+            }
+            catch(const MediaException &e)
+            {
+              ZYPP_CAUGHT(e);
+            }
+          }
+        }
+      }
+      ref.desired  = false;
+      ref.handler->release(ejectDev);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::releaseAll()
+    {
+      MutexLock glock(g_Mutex);
+
+      MIL << "Releasing all attached media" << std::endl;
+
+      ManagedMediaMap::iterator m(m_impl->mediaMap.begin());
+      for( ; m != m_impl->mediaMap.end(); ++m)
+      {
+        if( m->second.handler->dependsOnParent())
+          continue;
+
+        try
+        {
+          if(m->second.handler->isAttached())
+          {
+            DBG << "Releasing media id " << m->first << std::endl;
+            m->second.desired  = false;
+            m->second.handler->release();
+          }
+          else
+          {
+            DBG << "Media id " << m->first << " not attached " << std::endl;
+          }
+        }
+        catch(const MediaException & e)
+        {
+          ZYPP_CAUGHT(e);
+          ERR << "Failed to release media id " << m->first << std::endl;
+        }
+      }
+
+      MIL << "Exit" << std::endl;
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::disconnect(MediaAccessId accessId)
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.handler->disconnect();
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isAttached(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->isAttached();
+    }
+
+    // ---------------------------------------------------------------
+    bool MediaManager::isSharedMedia(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->isSharedMedia();
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isDesiredMedia(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      if( !ref.handler->isAttached())
+      {
+        ref.desired = false;
+      }
+      else
+      {
+        try {
+          ref.desired = ref.verifier->isDesiredMedia(ref.handler);
+        }
+        catch(const zypp::Exception &e) {
+          ZYPP_CAUGHT(e);
+          ref.desired = false;
+        }
+      }
+      DBG << "isDesiredMedia(" << accessId << "): "
+          << (ref.desired ? "" : "not ")
+          << "desired (report by "
+          << ref.verifier->info() << ")" << std::endl;
+      return ref.desired;
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isDesiredMedia(MediaAccessId           accessId,
+                                 const MediaVerifierRef &verifier) const
+    {
+      MutexLock glock(g_Mutex);
+
+      MediaVerifierRef v(verifier);
+      if( !v)
+        ZYPP_THROW(MediaException("Invalid verifier reference"));
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      bool desired = false;
+      if( ref.handler->isAttached())
+      {
+        try {
+          desired = v->isDesiredMedia(ref.handler);
+        }
+        catch(const zypp::Exception &e) {
+          ZYPP_CAUGHT(e);
+          desired = false;
+        }
+      }
+      DBG << "isDesiredMedia(" << accessId << "): "
+          << (desired ? "" : "not ")
+          << "desired (report by "
+          << v->info() << ")" << std::endl;
+      return desired;
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isChangeable(MediaAccessId accessId)
+    {
+      return url(accessId).getScheme() == "cd" || url(accessId).getScheme() == "dvd";
+    }
+
+    // ---------------------------------------------------------------
+    Pathname
+    MediaManager::localRoot(MediaAccessId accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      Pathname path;
+      path = ref.handler->localRoot();
+      return path;
+    }
+
+    // ---------------------------------------------------------------
+    Pathname
+    MediaManager::localPath(MediaAccessId accessId,
+                            const Pathname & pathname) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      Pathname path;
+      path = ref.handler->localPath(pathname);
+      return path;
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::provideFile(MediaAccessId   accessId,
+                              const Pathname &filename ) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkDesired(accessId);
+
+      ref.handler->provideFile(filename);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::setDeltafile(MediaAccessId   accessId,
+                              const Pathname &filename ) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkDesired(accessId);
+
+      ref.handler->setDeltafile(filename);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::provideDir(MediaAccessId   accessId,
+                             const Pathname &dirname) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkDesired(accessId);
+
+      ref.handler->provideDir(dirname);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::provideDirTree(MediaAccessId   accessId,
+                                 const Pathname &dirname) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkDesired(accessId);
+
+      ref.handler->provideDirTree(dirname);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::releaseFile(MediaAccessId   accessId,
+                              const Pathname &filename) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkAttached(accessId);
+
+      ref.handler->releaseFile(filename);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::releaseDir(MediaAccessId   accessId,
+                             const Pathname &dirname) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkAttached(accessId);
+
+      ref.handler->releaseDir(dirname);
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::releasePath(MediaAccessId   accessId,
+                              const Pathname &pathname) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      ref.checkAttached(accessId);
+
+      ref.handler->releasePath(pathname);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::dirInfo(MediaAccessId           accessId,
+                          std::list<std::string> &retlist,
+                          const Pathname         &dirname,
+                          bool                    dots) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      // FIXME: ref.checkDesired(accessId); ???
+      ref.checkAttached(accessId);
+
+      ref.handler->dirInfo(retlist, dirname, dots);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::dirInfo(MediaAccessId           accessId,
+                          filesystem::DirContent &retlist,
+                          const Pathname         &dirname,
+                          bool                    dots) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      // FIXME: ref.checkDesired(accessId); ???
+      ref.checkAttached(accessId);
+
+      ref.handler->dirInfo(retlist, dirname, dots);
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::doesFileExist(MediaAccessId  accessId, const Pathname & filename ) const
+    {
+      MutexLock glock(g_Mutex);
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      // FIXME: ref.checkDesired(accessId); ???
+      ref.checkAttached(accessId);
+
+      return ref.handler->doesFileExist(filename);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::getDetectedDevices(MediaAccessId accessId,
+                                     std::vector<std::string> & devices,
+                                     unsigned int & index) const
+    {
+      MutexLock glock(g_Mutex);
+      ManagedMedia &ref( m_impl->findMM(accessId));
+      return ref.handler->getDetectedDevices(devices, index);
+    }
+
+    // ---------------------------------------------------------------
+    // STATIC
+    time_t
+    MediaManager::getMountTableMTime()
+    {
+      MutexLock glock(g_Mutex);
+      return MediaManager_Impl::getMountTableMTime();
+    }
+
+    // ---------------------------------------------------------------
+    // STATIC
+    MountEntries
+    MediaManager::getMountEntries()
+    {
+      MutexLock glock(g_Mutex);
+
+      return MediaManager_Impl::getMountEntries();
+    }
+
+    // ---------------------------------------------------------------
+    bool
+    MediaManager::isUseableAttachPoint(const Pathname &path,
+                                       bool            mtab) const
+    {
+      if( path.empty() || path == "/" || !PathInfo(path).isDir())
+        return false;
+
+      MutexLock glock(g_Mutex);
+
+      //
+      // check against our current attach points
+      //
+      ManagedMediaMap::const_iterator m(m_impl->mediaMap.begin());
+      for( ; m != m_impl->mediaMap.end(); ++m)
+      {
+        AttachedMedia ret = m->second.handler->attachedMedia();
+        if( ret.mediaSource && ret.attachPoint)
+        {
+          std::string mnt(ret.attachPoint->path.asString());
+          std::string our(path.asString());
+
+          if( our == mnt)
+          {
+            // already used as attach point
+            return false;
+          }
+          else
+          if( mnt.size() > our.size()   &&
+              mnt.at(our.size()) == '/' &&
+             !mnt.compare(0, our.size(), our))
+          {
+            // mountpoint is bellow of path
+            // (would hide the content)
+            return false;
+          }
+        }
+      }
+
+      if( !mtab)
+        return true;
+
+      //
+      // check against system mount entries
+      //
+      MountEntries  entries( m_impl->getMountEntries());
+      MountEntries::const_iterator e;
+      for( e = entries.begin(); e != entries.end(); ++e)
+      {
+        std::string mnt(Pathname(e->dir).asString());
+        std::string our(path.asString());
+
+        if( our == mnt)
+        {
+          // already used as mountpoint
+          return false;
+        }
+        else
+        if( mnt.size() > our.size()   &&
+            mnt.at(our.size()) == '/' &&
+           !mnt.compare(0, our.size(), our))
+        {
+          // mountpoint is bellow of path
+          // (would hide the content)
+          return false;
+        }
+      }
+
+      return true;
+    }
+
+    // ---------------------------------------------------------------
+    AttachedMedia
+    MediaManager::getAttachedMedia(MediaAccessId &accessId) const
+    {
+      MutexLock glock(g_Mutex);
+
+      ManagedMedia &ref( m_impl->findMM(accessId));
+
+      return ref.handler->attachedMedia();
+    }
+
+    // ---------------------------------------------------------------
+    AttachedMedia
+    MediaManager::findAttachedMedia(const MediaSourceRef &media) const
+    {
+      MutexLock glock(g_Mutex);
+
+      if( !media || media->type.empty())
+        return AttachedMedia();
+
+      ManagedMediaMap::const_iterator m(m_impl->mediaMap.begin());
+      for( ; m != m_impl->mediaMap.end(); ++m)
+      {
+        if( !m->second.handler->isAttached())
+          continue;
+
+        AttachedMedia ret = m->second.handler->attachedMedia();
+        if( ret.mediaSource && ret.mediaSource->equals( *media))
+            return ret;
+      }
+      return AttachedMedia();
+    }
+
+    // ---------------------------------------------------------------
+    void
+    MediaManager::forceReleaseShared(const MediaSourceRef &media)
+    {
+      MutexLock glock(g_Mutex);
+
+      if( !media || media->type.empty())
+        return;
+
+      ManagedMediaMap::iterator m(m_impl->mediaMap.begin());
+      for( ; m != m_impl->mediaMap.end(); ++m)
+      {
+        if( !m->second.handler->isAttached())
+          continue;
+
+        AttachedMedia ret = m->second.handler->attachedMedia();
+        if( ret.mediaSource && ret.mediaSource->equals( *media))
+        {
+          m->second.handler->release();
+          m->second.desired  = false;
+        }
+      }
+    }
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/media/MediaManager.h b/zypp/media/MediaManager.h
new file mode 100644 (file)
index 0000000..40ce775
--- /dev/null
@@ -0,0 +1,931 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaManager.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAMANAGER_H
+#define ZYPP_MEDIA_MEDIAMANAGER_H
+
+#include <zypp/media/MediaAccess.h>
+
+#include <zypp/base/Deprecated.h>
+#include <zypp/base/NonCopyable.h>
+#include <zypp/base/PtrTypes.h>
+#include <zypp/Pathname.h>
+#include <zypp/Url.h>
+
+#include <list>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    typedef zypp::RW_pointer<MediaAccess> MediaAccessRef;
+
+    // OBSOLETE HERE:
+    typedef MediaAccessId                 MediaId;
+    typedef unsigned int                  MediaNr;
+
+
+    ///////////////////////////////////////////////////////////////////
+    // forward declaration
+    class MountEntry;
+    class MediaManager_Impl;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaVerifierBase
+    //
+    /**
+     * Interface to implement a media verifier.
+     */
+    class MediaVerifierBase //: private zypp::NonCopyable
+    {
+    public:
+      MediaVerifierBase()
+      {}
+
+      virtual
+      ~MediaVerifierBase()
+      {}
+
+      /**
+       * Returns a string with some info about the verifier.
+       * By default, the type info name is returned.
+       */
+      virtual std::string
+      info() const;
+
+      /*
+      ** Check if the specified attached media contains
+      ** the desired media (e.g. SLES10 CD1).
+      */
+      virtual bool
+      isDesiredMedia(const MediaAccessRef &ref) = 0;
+    };
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : NoVerifier
+    //
+    /**
+     * Dummy default media verifier, which is always happy.
+     */
+    class NoVerifier : public MediaVerifierBase
+    {
+    public:
+      NoVerifier(): MediaVerifierBase()
+      {}
+
+      virtual
+      ~NoVerifier()
+      {}
+
+      /**
+       * Returns the "zypp::media::NoVerifier" string.
+       */
+      virtual std::string
+      info() const;
+
+      /*
+      ** Don't check if the specified attached media contains
+      ** the desired media number. Always return true.
+      */
+      virtual bool
+      isDesiredMedia(const MediaAccessRef &ref)
+      {
+        (void)ref;
+        return true;
+      }
+    };
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaVerifierRef
+    //
+    /**
+     * A shared reference to the MediaVerifier implementation.
+     */
+    typedef zypp::RW_pointer<MediaVerifierBase> MediaVerifierRef;
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaManager
+    //
+    /**
+     * Manages access to the 'physical' media, e.g CDROM drives,
+     * Disk volumes, directory trees, etc, using \ref MediaAccessUrl's.
+     *
+     * \note The MediaManager class is just an envelope around an
+     *       inner singleton like implementation.<br>
+     *       That is, you can create as many managers as you want,
+     *       also temporary in a function call.<br>
+     *       But <b>don't</b> declare static MediaManager instances,
+     *       unless you want to force (mutex) initialization order
+     *       problems!
+     *
+     * \section MediaAccessUrl Media Access Url
+     * The MediaManager uses several media access handlers (backends),
+     * that can be specified by a Media Access URL in its open() method.
+     *
+     * All URL's may contain following query parameters, that are
+     * reserved by the Source classes and unused/ignored by the media
+     * manager:
+     * - <tt>alias</tt>: A source specific media alias string.
+     *
+     * Currently, following access handlers (backends) are implemented:
+     *   - \ref MediaCD_Url
+     *   - \ref MediaDISK_Url
+     * .
+     *   - \ref MediaISO_Url
+     *   - \ref MediaDIR_Url
+     * .
+     *   - \ref MediaNFS_Url
+     *   - \ref MediaCIFS_Url
+     *   - \ref MediaCurl_Url
+     *
+     * \subsection MediaCD_Url MediaCD - CD/DVD drives (cd, dvd)
+     * The access handler for media on CD / DVD drives.
+     *   - Scheme:
+     *     - <b>cd</b>: Requires a drive supporting CD media.
+     *     - <b>dvd</b>: Prefers a drive supporting DVD media.
+     *   - Examples:
+     *     \code
+     *       "cd:/"
+     *       "cd:/?devices=/dev/hda,/dev/hdb"
+     *       "cd:/subdir?devices=/dev/hda,/dev/hdb"
+     *
+     *       "dvd:/"
+     *       "dvd:/?devices=/dev/hda,/dev/hdb"
+     *       "dvd:/subdir?devices=/dev/hda,/dev/hdb"
+     *     \endcode
+     *     Note: You can use either "dvd:/" (just path, no authority)
+     *           or "dvd:///" (path and empty authority).
+     *   - Query parameters:
+     *     - <tt>devices</tt>:
+     *       Optional parameter, containing a comma separated list of
+     *       block device names to use, e.g.: "/dev/sr0,/dev/sr1".
+     *       <br>
+     *       The device names will be verified using a HAL query. If one
+     *       of the provided devices is not usable (not a block device,
+     *       or does not support required media type), an exception is
+     *       thrown.
+     *       <br>
+     *       If the devices parameter is not provided (or empty), all
+     *       avaliable CD/DVD drives 'detected' using a HAL query. The
+     *       preferred drive (used as first drive) is the drive pointed
+     *       to by the symlink "/dev/dvd" ("dvd" scheme only) or
+     *       "/dev/cdrom".
+     *   - Authority:
+     *     A non-empty authority URL component (e.g. containing a host
+     *     name) is not allowed.
+     *   - Path name:
+     *     Mandatory URL component, that specifies a subdirectory on the
+     *     CD/DVD, where the desired files are located.
+     *
+     * \subsection MediaDISK_Url MediaDISK - HD disk volumes (hd)
+     * The access handler for media on a disk volume (partition).
+     *   - Scheme:
+     *     - <b>hd</b>
+     *   - Examples:
+     *     \code
+     *       "hd:/?device=/dev/hda1"
+     *       "hd:/subdir?device=/dev/sda1"
+     *       "hd:/subdir?device=/dev/sda1&filesystem=reiserfs"
+     *     \endcode
+     *     Note: You can use either "hd:/" (just path, no authority)
+     *     or "hd:///" (path and empty authority).
+     *   - Query parameters:
+     *     - <tt>device</tt>:
+     *       Mandatory parameter specifying the name of the block device of
+     *       the partition to mount.
+     *     - <tt>filesystem</tt>:
+     *       The name of the filesystem. Defaults to "auto".
+     *   - Authority:
+     *     A non-empty authority URL component is not allowed.
+     *   - Path name:
+     *     Mandatory URL component, that specifies a subdirectory on the disk
+     *     partition, where the desired files are located.
+     *
+     * \subsection MediaDIR_Url MediaDIR - Local directory tree (dir, file)
+     * The access handler to media stored in a local directory tree.
+     *   - Scheme:
+     *     - <b>dir</b>
+     *     - <b>file</b>
+     *   - Examples:
+     *     \code
+     *       "dir:/directory/name"
+     *       "file:/directory/name"
+     *     \endcode
+     *   - Query parameters:
+     *     none
+     *   - Authority:
+     *     A non-empty authority URL component (e.g. containing
+     *     a host name) is not allowed.
+     *   - Path name:
+     *     Mandatory URL component, that specifies a directory, where
+     *     the desired files are located.
+     *
+     * \subsection MediaISO_Url MediaISO - Loopback ISO images (iso)
+     * The access handler for media in a ISO image (loopback mount).
+     *   - Scheme:
+     *     - <b>iso</b>
+     *   - Examples:
+     *     \code
+     *       "iso:/?iso=/path/to/CD1.iso"
+     *       "iso:/?iso=CD1.iso&url=dir:/path/to"
+     *
+     *       "iso:/?iso=CD1.iso&url=nfs://server/path/to/media"
+     *       "iso:/?iso=CD1.iso&url=hd:/?device=/dev/hda"
+     *
+     *        "iso:/subdir?iso=DVD1.iso&url=nfs://nfs-server/directory&mnt=/nfs/attach/point&filesystem=udf"
+     *     \endcode
+     *   - Query parameters:
+     *     - <tt>iso</tt>:
+     *       Mandatory parameter specifying the name of the iso file.<br>
+     *       If the url parameter is missed, the iso parameter has to contain
+     *       an absolute iso file name.
+     *     - <tt>url</tt>:
+     *       Optional parameter specifying the URL to the directory containing
+     *       the iso file.<br>
+     *       The supported URL schemes are: <i><b>hd</b>, <b>dir</b>,
+     *       <b>file</b>, <b>nfs</b>, <b>nfs4</b>, <b>smb</b>, <b>cifs</b>.</i>
+     *     - <tt>mnt</tt>:
+     *       Optional parameter specifying the prefered attach point for the
+     *       source media url.
+     *     - <tt>filesystem</tt>:
+     *       Optional name of the filesystem used in the iso file. Defaults
+     *       to "auto".
+     *   - Authority:
+     *     A non-empty authority URL component is not allowed.
+     *   - Path name:
+     *     Mandatory URL component, that specifies a subdirectory inside of
+     *     the iso file, where the desired files are located.
+     *
+     * \subsection MediaNFS_Url MediaNFS  - NFS directory tree (nfs)
+     * The access handler for media on NFS exported directory tree.
+     *   - Scheme:
+     *     - <b>nfs</b>
+     *     - <b>nfs</b>
+     *   - Examples:
+     *     \code
+     *        "nfs://nfs-server/exported/path"
+     *        "nfs://nfs-server/exported/path?mountoptions=ro"
+     *        "nfs://nfs-server/exported/path&type=nfs4"
+     *        "nfs4://nfs-server/exported/path"
+     *     \endcode
+     *   - Query parameters:
+     *     - <tt>mountoptions</tt>:
+     *       The mount options separated by comma ','.
+     *       Default is the "ro" option.
+     *     - <tt>type=nfs4</tt>:
+     *       Whether to mount as nfs4. This is the default for scheme nfs4.
+      *   - Authority:
+     *     The authority component has to provide a hostname.
+     *     Username, password and port are currently ignored.
+     *   - Path name:
+     *     Mandatory URL component, that specifies the exported
+     *     (sub-)directory on the NFS server, where the desired
+     *     files are located.
+     *
+     * \subsection MediaCIFS_Url MediaCIFS - CIFS/SMB directory tree (cifs, smb)
+     * The access handler for media in a CIFS/SMB shared directory tree.
+     *   - Scheme:
+     *     - <b>cifs</b>
+     *     - <b>smb</b>
+     *   - Examples:
+     *     \code
+     *       "cifs://servername/share/path/on/the/share"
+     *       "cifs://username:passwd@servername/share/path/on/the/share?mountoptions=ro"
+     *       "cifs://username:passwd@servername/share/path/on/the/share?mountoptions=noguest"
+     *       "smb://servername/share/path/on/the/share"
+     *       "smb://username:passwd@servername/share/path/on/the/share?mountoptions=ro"
+     *     \endcode
+     *     Note: There is no difference between cifs and smb scheme
+     *     (any more). In both cases the 'cifs' filesystem is used.
+     *   - Query parameters:
+     *     - <tt>mountoptions</tt>:
+     *       The mount options separated by a comma ','. Default are the
+     *       "ro" and "guest" options. Specify "noguest" to turn off
+     *       "guest". This is necessary if Samba is configured to reject
+     *       guest connections.
+     *     - <tt>workgroup</tt> or <tt>domain</tt>:
+     *       The name of the workgroup.
+     *     - <tt>username</tt>:
+     *       Alternative username to username in URL authority.
+     *     - <tt>password</tt>:
+     *       Alternative password to password in URL authority.
+     *     - <tt>user</tt>:
+     *       Alternative username (cifs specific variant)
+     *     - <tt>pass</tt>:
+     *       Alternative password (cifs specific variant)
+     *   - Authority:
+     *     The authority component has to provide a hostname. Optionally
+     *     also a username and password.
+     *   - Path name:
+     *     Mandatory URL component, that specifies the share name with
+     *     optional subdirectory, where the desired files are located.
+     *
+     * \subsection MediaCurl_Url MediaCurl - FTP/HTTP directory tree (ftp, http, https)
+     * The access handler to media directory tree on a ftp/http server.
+     *   - Scheme:
+     *     - <b>ftp</b>
+     *     - <b>http</b>
+     *     - <b>https</b>
+     *   - Examples:
+     *     \code
+     *       "ftp://server/relative/path/to/media/dir"
+     *       "ftp://server/%2fabsolute/path/to/media/dir"
+     *
+     *       "ftp://user:pass@server/path/to/media/dir"
+     *       "ftp://user:pass@server/%2f/home/user/path/to/media/dir"
+     *
+     *       "http://server/path/on/server"
+     *       "http://user:pass@server/path"
+     *       "https://user:pass@server/path?proxy=foo&proxyuser=me&proxypass=pw"
+     *     \endcode
+     *     Note: The "ftp" url scheme supports absolute and relative
+     *     paths to the default ftp server directory
+     *     (<a href="http://rfc.net/rfc1738.html">RFC1738, Section 3.2.2</a>).<br>
+     *     To use an absolute path, you have to prepend the path with an
+     *     additional slash, what results in a "/%2f" combination
+     *     (second "/" encoded to "%2f") at the begin of the URL path.
+     *     <br>
+     *     This is important, especially in user authenticated ftp,
+     *     where the users home is usually the default directory of the
+     *     server (except when the server chroots into the users home
+     *     directory).
+     *     <br>
+     *     For example, if the user "user" has a home directory
+     *     "/home/user", you can use either an URL with a relative path
+     *     to the home directory "ftp://user:pass@server/path/to/media"
+     *     or the absolute path
+     *     "ftp://user:pass@server/%2fhome/user/path/to/media" -- both
+     *     URLs points to the same directory on the server.
+     *   - Query parameters:
+     *     - <tt>cookies</tt>:
+     *       Turn off using cookies by setting it to "0" (or false, no, off).
+     *     - <tt>proxy</tt>:
+     *       A proxy hostname or hostname and port separated by ':'.
+     *     - <tt>proxyport</tt>:
+     *       Alternative way to provide the proxy port.
+     *     - <tt>proxyuser</tt>:
+     *       The proxy username.
+     *     - <tt>proxypass</tt>:
+     *       The proxy password.
+     *     - <tt>ssl_capath</tt>:
+     *       The absolute CA directory to use, default is /etc/ssl/certs.
+     *     - <tt>ssl_verify</tt>: Flag to modify the ssl verify behaviour.
+     *       Valid values are: 'yes', 'no' and a comma separated list of
+     *       'host' and 'peer' flags.
+     *       - 'no':
+     *         disables ssl verify
+     *       - 'yes':
+     *         enables ssl verify, this is the default
+     *         and is equivalent to 'host,peer'.
+     *       - 'host': The server is considered the intended one, when the
+     *         'Common Name' field or a 'Subject Alternate Name' field in
+     *         the certificate matches the host name in the URL.
+     *       - 'peer': Verifies whether the certificate provided by the
+     *         server is authentic against the chain of digital signatures
+     *         found in <tt>ssl_capath</tt>.
+     *     - <tt>timeout</tt>:
+     *       Transfer timeout in seconds between 0 and 3600, 0 disables
+     *       the timeout, default timeout is 180 seconds.
+     *     - <tt>auth</tt>: A comma separated list of http authentication
+     *       method names to use: 'basic', 'digest', 'ntlm', 'negotiate',
+     *       'spnego', 'gssnego'.
+     *       Note, that this list depends on the list of methods supported
+     *       by the curl library.
+     *     - <tt>mediahandler</tt>: Set the mediahandler for this url
+     *     Valid values are: 'curl', 'multicurl', 'aria2c'
+     *   - Authority:
+     *     The authority component has to provide a hostname. Optionally
+     *     also a username and password. In case of the 'ftp' scheme,
+     *     username and password defaults to 'anonymous' and 'yast2@'.
+     *   - Path name:
+     *     Mandatory URL component, that specifies the path name on the
+     *     server, where the desired files are located.
+     *
+     *   Proxy settings: If no proxy settings are present in tha URLs
+     *   query parameters, the media handler reads the system wide proxy
+     *   settings from the <tt>/etc/sysconfig/proxy</tt> file.
+     *   If a proxy setting was present, but the proxy password not,
+     *   it attempts to read the <tt>proxy-user</tt> variable from the
+     *   <tt>~/.curlrc</tt> (<tt>/root/.curlrc</tt>) file.
+     *   <br>
+     *   If no proxy setting was present, then libzypp does not pass any
+     *   proxy settings to curl, but curl fallbacks to use the content of
+     *   the <tt>http_proxy</tt>, <tt>ftp_proxy</tt>, etc environment
+     *   variables.
+     *
+     * \subsection MediaPlugin_Url MediaPlugin - custom media handler
+     * Media access is delegated to a script located in the libzypp
+     * media plugin directory. The URLs query options are translated
+     * into commandline arguments passed to the script.
+     *   - Scheme:
+     *     - <b>plugin</b>
+     *   - Examples:
+     *     \code
+     *       "plugin:script?loptv=lvalue&v=optv&lopt=&o&=foo"
+     *                      \__________/ \____/ \___/ | \_/
+     *                __________/__    ____/_    _|_  \   \___
+     *              /              \ /       \ /    \ /\ /    \
+     *       script --loptv "lvalue" -v "optv" --lopt -o -- foo
+     *     \endcode
+     *   - Query parameters:
+     *     - The URLs query options are translated into commandline
+     *       arguments passed to the script.
+     *     - \b Note: No option may appear twice, as the <tt>(option,value)</tt>
+     *       pairs are stored in a hash.
+     *     - \b Note: The order in which the query options are passes to the
+     *       script is arbitrary, except for an option with an empty key, which
+     *       is translated into <tt>'-- value'</tt> and passed as final option.
+     *     - <tt>'c[=[value]]'</tt> ist passed as <tt>'-c [value]'</tt>
+     *     - <tt>'word[=[value]]'</tt> ist passed as <tt>'--word [value]'</tt>
+     *     - <tt>'[=value]'</tt> ist passed as last args as <tt>'-- [value]'</tt>
+     *   - \c script<->libzypp communication:
+     *     - \TODO to be documented.
+     */
+    class MediaManager: private zypp::base::NonCopyable
+    {
+    public:
+      /**
+       * Creates a MediaManager envelope instance.
+       *
+       * In the case, that the inner implementation is not already
+       * allocated, and the MediaManager constructor was unable to
+       * allocate it, a std::bad_alloc exception is thrown.
+       *
+       * All further instances increase the use counter only.
+       *
+       * \throws std::bad_alloc
+       */
+      MediaManager();
+
+      /**
+       * Destroys MediaManager envelope instance.
+       * Decreases the use counter of the inner implementation.
+       */
+      ~MediaManager();
+
+      /**
+       * Opens the media access for specified with the url.
+       *
+       * If the \p preferred_attach_point parameter does not
+       * point to a usable attach point directory, the media
+       * manager automatically creates a temporary attach
+       * point in a default directory. This default directory
+       * can be changed using setAttachPrefix() function.
+       *
+       * Remember to close() each id you've opened and not
+       * need any more. It is like a new and delete!
+       *
+       * \param  url The \ref MediaAccessUrl.
+       * \param  preferred_attach_point The preferred, already
+       *         existing directory, where the media should be
+       *         attached.
+       * \return a new media access id.
+       * \throws std::bad_alloc
+       * \throws MediaException
+       */
+      MediaAccessId
+      open(const Url &url, const Pathname & preferred_attach_point = "");
+
+      /**
+       * Close the media access with specified id.
+       * \param accessId The media access id to close.
+       */
+      void
+      close(MediaAccessId accessId);
+
+      /**
+       * Query if the media access is open / exists.
+       *
+       * \param accessId The media access id to query.
+       * \return true, if access id is known and open.
+       */
+      bool
+      isOpen(MediaAccessId accessId) const;
+
+      /**
+       * Query the protocol name used by the media access
+       * handler. Similar to url().getScheme().
+       *
+       * \param accessId The media access id to query.
+       * \return The protocol name used by the media access
+       *         handler, otherwise 'unknown'.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      std::string
+      protocol(MediaAccessId accessId) const;
+
+      /**
+       * Hint if files are downloaded or not.
+       * \param accessId The media access id to query.
+       * \return True, if provideFile downloads files.
+       */
+      bool
+      downloads(MediaAccessId accessId) const;
+
+     /** \deprecated Use \ref Url::schemeIsDownloading */
+      static
+      ZYPP_DEPRECATED bool downloads(const Url &url)
+      { return url.schemeIsDownloading(); }
+
+      /**
+       * Returns the \ref MediaAccessUrl of the media access id.
+       *
+       * \param accessId The media access id to query.
+       * \return The \ref MediaAccessUrl used by the media access id.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      Url
+      url(MediaAccessId accessId) const;
+
+    public:
+      /**
+       * Add verifier implementation for the specified media id.
+       * By default, the NoVerifier is used.
+       *
+       * \param accessId A media access id.
+       * \param verifier The new verifier.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      void
+      addVerifier(MediaAccessId accessId,
+                  const MediaVerifierRef &verifier);
+
+      /**
+       * Remove verifier for specified media id.
+       * It resets the verifier to NoVerifier.
+       *
+       * \param accessId A media access id.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      void
+      delVerifier(MediaAccessId accessId);
+
+    public:
+      /**
+       * Set or resets the directory name, where the media manager
+       * handlers create their temporary attach points (see open()
+       * function).
+       * It has effect to newly created temporary attach points only.
+       *
+       * \param attach_prefix The new prefix for temporary attach
+       *        points, or empty pathname to reset to defaults.
+       * \return True on success, false if the \p attach_prefix
+       *         parameters contains a path name, that does not
+       *         point to a writable directory.
+       */
+      bool
+      setAttachPrefix(const Pathname &attach_prefix);
+
+      /**
+       * Attach the media using the concrete handler (checks all devices).
+       *
+       * Remember to release() or close() each id you've attached
+       * and not need any more. Attach is like an open of a file!
+       *
+       * \param accessId A media access id.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      void
+      attach(MediaAccessId accessId);
+
+      /** \deprecated Simply use \ref attach. */
+      ZYPP_DEPRECATED void attachDesiredMedia(MediaAccessId accessId)
+      { attach( accessId ); }
+
+      /**
+       * Release the attached media and optionally eject.
+       *
+       * If the \p ejectDev parameter is not empty all other access
+       * id's are released and the specified drive (CD/DVD drive) is
+       * ejected.
+       *
+       * \param accessId A media access id.
+       * \param ejectDev Device to eject. None if empty.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      void
+      release(MediaAccessId accessId, const std::string & ejectDev = "");
+
+      /**
+       * Release all attached media.
+       */
+      void
+      releaseAll();
+
+      /**
+       * Disconnect a remote media.
+       *
+       * This is useful for media which e.g. holds open a connection
+       * to a server like FTP. After calling disconnect() the media
+       * object (attach point) is still valid and files are present.
+       *
+       * But after calling disconnect() it's not possible to call
+       * fetch more data using the provideFile() or provideDir()
+       * functions anymore.
+       *
+       * \param accessId A media access id.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      void
+      disconnect(MediaAccessId accessId);
+
+      /**
+       * Check if media is attached or not.
+       *
+       * \param accessId A media access id.
+       * \return True if media is attached.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      bool
+      isAttached(MediaAccessId accessId) const;
+
+      /**
+       * Returns information if media is on a shared
+       * physical device or not.
+       *
+       * \param accessId A media access id.
+       * \return True if it is shared, false if not.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      bool
+      isSharedMedia(MediaAccessId accessId) const;
+
+      /**
+       * Ask the registered verifier if the attached
+       * media is the desired one or not.
+       *
+       * \param accessId A media access id.
+       * \return True if media is attached and desired
+       *         according to the actual verifier.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      bool
+      isDesiredMedia(MediaAccessId accessId) const;
+
+      /**
+       * Ask the specified verifier if the attached
+       * media is the desired one or not.
+       *
+       * \param accessId A media access id.
+       * \param verifier A verifier to use.
+       * \return True if media is attached and desired
+       *         according to the specified verifier.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      bool
+      isDesiredMedia(MediaAccessId           accessId,
+                     const MediaVerifierRef &verifier) const;
+
+      /**
+       * Simple check, based on media's URL scheme, telling whether the
+       * it is possible to physically change the media inside its drive, like
+       * CDs or DVDs. Useful to decide whether to request media change from
+       * user or not.
+       *
+       * \param accessId The media access id.
+       * \return <tt>false</tt> if the media is not changeable,
+       *         <tt>true</tt> otherwise.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      bool
+      isChangeable(MediaAccessId accessId);
+
+      /**
+       * Return the local directory that corresponds to medias url,
+       * no matter if media isAttached or not. Files requested will
+       * be available at 'localRoot() + filename' or even better
+       * 'localPath( filename )'
+       *
+       * \param accessId A media access id.
+       * \returns The directory name pointing to the media root
+       *          in local filesystem or an empty pathname if the
+       *          media is not attached.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      Pathname
+      localRoot(MediaAccessId accessId) const;
+
+      /**
+       * Shortcut for 'localRoot() + pathname', but returns an empty
+       * pathname if media is not attached.
+       * Files provided will be available at 'localPath(filename)'.
+       *
+       * \param accessId A media access id.
+       * \param pathname A path name relative to the localRoot().
+       * \returns The directory name in local filesystem pointing
+       *          to the desired relative pathname on the media
+       *          or an empty pathname if the media is not attached.
+       * \throws MediaNotOpenException for invalid access id.
+       */
+      Pathname
+      localPath(MediaAccessId accessId, const Pathname & pathname) const;
+
+    public:
+      /**
+       * Provide provide file denoted by relative path below of the
+       * 'attach point' of the specified media and the path prefix
+       * on the media.
+       *
+       * \param accessId  The media access id to use.
+       * \param filename  The filename to provide, relative to localRoot().
+       *
+       * \throws MediaNotOpenException in case of invalid access id.
+       * \throws MediaNotAttachedException in case, that the media is not attached.
+       * \throws MediaNotDesiredException in case, that the media verification failed.
+       * \throws MediaNotAFileException in case, that the requested filename is not a file.
+       * \throws MediaFileNotFoundException in case, that the requested filenamedoes not exists.
+       * \throws MediaWriteException in case, that the file can't be copied from from remote source.
+       * \throws MediaSystemException in case a system operation fails.
+       * \throws MediaException derived exception, depending on the url (handler).
+       */
+
+      void
+      provideFile(MediaAccessId   accessId,
+                  const Pathname &filename ) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      provideDir(MediaAccessId   accessId,
+                 const Pathname &dirname) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      provideDirTree(MediaAccessId  accessId,
+                     const Pathname &dirname) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      releaseFile(MediaAccessId   accessId,
+                  const Pathname &filename) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      releaseDir(MediaAccessId   accessId,
+                 const Pathname &dirname) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      releasePath(MediaAccessId   accessId,
+                  const Pathname &pathname) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      dirInfo(MediaAccessId           accessId,
+              std::list<std::string> &retlist,
+              const Pathname         &dirname,
+              bool                    dots = true) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      void
+      dirInfo(MediaAccessId           accessId,
+              filesystem::DirContent &retlist,
+              const Pathname         &dirname,
+              bool                   dots = true) const;
+
+      /**
+       * FIXME: see MediaAccess class.
+       */
+      bool doesFileExist(MediaAccessId  accessId,
+                         const Pathname & filename ) const;
+
+      /**
+       * Fill in a vector of detected ejectable devices and the index of the
+       * currently attached device within the vector. The contents of the vector
+       * are the device names (/dev/cdrom and such).
+       *
+       * \param accessId Medium id.
+       * \param devices  vector to load with the device names
+       * \param index    index of the currently used device in the devices vector
+       */
+      void
+      getDetectedDevices(MediaAccessId accessId,
+                         std::vector<std::string> & devices,
+                         unsigned int & index) const;
+
+      void
+      setDeltafile(MediaAccessId   accessId,
+                  const Pathname &filename ) const;
+
+    public:
+      /**
+       * Get the modification time of the /etc/mtab file.
+       * \return Modification time of the /etc/mtab file.
+       */
+      static time_t
+      getMountTableMTime();
+
+      /**
+       * Get current mount entries from /etc/mtab file.
+       * \return Current mount entries from /etc/mtab file.
+       */
+      static std::vector<MountEntry>
+      getMountEntries();
+
+      /**
+       * Check if the specified \p path is useable as
+       * attach point.
+       *
+       * \param path The attach point to check.
+       * \param mtab Whether to check against the mtab, too.
+       * \return True, if it is a directory and there are
+       *         no another attach points bellow of it.
+       */
+      bool
+      isUseableAttachPoint(const Pathname &path,
+                           bool            mtab=true) const;
+
+    private:
+      friend class MediaHandler;
+
+      /**
+       * \internal
+       * Return the attached media reference of the specified
+       * media access id. Used to resolve nested attachments
+       * as used in the MediaISO (iso-loop) handler.
+       * Causes temporary creation of a shared attachment
+       * (increases reference counters on attachedMedia).
+       * \param media A media access id.
+       */
+      AttachedMedia
+      getAttachedMedia(MediaAccessId &accessId) const;
+
+      /**
+       * \internal
+       * Called by media handler in while attach() to retrieve
+       * attached media reference matching the specified media
+       * source reference.
+       * Causes temporary creation of a shared attachment
+       * (increases reference counters on attachedMedia).
+       * \param media The media source reference to search for.
+       */
+      AttachedMedia
+      findAttachedMedia(const MediaSourceRef &media) const;
+
+      /**
+       * \internal
+       * Called by media handler in case of relase(eject=true)
+       * to release all access id's using the specified media.
+       * Causes temporary creation of a shared attachment
+       * (increases reference counters on attachedMedia).
+       * \param media The media source reference to release.
+       */
+      void
+      forceReleaseShared(const MediaSourceRef &media);
+
+    private:
+      /**
+       * Static reference to the implementation (singleton).
+       */
+      static zypp::RW_pointer<MediaManager_Impl> m_impl;
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_MEDIA_MEDIAMANAGER_H
+
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/media/MediaMultiCurl.cc b/zypp/media/MediaMultiCurl.cc
new file mode 100644 (file)
index 0000000..b0b9f5a
--- /dev/null
@@ -0,0 +1,1514 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaMultiCurl.cc
+ *
+*/
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <vector>
+#include <iostream>
+#include <algorithm>
+
+
+#include "zypp/ZConfig.h"
+#include "zypp/base/Logger.h"
+#include "zypp/media/MediaMultiCurl.h"
+#include "zypp/media/MetaLinkParser.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+class multifetchrequest;
+
+// Hack: we derive from MediaCurl just to get the storage space for
+// settings, url, curlerrors and the like
+
+class multifetchworker : MediaCurl {
+  friend class multifetchrequest;
+
+public:
+  multifetchworker(int no, multifetchrequest &request, const Url &url);
+  ~multifetchworker();
+  void nextjob();
+  void run();
+  bool checkChecksum();
+  bool recheckChecksum();
+  void disableCompetition();
+
+  void checkdns();
+  void adddnsfd(fd_set &rset, int &maxfd);
+  void dnsevent(fd_set &rset);
+
+  int _workerno;
+
+  int _state;
+  bool _competing;
+
+  size_t _blkno;
+  off_t _blkstart;
+  size_t _blksize;
+  bool _noendrange;
+
+  double _blkstarttime;
+  size_t _blkreceived;
+  off_t  _received;
+
+  double _avgspeed;
+  double _maxspeed;
+
+  double _sleepuntil;
+
+private:
+  void stealjob();
+
+  size_t writefunction(void *ptr, size_t size);
+  static size_t _writefunction(void *ptr, size_t size, size_t nmemb, void *stream);
+
+  size_t headerfunction(char *ptr, size_t size);
+  static size_t _headerfunction(void *ptr, size_t size, size_t nmemb, void *stream);
+
+  multifetchrequest *_request;
+  int _pass;
+  string _urlbuf;
+  off_t _off;
+  size_t _size;
+  Digest _dig;
+
+  pid_t _pid;
+  int _dnspipe;
+};
+
+#define WORKER_STARTING 0
+#define WORKER_LOOKUP   1
+#define WORKER_FETCH    2
+#define WORKER_DISCARD  3
+#define WORKER_DONE     4
+#define WORKER_SLEEP    5
+#define WORKER_BROKEN   6
+
+
+
+class multifetchrequest {
+public:
+  multifetchrequest(const MediaMultiCurl *context, const Pathname &filename, const Url &baseurl, CURLM *multi, FILE *fp, callback::SendReport<DownloadProgressReport> *report, MediaBlockList *blklist, off_t filesize);
+  ~multifetchrequest();
+
+  void run(std::vector<Url> &urllist);
+
+protected:
+  friend class multifetchworker;
+
+  const MediaMultiCurl *_context;
+  const Pathname _filename;
+  Url _baseurl;
+
+  FILE *_fp;
+  callback::SendReport<DownloadProgressReport> *_report;
+  MediaBlockList *_blklist;
+  off_t _filesize;
+
+  CURLM *_multi;
+
+  std::list<multifetchworker *> _workers;
+  bool _stealing;
+  bool _havenewjob;
+
+  size_t _blkno;
+  off_t _blkoff;
+  size_t _activeworkers;
+  size_t _lookupworkers;
+  size_t _sleepworkers;
+  double _minsleepuntil;
+  bool _finished;
+  off_t _totalsize;
+  off_t _fetchedsize;
+  off_t _fetchedgoodsize;
+
+  double _starttime;
+  double _lastprogress;
+
+  double _lastperiodstart;
+  double _lastperiodfetched;
+  double _periodavg;
+
+public:
+  double _timeout;
+  double _connect_timeout;
+  double _maxspeed;
+  int _maxworkers;
+};
+
+#define BLKSIZE                131072
+#define MAXURLS                10
+
+
+//////////////////////////////////////////////////////////////////////
+
+static double
+currentTime()
+{
+  struct timeval tv;
+  if (gettimeofday(&tv, NULL))
+    return 0;
+  return tv.tv_sec + tv.tv_usec / 1000000.;
+}
+
+size_t
+multifetchworker::writefunction(void *ptr, size_t size)
+{
+  size_t len, cnt;
+  if (_state == WORKER_BROKEN)
+    return size ? 0 : 1;
+
+  double now = currentTime();
+
+  len = size > _size ? _size : size;
+  if (!len)
+    {
+      // kill this job?
+      return size;
+    }
+
+  if (_blkstart && _off == _blkstart)
+    {
+      // make sure that the server replied with "partial content"
+      // for http requests
+      char *effurl;
+      (void)curl_easy_getinfo(_curl, CURLINFO_EFFECTIVE_URL, &effurl);
+      if (effurl && !strncasecmp(effurl, "http", 4))
+       {
+         long statuscode = 0;
+         (void)curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &statuscode);
+         if (statuscode != 206)
+           return size ? 0 : 1;
+       }
+    }
+
+  _blkreceived += len;
+  _received += len;
+
+  _request->_lastprogress = now;
+
+  if (_state == WORKER_DISCARD || !_request->_fp)
+    {
+      // block is no longer needed
+      // still calculate the checksum so that we can throw out bad servers
+      if (_request->_blklist)
+        _dig.update((const char *)ptr, len);
+      _off += len;
+      _size -= len;
+      return size;
+    }
+  if (fseeko(_request->_fp, _off, SEEK_SET))
+    return size ? 0 : 1;
+  cnt = fwrite(ptr, 1, len, _request->_fp);
+  if (cnt > 0)
+    {
+      _request->_fetchedsize += cnt;
+      if (_request->_blklist)
+        _dig.update((const char *)ptr, cnt);
+      _off += cnt;
+      _size -= cnt;
+      if (cnt == len)
+       return size;
+    }
+  return cnt;
+}
+
+size_t
+multifetchworker::_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  multifetchworker *me = reinterpret_cast<multifetchworker *>(stream);
+  return me->writefunction(ptr, size * nmemb);
+}
+
+size_t
+multifetchworker::headerfunction(char *p, size_t size)
+{
+  size_t l = size;
+  if (l > 9 && !strncasecmp(p, "Location:", 9))
+    {
+      string line(p + 9, l - 9);
+      if (line[l - 10] == '\r')
+       line.erase(l - 10, 1);
+      DBG << "#" << _workerno << ": redirecting to" << line << endl;
+      return size;
+    }
+  if (l <= 14 || l >= 128 || strncasecmp(p, "Content-Range:", 14) != 0)
+    return size;
+  p += 14;
+  l -= 14;
+  while (l && (*p == ' ' || *p == '\t'))
+    p++, l--;
+  if (l < 6 || strncasecmp(p, "bytes", 5))
+    return size;
+  p += 5;
+  l -= 5;
+  char buf[128];
+  memcpy(buf, p, l);
+  buf[l] = 0;
+  unsigned long long start, off, filesize;
+  if (sscanf(buf, "%llu-%llu/%llu", &start, &off, &filesize) != 3)
+    return size;
+  if (_request->_filesize == (off_t)-1)
+    {
+      WAR << "#" << _workerno << ": setting request filesize to " << filesize << endl;
+      _request->_filesize = filesize;
+      if (_request->_totalsize == 0 && !_request->_blklist)
+       _request->_totalsize = filesize;
+    }
+  if (_request->_filesize != (off_t)filesize)
+    {
+      DBG << "#" << _workerno << ": filesize mismatch" << endl;
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "filesize mismatch", CURL_ERROR_SIZE);
+    }
+  return size;
+}
+
+size_t
+multifetchworker::_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  multifetchworker *me = reinterpret_cast<multifetchworker *>(stream);
+  return me->headerfunction((char *)ptr, size * nmemb);
+}
+
+multifetchworker::multifetchworker(int no, multifetchrequest &request, const Url &url)
+: MediaCurl(url, Pathname())
+{
+  _workerno = no;
+  _request = &request;
+  _state = WORKER_STARTING;
+  _competing = false;
+  _off = _blkstart = 0;
+  _size = _blksize = 0;
+  _pass = 0;
+  _blkno = 0;
+  _pid = 0;
+  _dnspipe = -1;
+  _blkreceived = 0;
+  _received = 0;
+  _blkstarttime = 0;
+  _avgspeed = 0;
+  _sleepuntil = 0;
+  _maxspeed = _request->_maxspeed;
+  _noendrange = false;
+
+  Url curlUrl( clearQueryString(url) );
+  _urlbuf = curlUrl.asString();
+  _curl = _request->_context->fromEasyPool(_url.getHost());
+  if (_curl)
+    DBG << "reused worker from pool" << endl;
+  if (!_curl && !(_curl = curl_easy_init()))
+    {
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "curl_easy_init failed", CURL_ERROR_SIZE);
+      return;
+    }
+  try
+    {
+      setupEasy();
+    }
+  catch (Exception &ex)
+    {
+      curl_easy_cleanup(_curl);
+      _curl = 0;
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "curl_easy_setopt failed", CURL_ERROR_SIZE);
+      return;
+    }
+  curl_easy_setopt(_curl, CURLOPT_PRIVATE, this);
+  curl_easy_setopt(_curl, CURLOPT_URL, _urlbuf.c_str());
+  curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, &_writefunction);
+  curl_easy_setopt(_curl, CURLOPT_WRITEDATA, this);
+  if (_request->_filesize == off_t(-1) || !_request->_blklist || !_request->_blklist->haveChecksum(0))
+    {
+      curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, &_headerfunction);
+      curl_easy_setopt(_curl, CURLOPT_HEADERDATA, this);
+    }
+  // if this is the same host copy authorization
+  // (the host check is also what curl does when doing a redirect)
+  // (note also that unauthorized exceptions are thrown with the request host)
+  if (url.getHost() == _request->_context->_url.getHost())
+    {
+      _settings.setUsername(_request->_context->_settings.username());
+      _settings.setPassword(_request->_context->_settings.password());
+      _settings.setAuthType(_request->_context->_settings.authType());
+      if ( _settings.userPassword().size() )
+       {
+         curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
+         string use_auth = _settings.authType();
+         if (use_auth.empty())
+           use_auth = "digest,basic";        // our default
+         long auth = CurlAuthData::auth_type_str2long(use_auth);
+         if( auth != CURLAUTH_NONE)
+         {
+           DBG << "#" << _workerno << ": Enabling HTTP authentication methods: " << use_auth
+               << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
+           curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth);
+         }
+       }
+    }
+  checkdns();
+}
+
+multifetchworker::~multifetchworker()
+{
+  if (_curl)
+    {
+      if (_state == WORKER_FETCH || _state == WORKER_DISCARD)
+        curl_multi_remove_handle(_request->_multi, _curl);
+      if (_state == WORKER_DONE || _state == WORKER_SLEEP)
+       {
+#if LIBCURL_VERSION_NUMBER >= 0x071505
+         curl_easy_setopt(_curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)0);
+#endif
+         curl_easy_setopt(_curl, CURLOPT_PRIVATE, (void *)0);
+         curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, (void *)0);
+         curl_easy_setopt(_curl, CURLOPT_WRITEDATA, (void *)0);
+         curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, (void *)0);
+         curl_easy_setopt(_curl, CURLOPT_HEADERDATA, (void *)0);
+          _request->_context->toEasyPool(_url.getHost(), _curl);
+       }
+      else
+        curl_easy_cleanup(_curl);
+      _curl = 0;
+    }
+  if (_pid)
+    {
+      kill(_pid, SIGKILL);
+      int status;
+      while (waitpid(_pid, &status, 0) == -1)
+        if (errno != EINTR)
+         break;
+      _pid = 0;
+    }
+  if (_dnspipe != -1)
+    {
+      close(_dnspipe);
+      _dnspipe = -1;
+    }
+  // the destructor in MediaCurl doesn't call disconnect() if
+  // the media is not attached, so we do it here manually
+  disconnectFrom();
+}
+
+static inline bool env_isset(string name)
+{
+  const char *s = getenv(name.c_str());
+  return s && *s ? true : false;
+}
+
+void
+multifetchworker::checkdns()
+{
+  string host = _url.getHost();
+
+  if (host.empty())
+    return;
+
+  if (_request->_context->isDNSok(host))
+    return;
+
+  // no need to do dns checking for numeric hosts
+  char addrbuf[128];
+  if (inet_pton(AF_INET, host.c_str(), addrbuf) == 1)
+    return;
+  if (inet_pton(AF_INET6, host.c_str(), addrbuf) == 1)
+    return;
+
+  // no need to do dns checking if we use a proxy
+  if (!_settings.proxy().empty())
+    return;
+  if (env_isset("all_proxy") || env_isset("ALL_PROXY"))
+    return;
+  string schemeproxy = _url.getScheme() + "_proxy";
+  if (env_isset(schemeproxy))
+    return;
+  if (schemeproxy != "http_proxy")
+    {
+      std::transform(schemeproxy.begin(), schemeproxy.end(), schemeproxy.begin(), ::toupper);
+      if (env_isset(schemeproxy))
+       return;
+    }
+
+  DBG << "checking DNS lookup of " << host << endl;
+  int pipefds[2];
+  if (pipe(pipefds))
+    {
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "DNS pipe creation failed", CURL_ERROR_SIZE);
+      return;
+    }
+  _pid = fork();
+  if (_pid == pid_t(-1))
+    {
+      close(pipefds[0]);
+      close(pipefds[1]);
+      _pid = 0;
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "DNS checker fork failed", CURL_ERROR_SIZE);
+      return;
+    }
+  else if (_pid == 0)
+    {
+      close(pipefds[0]);
+      // XXX: close all other file descriptors
+      struct addrinfo *ai, aihints;
+      memset(&aihints, 0, sizeof(aihints));
+      aihints.ai_family = PF_UNSPEC;
+      int tstsock = socket(PF_INET6, SOCK_DGRAM, 0);
+      if (tstsock == -1)
+       aihints.ai_family = PF_INET;
+      else
+       close(tstsock);
+      aihints.ai_socktype = SOCK_STREAM;
+      aihints.ai_flags = AI_CANONNAME;
+      unsigned int connecttimeout = _request->_connect_timeout;
+      if (connecttimeout)
+       alarm(connecttimeout);
+      signal(SIGALRM, SIG_DFL);
+      if (getaddrinfo(host.c_str(), NULL, &aihints, &ai))
+        _exit(1);
+      _exit(0);
+    }
+  close(pipefds[1]);
+  _dnspipe = pipefds[0];
+  _state = WORKER_LOOKUP;
+}
+
+void
+multifetchworker::adddnsfd(fd_set &rset, int &maxfd)
+{
+  if (_state != WORKER_LOOKUP)
+    return;
+  FD_SET(_dnspipe, &rset);
+  if (maxfd < _dnspipe)
+    maxfd = _dnspipe;
+}
+
+void
+multifetchworker::dnsevent(fd_set &rset)
+{
+
+  if (_state != WORKER_LOOKUP || !FD_ISSET(_dnspipe, &rset))
+    return;
+  int status;
+  while (waitpid(_pid, &status, 0) == -1)
+    {
+      if (errno != EINTR)
+        return;
+    }
+  _pid = 0;
+  if (_dnspipe != -1)
+    {
+      close(_dnspipe);
+      _dnspipe = -1;
+    }
+  if (!WIFEXITED(status))
+    {
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "DNS lookup failed", CURL_ERROR_SIZE);
+      _request->_activeworkers--;
+      return;
+    }
+  int exitcode = WEXITSTATUS(status);
+  DBG << "#" << _workerno << ": DNS lookup returned " << exitcode << endl;
+  if (exitcode != 0)
+    {
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "DNS lookup failed", CURL_ERROR_SIZE);
+      _request->_activeworkers--;
+      return;
+    }
+  _request->_context->setDNSok(_url.getHost());
+  nextjob();
+}
+
+bool
+multifetchworker::checkChecksum()
+{
+  // DBG << "checkChecksum block " << _blkno << endl;
+  if (!_blksize || !_request->_blklist)
+    return true;
+  return _request->_blklist->verifyDigest(_blkno, _dig);
+}
+
+bool
+multifetchworker::recheckChecksum()
+{
+  // DBG << "recheckChecksum block " << _blkno << endl;
+  if (!_request->_fp || !_blksize || !_request->_blklist)
+    return true;
+  if (fseeko(_request->_fp, _blkstart, SEEK_SET))
+    return false;
+  char buf[4096];
+  size_t l = _blksize;
+  _request->_blklist->createDigest(_dig);      // resets digest
+  while (l)
+    {
+      size_t cnt = l > sizeof(buf) ? sizeof(buf) : l;
+      if (fread(buf, cnt, 1, _request->_fp) != 1)
+       return false;
+      _dig.update(buf, cnt);
+      l -= cnt;
+    }
+  return _request->_blklist->verifyDigest(_blkno, _dig);
+}
+
+
+void
+multifetchworker::stealjob()
+{
+  if (!_request->_stealing)
+    {
+      DBG << "start stealing!" << endl;
+      _request->_stealing = true;
+    }
+  multifetchworker *best = 0;
+  std::list<multifetchworker *>::iterator workeriter = _request->_workers.begin();
+  double now = 0;
+  for (; workeriter != _request->_workers.end(); ++workeriter)
+    {
+      multifetchworker *worker = *workeriter;
+      if (worker == this)
+       continue;
+      if (worker->_pass == -1)
+       continue;       // do not steal!
+      if (worker->_state == WORKER_DISCARD || worker->_state == WORKER_DONE || worker->_state == WORKER_SLEEP || !worker->_blksize)
+       continue;       // do not steal finished jobs
+      if (!worker->_avgspeed && worker->_blkreceived)
+       {
+         if (!now)
+           now = currentTime();
+         if (now > worker->_blkstarttime)
+           worker->_avgspeed = worker->_blkreceived / (now - worker->_blkstarttime);
+       }
+      if (!best || best->_pass > worker->_pass)
+       {
+          best = worker;
+         continue;
+       }
+      if (best->_pass < worker->_pass)
+       continue;
+      // if it is the same block, we want to know the best worker, otherwise the worst
+      if (worker->_blkstart == best->_blkstart)
+       {
+         if ((worker->_blksize - worker->_blkreceived) * best->_avgspeed < (best->_blksize - best->_blkreceived) * worker->_avgspeed)
+           best = worker;
+       }
+      else
+       {
+         if ((worker->_blksize - worker->_blkreceived) * best->_avgspeed > (best->_blksize - best->_blkreceived) * worker->_avgspeed)
+           best = worker;
+       }
+    }
+  if (!best)
+    {
+      _state = WORKER_DONE;
+      _request->_activeworkers--;
+      _request->_finished = true;
+      return;
+    }
+  // do not sleep twice
+  if (_state != WORKER_SLEEP)
+    {
+      if (!_avgspeed && _blkreceived)
+       {
+         if (!now)
+           now = currentTime();
+         if (now > _blkstarttime)
+           _avgspeed = _blkreceived / (now - _blkstarttime);
+       }
+
+      // lets see if we should sleep a bit
+      DBG << "me #" << _workerno << ": " << _avgspeed << ", size " << best->_blksize << endl;
+      DBG << "best #" << best->_workerno << ": " << best->_avgspeed << ", size " << (best->_blksize - best->_blkreceived) << endl;
+      if (_avgspeed && best->_avgspeed && best->_blksize - best->_blkreceived > 0 &&
+          (best->_blksize - best->_blkreceived) * _avgspeed < best->_blksize * best->_avgspeed)
+       {
+         if (!now)
+           now = currentTime();
+         double sl = (best->_blksize - best->_blkreceived) / best->_avgspeed * 2;
+         if (sl > 1)
+           sl = 1;
+         DBG << "#" << _workerno << ": going to sleep for " << sl * 1000 << " ms" << endl;
+         _sleepuntil = now + sl;
+         _state = WORKER_SLEEP;
+         _request->_sleepworkers++;
+         return;
+       }
+    }
+
+  _competing = true;
+  best->_competing = true;
+  _blkstart = best->_blkstart;
+  _blksize = best->_blksize;
+  best->_pass++;
+  _pass = best->_pass;
+  _blkno = best->_blkno;
+  run();
+}
+
+void
+multifetchworker::disableCompetition()
+{
+  std::list<multifetchworker *>::iterator workeriter = _request->_workers.begin();
+  for (; workeriter != _request->_workers.end(); ++workeriter)
+    {
+      multifetchworker *worker = *workeriter;
+      if (worker == this)
+       continue;
+      if (worker->_blkstart == _blkstart)
+       {
+         if (worker->_state == WORKER_FETCH)
+           worker->_state = WORKER_DISCARD;
+         worker->_pass = -1;   /* do not steal this one, we already have it */
+       }
+    }
+}
+
+
+void
+multifetchworker::nextjob()
+{
+  _noendrange = false;
+  if (_request->_stealing)
+    {
+      stealjob();
+      return;
+    }
+
+  MediaBlockList *blklist = _request->_blklist;
+  if (!blklist)
+    {
+      _blksize = BLKSIZE;
+      if (_request->_filesize != off_t(-1))
+       {
+         if (_request->_blkoff >= _request->_filesize)
+           {
+             stealjob();
+             return;
+           }
+         _blksize = _request->_filesize - _request->_blkoff;
+         if (_blksize > BLKSIZE)
+           _blksize = BLKSIZE;
+       }
+    }
+  else
+    {
+      MediaBlock blk = blklist->getBlock(_request->_blkno);
+      while (_request->_blkoff >= blk.off + blk.size)
+       {
+         if (++_request->_blkno == blklist->numBlocks())
+           {
+             stealjob();
+             return;
+           }
+         blk = blklist->getBlock(_request->_blkno);
+         _request->_blkoff = blk.off;
+       }
+      _blksize = blk.off + blk.size - _request->_blkoff;
+      if (_blksize > BLKSIZE && !blklist->haveChecksum(_request->_blkno))
+       _blksize = BLKSIZE;
+    }
+  _blkno = _request->_blkno;
+  _blkstart = _request->_blkoff;
+  _request->_blkoff += _blksize;
+  run();
+}
+
+void
+multifetchworker::run()
+{
+  char rangebuf[128];
+
+  if (_state == WORKER_BROKEN || _state == WORKER_DONE)
+     return;   // just in case...
+  if (_noendrange)
+    sprintf(rangebuf, "%llu-", (unsigned long long)_blkstart);
+  else
+    sprintf(rangebuf, "%llu-%llu", (unsigned long long)_blkstart, (unsigned long long)_blkstart + _blksize - 1);
+  DBG << "#" << _workerno << ": BLK " << _blkno << ":" << rangebuf << " " << _url << endl;
+  if (curl_easy_setopt(_curl, CURLOPT_RANGE, !_noendrange || _blkstart != 0 ? rangebuf : (char *)0) != CURLE_OK)
+    {
+      _request->_activeworkers--;
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "curl_easy_setopt range failed", CURL_ERROR_SIZE);
+      return;
+    }
+  if (curl_multi_add_handle(_request->_multi, _curl) != CURLM_OK)
+    {
+      _request->_activeworkers--;
+      _state = WORKER_BROKEN;
+      strncpy(_curlError, "curl_multi_add_handle failed", CURL_ERROR_SIZE);
+      return;
+    }
+  _request->_havenewjob = true;
+  _off = _blkstart;
+  _size = _blksize;
+  if (_request->_blklist)
+    _request->_blklist->createDigest(_dig);    // resets digest
+  _state = WORKER_FETCH;
+
+  double now = currentTime();
+  _blkstarttime = now;
+  _blkreceived = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+multifetchrequest::multifetchrequest(const MediaMultiCurl *context, const Pathname &filename, const Url &baseurl, CURLM *multi, FILE *fp, callback::SendReport<DownloadProgressReport> *report, MediaBlockList *blklist, off_t filesize) : _context(context), _filename(filename), _baseurl(baseurl)
+{
+  _fp = fp;
+  _report = report;
+  _blklist = blklist;
+  _filesize = filesize;
+  _multi = multi;
+  _stealing = false;
+  _havenewjob = false;
+  _blkno = 0;
+  if (_blklist)
+    _blkoff = _blklist->getBlock(0).off;
+  else
+    _blkoff = 0;
+  _activeworkers = 0;
+  _lookupworkers = 0;
+  _sleepworkers = 0;
+  _minsleepuntil = 0;
+  _finished = false;
+  _fetchedsize = 0;
+  _fetchedgoodsize = 0;
+  _totalsize = 0;
+  _lastperiodstart = _lastprogress = _starttime = currentTime();
+  _lastperiodfetched = 0;
+  _periodavg = 0;
+  _timeout = 0;
+  _connect_timeout = 0;
+  _maxspeed = 0;
+  _maxworkers = 0;
+  if (blklist)
+    {
+      for (size_t blkno = 0; blkno < blklist->numBlocks(); blkno++)
+       {
+         MediaBlock blk = blklist->getBlock(blkno);
+         _totalsize += blk.size;
+       }
+    }
+  else if (filesize != off_t(-1))
+    _totalsize = filesize;
+}
+
+multifetchrequest::~multifetchrequest()
+{
+  for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+    {
+      multifetchworker *worker = *workeriter;
+      *workeriter = NULL;
+      delete worker;
+    }
+  _workers.clear();
+}
+
+void
+multifetchrequest::run(std::vector<Url> &urllist)
+{
+  int workerno = 0;
+  std::vector<Url>::iterator urliter = urllist.begin();
+  for (;;)
+    {
+      fd_set rset, wset, xset;
+      int maxfd, nqueue;
+
+      if (_finished)
+       {
+         DBG << "finished!" << endl;
+         break;
+       }
+
+      if (_activeworkers < _maxworkers && urliter != urllist.end() && _workers.size() < MAXURLS)
+       {
+         // spawn another worker!
+         multifetchworker *worker = new multifetchworker(workerno++, *this, *urliter);
+         _workers.push_back(worker);
+         if (worker->_state != WORKER_BROKEN)
+           {
+             _activeworkers++;
+             if (worker->_state != WORKER_LOOKUP)
+               {
+                 worker->nextjob();
+               }
+             else
+               _lookupworkers++;
+           }
+         ++urliter;
+         continue;
+       }
+      if (!_activeworkers)
+       {
+         WAR << "No more active workers!" << endl;
+         // show the first worker error we find
+         for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+           {
+             if ((*workeriter)->_state != WORKER_BROKEN)
+               continue;
+             ZYPP_THROW(MediaCurlException(_baseurl, "Server error", (*workeriter)->_curlError));
+           }
+         break;
+       }
+
+      FD_ZERO(&rset);
+      FD_ZERO(&wset);
+      FD_ZERO(&xset);
+
+      curl_multi_fdset(_multi, &rset, &wset, &xset, &maxfd);
+
+      if (_lookupworkers)
+        for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+         (*workeriter)->adddnsfd(rset, maxfd);
+
+      timeval tv;
+      // if we added a new job we have to call multi_perform once
+      // to make it show up in the fd set. do not sleep in this case.
+      tv.tv_sec = 0;
+      tv.tv_usec = _havenewjob ? 0 : 200000;
+      if (_sleepworkers && !_havenewjob)
+       {
+         if (_minsleepuntil == 0)
+           {
+             for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+               {
+                 multifetchworker *worker = *workeriter;
+                 if (worker->_state != WORKER_SLEEP)
+                   continue;
+                 if (!_minsleepuntil || _minsleepuntil > worker->_sleepuntil)
+                   _minsleepuntil = worker->_sleepuntil;
+               }
+           }
+         double sl = _minsleepuntil - currentTime();
+         if (sl < 0)
+           {
+             sl = 0;
+             _minsleepuntil = 0;
+           }
+         if (sl < .2)
+           tv.tv_usec = sl * 1000000;
+       }
+      int r = select(maxfd + 1, &rset, &wset, &xset, &tv);
+      if (r == -1 && errno != EINTR)
+       ZYPP_THROW(MediaCurlException(_baseurl, "select() failed", "unknown error"));
+      if (r != 0 && _lookupworkers)
+       for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+         {
+           multifetchworker *worker = *workeriter;
+           if (worker->_state != WORKER_LOOKUP)
+             continue;
+           (*workeriter)->dnsevent(rset);
+           if (worker->_state != WORKER_LOOKUP)
+             _lookupworkers--;
+         }
+      _havenewjob = false;
+
+      // run curl
+      for (;;)
+        {
+          CURLMcode mcode;
+         int tasks;
+          mcode = curl_multi_perform(_multi, &tasks);
+          if (mcode == CURLM_CALL_MULTI_PERFORM)
+            continue;
+         if (mcode != CURLM_OK)
+           ZYPP_THROW(MediaCurlException(_baseurl, "curl_multi_perform", "unknown error"));
+         break;
+        }
+
+      double now = currentTime();
+
+      // update periodavg
+      if (now > _lastperiodstart + .5)
+       {
+         if (!_periodavg)
+           _periodavg = (_fetchedsize - _lastperiodfetched) / (now - _lastperiodstart);
+         else
+           _periodavg = (_periodavg + (_fetchedsize - _lastperiodfetched) / (now - _lastperiodstart)) / 2;
+         _lastperiodfetched = _fetchedsize;
+         _lastperiodstart = now;
+       }
+
+      // wake up sleepers
+      if (_sleepworkers)
+       {
+         for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+           {
+             multifetchworker *worker = *workeriter;
+             if (worker->_state != WORKER_SLEEP)
+               continue;
+             if (worker->_sleepuntil > now)
+               continue;
+             if (_minsleepuntil == worker->_sleepuntil)
+               _minsleepuntil = 0;
+             DBG << "#" << worker->_workerno << ": sleep done, wake up" << endl;
+             _sleepworkers--;
+             // nextjob chnages the state
+             worker->nextjob();
+           }
+       }
+
+      // collect all curl results, reschedule new jobs
+      CURLMsg *msg;
+      while ((msg = curl_multi_info_read(_multi, &nqueue)) != 0)
+       {
+         if (msg->msg != CURLMSG_DONE)
+           continue;
+         CURL *easy = msg->easy_handle;
+         CURLcode cc = msg->data.result;
+         multifetchworker *worker;
+         if (curl_easy_getinfo(easy, CURLINFO_PRIVATE, &worker) != CURLE_OK)
+           ZYPP_THROW(MediaCurlException(_baseurl, "curl_easy_getinfo", "unknown error"));
+         if (worker->_blkreceived && now > worker->_blkstarttime)
+           {
+             if (worker->_avgspeed)
+               worker->_avgspeed = (worker->_avgspeed + worker->_blkreceived / (now - worker->_blkstarttime)) / 2;
+             else
+               worker->_avgspeed = worker->_blkreceived / (now - worker->_blkstarttime);
+           }
+         DBG << "#" << worker->_workerno << ": BLK " << worker->_blkno << " done code " << cc << " speed " << worker->_avgspeed << endl;
+         curl_multi_remove_handle(_multi, easy);
+         if (cc == CURLE_HTTP_RETURNED_ERROR)
+           {
+             long statuscode = 0;
+             (void)curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &statuscode);
+             DBG << "HTTP status " << statuscode << endl;
+             if (statuscode == 416 && !_blklist)       /* Range error */
+               {
+                 if (_filesize == off_t(-1))
+                   {
+                     if (!worker->_noendrange)
+                       {
+                         DBG << "#" << worker->_workerno << ": retrying with no end range" << endl;
+                         worker->_noendrange = true;
+                         worker->run();
+                         continue;
+                       }
+                     worker->_noendrange = false;
+                     worker->stealjob();
+                     continue;
+                   }
+                 if (worker->_blkstart >= _filesize)
+                   {
+                     worker->nextjob();
+                     continue;
+                   }
+               }
+           }
+         if (cc == 0)
+           {
+             if (!worker->checkChecksum())
+               {
+                 WAR << "#" << worker->_workerno << ": checksum error, disable worker" << endl;
+                 worker->_state = WORKER_BROKEN;
+                 strncpy(worker->_curlError, "checksum error", CURL_ERROR_SIZE);
+                 _activeworkers--;
+                 continue;
+               }
+             if (worker->_state == WORKER_FETCH)
+               {
+                 if (worker->_competing)
+                   {
+                     worker->disableCompetition();
+                     // multiple workers wrote into this block. We already know that our
+                     // data was correct, but maybe some other worker overwrote our data
+                     // with something broken. Thus we have to re-check the block.
+                     if (!worker->recheckChecksum())
+                       {
+                         DBG << "#" << worker->_workerno << ": recheck checksum error, refetch block" << endl;
+                         // re-fetch! No need to worry about the bad workers,
+                         // they will now be set to DISCARD. At the end of their block
+                         // they will notice that they wrote bad data and go into BROKEN.
+                         worker->run();
+                         continue;
+                       }
+                   }
+                 _fetchedgoodsize += worker->_blksize;
+               }
+
+             // make bad workers sleep a little
+             double maxavg = 0;
+             int maxworkerno = 0;
+             int numbetter = 0;
+             for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+               {
+                 multifetchworker *oworker = *workeriter;
+                 if (oworker->_state == WORKER_BROKEN)
+                   continue;
+                 if (oworker->_avgspeed > maxavg)
+                   {
+                     maxavg = oworker->_avgspeed;
+                     maxworkerno = oworker->_workerno;
+                   }
+                 if (oworker->_avgspeed > worker->_avgspeed)
+                   numbetter++;
+               }
+             if (maxavg && !_stealing)
+               {
+                 double ratio = worker->_avgspeed / maxavg;
+                 ratio = 1 - ratio;
+                 if (numbetter < 3)    // don't sleep that much if we're in the top two
+                   ratio = ratio * ratio;
+                 if (ratio > .01)
+                   {
+                     DBG << "#" << worker->_workerno << ": too slow ("<< ratio << ", " << worker->_avgspeed << ", #" << maxworkerno << ": " << maxavg << "), going to sleep for " << ratio * 1000 << " ms" << endl;
+                     worker->_sleepuntil = now + ratio;
+                     worker->_state = WORKER_SLEEP;
+                     _sleepworkers++;
+                     continue;
+                   }
+               }
+
+             // do rate control (if requested)
+             // should use periodavg, but that's not what libcurl does
+             if (_maxspeed && now > _starttime)
+               {
+                 double avg = _fetchedsize / (now - _starttime);
+                 avg = worker->_maxspeed * _maxspeed / avg;
+                 if (avg < _maxspeed / _maxworkers)
+                   avg = _maxspeed / _maxworkers;
+                 if (avg > _maxspeed)
+                   avg = _maxspeed;
+                 if (avg < 1024)
+                   avg = 1024;
+                 worker->_maxspeed = avg;
+#if LIBCURL_VERSION_NUMBER >= 0x071505
+                 curl_easy_setopt(worker->_curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)(avg));
+#endif
+               }
+
+             worker->nextjob();
+           }
+         else
+           {
+             worker->_state = WORKER_BROKEN;
+             _activeworkers--;
+             if (!_activeworkers && !(urliter != urllist.end() && _workers.size() < MAXURLS))
+               {
+                 // end of workers reached! goodbye!
+                 worker->evaluateCurlCode(Pathname(), cc, false);
+               }
+           }
+       }
+
+      // send report
+      if (_report)
+       {
+         int percent = _totalsize ? (100 * (_fetchedgoodsize + _fetchedsize)) / (_totalsize + _fetchedsize) : 0;
+         double avg = 0;
+         if (now > _starttime)
+           avg = _fetchedsize / (now - _starttime);
+         if (!(*(_report))->progress(percent, _baseurl, avg, _lastperiodstart == _starttime ? avg : _periodavg))
+           ZYPP_THROW(MediaCurlException(_baseurl, "User abort", "cancelled"));
+       }
+
+      if (_timeout && now - _lastprogress > _timeout)
+       break;
+    }
+
+  if (!_finished)
+    ZYPP_THROW(MediaTimeoutException(_baseurl));
+
+  // print some download stats
+  WAR << "overall result" << endl;
+  for (std::list<multifetchworker *>::iterator workeriter = _workers.begin(); workeriter != _workers.end(); ++workeriter)
+    {
+      multifetchworker *worker = *workeriter;
+      WAR << "#" << worker->_workerno << ": state: " << worker->_state << " received: " << worker->_received << " url: " << worker->_url << endl;
+    }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
+MediaMultiCurl::MediaMultiCurl(const Url &url_r, const Pathname & attach_point_hint_r)
+    : MediaCurl(url_r, attach_point_hint_r)
+{
+  MIL << "MediaMultiCurl::MediaMultiCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+  _multi = 0;
+  _customHeadersMetalink = 0;
+}
+
+MediaMultiCurl::~MediaMultiCurl()
+{
+  if (_customHeadersMetalink)
+    {
+      curl_slist_free_all(_customHeadersMetalink);
+      _customHeadersMetalink = 0;
+    }
+  if (_multi)
+    {
+      curl_multi_cleanup(_multi);
+      _multi = 0;
+    }
+  std::map<std::string, CURL *>::iterator it;
+  for (it = _easypool.begin(); it != _easypool.end(); it++)
+    {
+      CURL *easy = it->second;
+      if (easy)
+       {
+         curl_easy_cleanup(easy);
+         it->second = NULL;
+       }
+    }
+}
+
+void MediaMultiCurl::setupEasy()
+{
+  MediaCurl::setupEasy();
+
+  if (_customHeadersMetalink)
+    {
+      curl_slist_free_all(_customHeadersMetalink);
+      _customHeadersMetalink = 0;
+    }
+  struct curl_slist *sl = _customHeaders;
+  for (; sl; sl = sl->next)
+    _customHeadersMetalink = curl_slist_append(_customHeadersMetalink, sl->data);
+  _customHeadersMetalink = curl_slist_append(_customHeadersMetalink, "Accept: */*, application/metalink+xml, application/metalink4+xml");
+}
+
+static bool looks_like_metalink(const Pathname & file)
+{
+  char buf[256], *p;
+  int fd, l;
+  if ((fd = open(file.asString().c_str(), O_RDONLY)) == -1)
+    return false;
+  while ((l = read(fd, buf, sizeof(buf) - 1)) == -1 && errno == EINTR)
+    ;
+  close(fd);
+  if (l == -1)
+    return 0;
+  buf[l] = 0;
+  p = buf;
+  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+    p++;
+  if (!strncasecmp(p, "<?xml", 5))
+    {
+      while (*p && *p != '>')
+       p++;
+      if (*p == '>')
+       p++;
+      while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+       p++;
+    }
+  bool ret = !strncasecmp(p, "<metalink", 9) ? true : false;
+  DBG << "looks_like_metalink(" << file << "): " << ret << endl;
+  return ret;
+}
+
+void MediaMultiCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+{
+  Pathname dest = target.absolutename();
+  if( assert_dir( dest.dirname() ) )
+  {
+    DBG << "assert_dir " << dest.dirname() << " failed" << endl;
+    Url url(getFileUrl(filename));
+    ZYPP_THROW( MediaSystemException(url, "System error on " + dest.dirname().asString()) );
+  }
+  string destNew = target.asString() + ".new.zypp.XXXXXX";
+  char *buf = ::strdup( destNew.c_str());
+  if( !buf)
+  {
+    ERR << "out of memory for temp file name" << endl;
+    Url url(getFileUrl(filename));
+    ZYPP_THROW(MediaSystemException(url, "out of memory for temp file name"));
+  }
+
+  int tmp_fd = ::mkstemp( buf );
+  if( tmp_fd == -1)
+  {
+    free( buf);
+    ERR << "mkstemp failed for file '" << destNew << "'" << endl;
+    ZYPP_THROW(MediaWriteException(destNew));
+  }
+  destNew = buf;
+  free( buf);
+
+  FILE *file = ::fdopen( tmp_fd, "w" );
+  if ( !file ) {
+    ::close( tmp_fd);
+    filesystem::unlink( destNew );
+    ERR << "fopen failed for file '" << destNew << "'" << endl;
+    ZYPP_THROW(MediaWriteException(destNew));
+  }
+  DBG << "dest: " << dest << endl;
+  DBG << "temp: " << destNew << endl;
+
+  // set IFMODSINCE time condition (no download if not modified)
+  if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
+  {
+    curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
+    curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
+  }
+  else
+  {
+    curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+    curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+  }
+  // change header to include Accept: metalink
+  curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _customHeadersMetalink);
+  try
+    {
+      MediaCurl::doGetFileCopyFile(filename, dest, file, report, options);
+    }
+  catch (Exception &ex)
+    {
+      ::fclose(file);
+      filesystem::unlink(destNew);
+      curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+      curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+      curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _customHeaders);
+      ZYPP_RETHROW(ex);
+    }
+  curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+  curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
+  curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _customHeaders);
+  long httpReturnCode = 0;
+  CURLcode infoRet = curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode);
+  if (infoRet == CURLE_OK)
+  {
+    DBG << "HTTP response: " + str::numstring(httpReturnCode) << endl;
+    if ( httpReturnCode == 304
+        || ( httpReturnCode == 213 && _url.getScheme() == "ftp" ) ) // not modified
+    {
+      DBG << "not modified: " << PathInfo(dest) << endl;
+      return;
+    }
+  }
+  else
+  {
+    WAR << "Could not get the reponse code." << endl;
+  }
+
+  bool ismetalink = false;
+
+  char *ptr = NULL;
+  if (curl_easy_getinfo(_curl, CURLINFO_CONTENT_TYPE, &ptr) == CURLE_OK && ptr)
+    {
+      string ct = string(ptr);
+      if (ct.find("application/metalink+xml") == 0 || ct.find("application/metalink4+xml") == 0)
+       ismetalink = true;
+    }
+
+  if (!ismetalink)
+    {
+      // some proxies do not store the content type, so also look at the file to find
+      // out if we received a metalink (bnc#649925)
+      fflush(file);
+      if (looks_like_metalink(Pathname(destNew)))
+       ismetalink = true;
+    }
+
+  if (ismetalink)
+    {
+      bool userabort = false;
+      fclose(file);
+      file = NULL;
+      Pathname failedFile = ZConfig::instance().repoCachePath() / "MultiCurl.failed";
+      try
+       {
+         MetaLinkParser mlp;
+         mlp.parse(Pathname(destNew));
+         MediaBlockList bl = mlp.getBlockList();
+         vector<Url> urls = mlp.getUrls();
+         DBG << bl << endl;
+         file = fopen(destNew.c_str(), "w+");
+         if (!file)
+           ZYPP_THROW(MediaWriteException(destNew));
+         if (PathInfo(target).isExist())
+           {
+             DBG << "reusing blocks from file " << target << endl;
+             bl.reuseBlocks(file, target.asString());
+             DBG << bl << endl;
+           }
+         if (bl.haveChecksum(1) && PathInfo(failedFile).isExist())
+           {
+             DBG << "reusing blocks from file " << failedFile << endl;
+             bl.reuseBlocks(file, failedFile.asString());
+             DBG << bl << endl;
+             filesystem::unlink(failedFile);
+           }
+         Pathname df = deltafile();
+         if (!df.empty())
+           {
+             DBG << "reusing blocks from file " << df << endl;
+             bl.reuseBlocks(file, df.asString());
+             DBG << bl << endl;
+           }
+         try
+           {
+             multifetch(filename, file, &urls, &report, &bl);
+           }
+         catch (MediaCurlException &ex)
+           {
+             userabort = ex.errstr() == "User abort";
+             ZYPP_RETHROW(ex);
+           }
+       }
+      catch (Exception &ex)
+       {
+         // something went wrong. fall back to normal download
+         if (file)
+           fclose(file);
+         file = NULL;
+         if (PathInfo(destNew).size() >= 63336)
+           {
+             ::unlink(failedFile.asString().c_str());
+             filesystem::hardlinkCopy(destNew, failedFile);
+           }
+         if (userabort)
+           {
+             filesystem::unlink(destNew);
+             ZYPP_RETHROW(ex);
+           }
+         file = fopen(destNew.c_str(), "w+");
+         if (!file)
+           ZYPP_THROW(MediaWriteException(destNew));
+         MediaCurl::doGetFileCopyFile(filename, dest, file, report, options | OPTION_NO_REPORT_START);
+       }
+    }
+
+  if (::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 )))
+    {
+      ERR << "Failed to chmod file " << destNew << endl;
+    }
+  if (::fclose(file))
+    {
+      filesystem::unlink(destNew);
+      ERR << "Fclose failed for file '" << destNew << "'" << endl;
+      ZYPP_THROW(MediaWriteException(destNew));
+    }
+  if ( rename( destNew, dest ) != 0 )
+    {
+      ERR << "Rename failed" << endl;
+      ZYPP_THROW(MediaWriteException(dest));
+    }
+  DBG << "done: " << PathInfo(dest) << endl;
+}
+
+void MediaMultiCurl::multifetch(const Pathname & filename, FILE *fp, std::vector<Url> *urllist, callback::SendReport<DownloadProgressReport> *report, MediaBlockList *blklist, off_t filesize) const
+{
+  Url baseurl(getFileUrl(filename));
+  if (blklist && filesize == off_t(-1) && blklist->haveFilesize())
+    filesize = blklist->getFilesize();
+  if (blklist && !blklist->haveBlocks() && filesize != 0)
+    blklist = 0;
+  if (blklist && (filesize == 0 || !blklist->numBlocks()))
+    {
+      checkFileDigest(baseurl, fp, blklist);
+      return;
+    }
+  if (filesize == 0)
+    return;
+  if (!_multi)
+    {
+      _multi = curl_multi_init();
+      if (!_multi)
+       ZYPP_THROW(MediaCurlInitException(baseurl));
+    }
+  multifetchrequest req(this, filename, baseurl, _multi, fp, report, blklist, filesize);
+  req._timeout = _settings.timeout();
+  req._connect_timeout = _settings.connectTimeout();
+  req._maxspeed = _settings.maxDownloadSpeed();
+  req._maxworkers = _settings.maxConcurrentConnections();
+  if (req._maxworkers > MAXURLS)
+    req._maxworkers = MAXURLS;
+  if (req._maxworkers <= 0)
+    req._maxworkers = 1;
+  std::vector<Url> myurllist;
+  for (std::vector<Url>::iterator urliter = urllist->begin(); urliter != urllist->end(); ++urliter)
+    {
+      try
+       {
+         string scheme = urliter->getScheme();
+         if (scheme == "http" || scheme == "https" || scheme == "ftp")
+           {
+             checkProtocol(*urliter);
+             myurllist.push_back(*urliter);
+           }
+       }
+      catch (...)
+       {
+       }
+    }
+  if (!myurllist.size())
+    myurllist.push_back(baseurl);
+  req.run(myurllist);
+  checkFileDigest(baseurl, fp, blklist);
+}
+
+void MediaMultiCurl::checkFileDigest(Url &url, FILE *fp, MediaBlockList *blklist) const
+{
+  if (!blklist || !blklist->haveFileChecksum())
+    return;
+  if (fseeko(fp, off_t(0), SEEK_SET))
+    ZYPP_THROW(MediaCurlException(url, "fseeko", "seek error"));
+  Digest dig;
+  blklist->createFileDigest(dig);
+  char buf[4096];
+  size_t l;
+  while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+    dig.update(buf, l);
+  if (!blklist->verifyFileDigest(dig))
+    ZYPP_THROW(MediaCurlException(url, "file verification failed", "checksum error"));
+}
+
+bool MediaMultiCurl::isDNSok(const string &host) const
+{
+  return _dnsok.find(host) == _dnsok.end() ? false : true;
+}
+
+void MediaMultiCurl::setDNSok(const string &host) const
+{
+  _dnsok.insert(host);
+}
+
+CURL *MediaMultiCurl::fromEasyPool(const string &host) const
+{
+  if (_easypool.find(host) == _easypool.end())
+    return 0;
+  CURL *ret = _easypool[host];
+  _easypool.erase(host);
+  return ret;
+}
+
+void MediaMultiCurl::toEasyPool(const std::string &host, CURL *easy) const
+{
+  CURL *oldeasy = _easypool[host];
+  _easypool[host] = easy;
+  if (oldeasy)
+    curl_easy_cleanup(oldeasy);
+}
+
+  } // namespace media
+} // namespace zypp
+
diff --git a/zypp/media/MediaMultiCurl.h b/zypp/media/MediaMultiCurl.h
new file mode 100644 (file)
index 0000000..ce926fe
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaMultiCurl.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAMULTICURL_H
+#define ZYPP_MEDIA_MEDIAMULTICURL_H
+
+#include <string>
+#include <vector>
+#include <list>
+#include <set>
+
+#include "zypp/media/MediaHandler.h"
+#include "zypp/media/MediaCurl.h"
+#include "zypp/media/MediaBlockList.h"
+#include "zypp/media/TransferSettings.h"
+#include "zypp/ZYppCallbacks.h"
+
+namespace zypp {
+  namespace media {
+
+/**
+ * @short Implementation class for FTP, HTTP and HTTPS MediaHandler
+ *
+ * @author Michael Schroeder <mls@suse.de>
+ *
+ * @see MediaHandler
+ **/
+
+class multifetchrequest;
+class multifetchworker;
+
+class MediaMultiCurl : public MediaCurl {
+public:
+  friend class multifetchrequest;
+  friend class multifetchworker;
+
+  MediaMultiCurl(const Url &url_r, const Pathname & attach_point_hint_r);
+  ~MediaMultiCurl();
+
+  virtual void doGetFileCopy( const Pathname & srcFilename, const Pathname & targetFilename, callback::SendReport<DownloadProgressReport> & _report, RequestOptions options = OPTION_NONE ) const;
+
+  void multifetch(const Pathname &filename, FILE *fp, std::vector<Url> *urllist, callback::SendReport<DownloadProgressReport> *report = 0, MediaBlockList *blklist = 0, off_t filesize = off_t(-1)) const;
+
+protected:
+
+  bool isDNSok(const std::string &host) const;
+  void setDNSok(const std::string &host) const;
+
+  CURL *fromEasyPool(const std::string &host) const;
+  void toEasyPool(const std::string &host, CURL *easy) const;
+
+  virtual void setupEasy();
+  void checkFileDigest(Url &url, FILE *fp, MediaBlockList *blklist) const;
+
+private:
+  // the custom headers from MediaCurl plus a "Accept: metalink" header
+  curl_slist *_customHeadersMetalink;
+  mutable CURLM *_multi;       // reused for all fetches so we can make use of the dns cache
+  mutable std::set<std::string> _dnsok;
+  mutable std::map<std::string, CURL *> _easypool;
+};
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIAMULTICURL_H
diff --git a/zypp/media/MediaNFS.cc b/zypp/media/MediaNFS.cc
new file mode 100644 (file)
index 0000000..63c1fc5
--- /dev/null
@@ -0,0 +1,265 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaNFS.cc
+ *
+*/
+
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/media/MediaNFS.h"
+#include "zypp/media/Mount.h"
+
+#include <dirent.h>
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaNFS
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaNFS::MediaNFS
+    // METHOD TYPE : Constructor
+    //
+    // DESCRIPTION :
+    //
+    MediaNFS::MediaNFS( const Url &      url_r,
+                       const Pathname & attach_point_hint_r )
+      : MediaHandler( url_r, attach_point_hint_r,
+                     "/", // urlpath at attachpoint
+                     false ) // does_download
+    {
+       MIL << "MediaNFS::MediaNFS(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaNFS::attachTo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+    //
+    void MediaNFS::attachTo(bool next)
+    {
+      if(_url.getHost().empty())
+       ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
+      if(next)
+       ZYPP_THROW(MediaNotSupportedException(_url));
+
+      string path = _url.getHost();
+      path += ':';
+      path += Pathname(_url.getPathName()).asString();
+
+      MediaSourceRef media( new MediaSource("nfs", path));
+      AttachedMedia  ret( findAttachedMedia( media));
+
+      if( ret.mediaSource &&
+         ret.attachPoint &&
+         !ret.attachPoint->empty())
+      {
+       DBG << "Using a shared media "
+           << ret.mediaSource->name
+           << " attached on "
+           << ret.attachPoint->path
+           << endl;
+
+       removeAttachPoint();
+       setAttachPoint(ret.attachPoint);
+       setMediaSource(ret.mediaSource);
+       return;
+      }
+
+      std::string mountpoint( attachPoint().asString() );
+      Mount mount;
+
+      if( !isUseableAttachPoint(attachPoint()))
+      {
+       mountpoint = createAttachPoint().asString();
+       if( mountpoint.empty())
+         ZYPP_THROW( MediaBadAttachPointException(url()));
+       setAttachPoint( mountpoint, true);
+      }
+
+      std::string filesystem( _url.getScheme() );
+      if ( filesystem != "nfs4" && _url.getQueryParam("type") == "nfs4" )
+      {
+        filesystem = "nfs4";
+      }
+
+      string options = _url.getQueryParam("mountoptions");
+      if(options.empty())
+      {
+       options="ro";
+      }
+
+      vector<string> optionList;
+      str::split( options, std::back_inserter(optionList), "," );
+      vector<string>::const_iterator it;
+      bool contains_lock  = false, contains_soft = false,
+           contains_timeo = false, contains_hard = false;
+
+      for( it = optionList.begin(); it != optionList.end(); ++it ) {
+        if ( *it == "lock" || *it == "nolock" ) contains_lock = true;
+        else if ( *it == "soft") contains_soft = true;
+        else if ( *it == "hard") contains_hard = true;
+        else if ( it->find("timeo") != string::npos ) contains_timeo = true;
+      }
+
+      if ( !(contains_lock && contains_soft) ) {
+        // Add option "nolock", unless option "lock" or "unlock" is already set.
+        // This should prevent the mount command hanging when the portmapper isn't
+        // running.
+        if ( !contains_lock ) {
+          optionList.push_back( "nolock" );
+        }
+        // Add options "soft,timeo=NFS_MOUNT_TIMEOUT", unless they are set
+        // already or "hard" option is explicitly specified. This prevent
+        // the mount command from hanging when the nfs server is not responding
+        // and file transactions from an unresponsive to throw an error after
+        // a short time instead of hanging forever
+        if ( !(contains_soft || contains_hard) ) {
+          optionList.push_back( "soft" );
+          if ( !contains_timeo ) {
+            ostringstream s;
+            s << "timeo=" << NFS_MOUNT_TIMEOUT;
+            optionList.push_back( s.str() );
+          }
+        }
+        options = str::join( optionList, "," );
+      }
+
+      mount.mount(path,mountpoint,filesystem,options);
+
+      setMediaSource(media);
+
+      // wait for /etc/mtab update ...
+      // (shouldn't be needed)
+      int limit = 3;
+      bool mountsucceeded;
+      while( !(mountsucceeded=isAttached()) && --limit)
+      {
+        sleep(1);
+      }
+
+      if( !mountsucceeded)
+      {
+        setMediaSource(MediaSourceRef());
+        try
+        {
+          mount.umount(attachPoint().asString());
+        }
+        catch (const MediaException & excpt_r)
+        {
+          ZYPP_CAUGHT(excpt_r);
+        }
+        ZYPP_THROW(MediaMountException(
+          "Unable to verify that the media was mounted",
+         path, mountpoint
+        ));
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaNFS::isAttached
+    // METHOD TYPE : bool
+    //
+    // DESCRIPTION : Override check if media is attached.
+    //
+    bool
+    MediaNFS::isAttached() const
+    {
+      return checkAttached(true);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    //  METHOD NAME : MediaNFS::releaseFrom
+    //  METHOD TYPE : void
+    //
+    //  DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaNFS::releaseFrom( const std::string & ejectDev )
+    {
+      Mount mount;
+      mount.umount(attachPoint().asString());
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaNFS::getFile
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaNFS::getFile (const Pathname & filename) const
+    {
+      MediaHandler::getFile( filename );;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaNFS::getDir
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached.
+    //
+    void MediaNFS::getDir( const Pathname & dirname, bool recurse_r ) const
+    {
+      MediaHandler::getDir( dirname, recurse_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaNFS::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaNFS::getDirInfo( std::list<std::string> & retlist,
+                             const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //
+    // METHOD NAME : MediaNFS::getDirInfo
+    // METHOD TYPE : PMError
+    //
+    // DESCRIPTION : Asserted that media is attached and retlist is empty.
+    //
+    void MediaNFS::getDirInfo( filesystem::DirContent & retlist,
+                          const Pathname & dirname, bool dots ) const
+    {
+      MediaHandler::getDirInfo( retlist, dirname, dots );
+    }
+
+    bool MediaNFS::getDoesFileExist( const Pathname & filename ) const
+    {
+      return MediaHandler::getDoesFileExist( filename );
+    }
+
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaNFS.h b/zypp/media/MediaNFS.h
new file mode 100644 (file)
index 0000000..eea5daf
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaNFS.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIANFS_H
+#define ZYPP_MEDIA_MEDIANFS_H
+
+#include "zypp/media/MediaHandler.h"
+
+/**
+ * Value of NFS mount minor timeout (passed to <code>timeo</code> option
+ * of the NFS mount) in tenths of a second.
+ *
+ * The value of 300 should give a major timeout after 3.5 minutes
+ * for UDP and 1.5 minutes for TCP. (#350309)
+ */
+#define NFS_MOUNT_TIMEOUT 300
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaNFS
+    /**
+     * @short Implementation class for NFS MediaHandler
+     * @see MediaHandler
+     **/
+    class MediaNFS : public MediaHandler {
+
+      protected:
+
+       virtual void attachTo (bool next = false);
+
+        virtual void releaseFrom( const std::string & ejectDev );
+       virtual void getFile( const Pathname & filename ) const;
+       virtual void getDir( const Pathname & dirname, bool recurse_r ) const;
+        virtual void getDirInfo( std::list<std::string> & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual void getDirInfo( filesystem::DirContent & retlist,
+                                 const Pathname & dirname, bool dots = true ) const;
+        virtual bool getDoesFileExist( const Pathname & filename ) const;
+
+      public:
+
+        MediaNFS( const Url&       url_r,
+                 const Pathname & attach_point_hint_r );
+
+        virtual ~MediaNFS() { try { release(); } catch(...) {} }
+
+       virtual bool isAttached() const;
+    };
+
+    ///////////////////////////////////////////////////////////////////
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_MEDIANFS_H
diff --git a/zypp/media/MediaPlugin.cc b/zypp/media/MediaPlugin.cc
new file mode 100644 (file)
index 0000000..76b75b3
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaPlugin.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/media/MediaPlugin.h"
+#include "zypp/media/MediaManager.h"
+
+using std::endl;
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+    MediaPlugin::MediaPlugin( const Url & url_r, const Pathname & attach_point_hint_r )
+      : MediaHandler( url_r, attach_point_hint_r, /*path below attachpoint*/"/", /*does_download*/false )
+    {
+      MIL << "MediaPlugin::MediaPlugin(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+    }
+    void MediaPlugin::attachTo( bool next_r )
+    {}
+
+    void MediaPlugin::releaseFrom( const std::string & ejectDev_r )
+    {}
+
+    void MediaPlugin::getFile( const Pathname & filename_r ) const
+    {}
+
+    void MediaPlugin::getDir( const Pathname & dirname_r, bool recurse_r ) const
+    {}
+
+    void MediaPlugin::getDirInfo( std::list<std::string> & retlist_r, const Pathname & dirname_r, bool dots_r ) const
+    {}
+
+    void MediaPlugin::getDirInfo( filesystem::DirContent & retlist_r, const Pathname & dirname_r, bool dots_r ) const
+    {}
+
+    bool MediaPlugin::getDoesFileExist( const Pathname & filename_r ) const
+    { return false; }
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
diff --git a/zypp/media/MediaPlugin.h b/zypp/media/MediaPlugin.h
new file mode 100644 (file)
index 0000000..2b68ace
--- /dev/null
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaPlugin.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAPLUGIN_H
+#define ZYPP_MEDIA_MEDIAPLUGIN_H
+
+#include "zypp/media/MediaHandler.h"
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace media
+  { //////////////////////////////////////////////////////////////////
+
+    /**
+     * \brief Implementation class for plugin MediaHandler
+     *
+     * @see MediaHandler
+     */
+    class MediaPlugin : public MediaHandler
+    {
+      public:
+       MediaPlugin( const Url & url_r, const Pathname & attach_point_hint_r );
+
+        virtual ~MediaPlugin() { try { release(); } catch(...) {} }
+
+      protected:
+       virtual void attachTo( bool next_r = false );
+       virtual void releaseFrom( const std::string & ejectDev_r );
+       virtual void getFile( const Pathname & filename_r ) const;
+       virtual void getDir( const Pathname & dirname_r, bool recurse_r ) const;
+       virtual void getDirInfo( std::list<std::string> & retlist_r, const Pathname & dirname_r, bool dots_r = true ) const;
+       virtual void getDirInfo( filesystem::DirContent & retlist_r, const Pathname & dirname_r, bool dots_r = true ) const;
+       virtual bool getDoesFileExist( const Pathname & filename_r ) const;
+    };
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace media
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_MEDIAPLUGIN_H
diff --git a/zypp/media/MediaPriority.cc b/zypp/media/MediaPriority.cc
new file mode 100644 (file)
index 0000000..b063661
--- /dev/null
@@ -0,0 +1,103 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/MediaPriority.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/Url.h"
+#include "zypp/ZConfig.h"
+
+#include "zypp/media/MediaPriority.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      /**
+       * 4: local:     file,dir,hd
+       * 3: remote:    nfs,cifs,smb
+       * ?: download:  http,https,ftp,sftp
+       * ?: volatile:  cd,dvd
+       * 0:            the rest
+      */
+      MediaPriority::value_type scheme2priority(  const std::string & scheme_r )
+      {
+       switch ( scheme_r[0] )
+       {
+#define RETURN_IF(scheme,value) \
+       if ( ::strcmp( scheme+1, scheme_r.c_str()+1 ) == 0 ) return value;
+         case 'c':
+           RETURN_IF( "cd",    ZConfig::instance().download_media_prefer_download() ? 1 : 2 );
+           RETURN_IF( "cifs",  3 );
+           break;
+
+         case 'd':
+           RETURN_IF( "dvd",   ZConfig::instance().download_media_prefer_download() ? 1 : 2 );
+           RETURN_IF( "dir",   4 );
+           break;
+
+         case 'f':
+           RETURN_IF( "file",  4 );
+           RETURN_IF( "ftp",   ZConfig::instance().download_media_prefer_download() ? 2 : 1);
+           break;
+
+         case 'h':
+           RETURN_IF( "http",  ZConfig::instance().download_media_prefer_download() ? 2 : 1 );
+           RETURN_IF( "https", ZConfig::instance().download_media_prefer_download() ? 2 : 1 );
+           RETURN_IF( "hd",    4 );
+           break;
+
+         case 'n':
+           RETURN_IF( "nfs",   3 );
+           RETURN_IF( "nfs4",  3 );
+           break;
+
+         case 's':
+           RETURN_IF( "sftp",  ZConfig::instance().download_media_prefer_download() ? 2 : 1 );
+           RETURN_IF( "smb",   3 );
+           break;
+#undef RETURN_IF
+       }
+       return 0;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : MediaPriority::MediaPriority
+    // METHOD TYPE : Ctor
+    //
+    MediaPriority::MediaPriority( const std::string & scheme_r )
+      : _val( scheme2priority( scheme_r ) )
+    {}
+
+    MediaPriority::MediaPriority( const Url & url_r )
+      : _val( scheme2priority( url_r.getScheme() ) )
+    {}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/media/MediaPriority.h b/zypp/media/MediaPriority.h
new file mode 100644 (file)
index 0000000..aef3199
--- /dev/null
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/MediaPriority.h
+ *
+*/
+#ifndef ZYPP_MEDIA_MEDIAPRIORITY_H
+#define ZYPP_MEDIA_MEDIAPRIORITY_H
+
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class Url;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MediaPriority
+    //
+    /** Derive a numeric priority from \ref Url scheme according to zypp.conf(download.media_preference).
+     *
+     * The class is simple. Constructable and assignable from \ref Url
+     * or scheme string. Implicit convertible into a numic \ref value_type.
+     *
+     * \code
+     *   if ( MediaPriority("cd") \< MediaPriority("ftp") )
+     *     ...
+     * \endcode
+     *
+     * \todo Maybe introduce a static tribool, to allow overwriting zypp.conf(download.media_preference) default.
+    */
+    class MediaPriority
+    {
+      public:
+       typedef int value_type;
+
+      public:
+       /** Default ctor. Least priority \c 0.*/
+       MediaPriority()
+       : _val( 0 )
+       {}
+
+       /** Copy ctor. */
+       MediaPriority( value_type val_r )
+       : _val( val_r )
+       {}
+
+       /** Ctor from scheme string.*/
+       MediaPriority( const std::string & scheme_r );
+
+       /** Ctor from scheme string defined by Url. */
+       MediaPriority( const Url & url_r );
+
+      public:
+       /** Assign. */
+       MediaPriority & operator=( value_type rhs )
+       { _val = rhs; return *this; }
+
+       /** Assign priority of scheme string. */
+       MediaPriority & operator=( const std::string & scheme_r )
+       { _val = MediaPriority(scheme_r); return *this; }
+
+       /** Assign priority of scheme string defined by Url. */
+       MediaPriority & operator=( const Url & url_r )
+       { _val = MediaPriority(url_r); return *this; }
+
+      public:
+       /** Conversion to value_type. */
+       //@{
+       /** Explicit */
+       value_type &       get()            { return _val; }
+       /** Explicit */
+       const value_type & get() const      { return _val; }
+       /** Implicit */
+       operator value_type &()             { return get(); }
+       /** Implicit */
+       operator const value_type &() const { return get(); }
+       //@}
+
+      private:
+       value_type _val;
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_MEDIAPRIORITY_H
diff --git a/zypp/media/MediaSource.cc b/zypp/media/MediaSource.cc
new file mode 100644 (file)
index 0000000..a0f31c5
--- /dev/null
@@ -0,0 +1,29 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaSource.cc
+*/
+#include <iostream>
+
+#include "zypp/media/MediaSource.h"
+
+namespace zypp {
+  namespace media {
+
+    std::ostream & operator<<( std::ostream & str, const AttachPoint & obj )
+    {
+      return str << (obj.temp ? "*" : "") << obj.path;
+    }
+
+    std::ostream & operator<<( std::ostream & str, const AttachedMedia & obj )
+    {
+      return str << "media("  << obj.mediaSource << ")attached(" << obj.attachPoint << ")";
+    }
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaSource.h b/zypp/media/MediaSource.h
new file mode 100644 (file)
index 0000000..45ef522
--- /dev/null
@@ -0,0 +1,156 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaSource.h
+ *
+ */
+#ifndef ZYPP_MEDIA_MEDIASOURCE_H
+#define ZYPP_MEDIA_MEDIASOURCE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/base/String.h"
+#include "zypp/base/PtrTypes.h"
+
+
+namespace zypp {
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Media manager access Id type.
+     */
+    typedef unsigned int MediaAccessId;
+
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Media source internally used by MediaManager and MediaHandler.
+     */
+    class MediaSource
+    {
+    public:
+      MediaSource(const std::string &_type,  const std::string &_name,
+                  unsigned int       _maj=0, unsigned int       _min=0,
+                 const std::string &_bdir=std::string(), bool  _own=true)
+        : maj_nr(_maj)
+        , min_nr(_min)
+        , type(_type)
+        , name(_name)
+       , bdir(_bdir)
+       , iown(_own)
+      {}
+
+      MediaSource()
+        : maj_nr(0)
+        , min_nr(0)
+      {}
+
+      virtual
+      ~MediaSource()
+      {}
+
+      /**
+       * Check if the both sources are equal.
+       */
+      virtual bool equals(const MediaSource &src) const
+      {
+        if( type == src.type)
+        {
+          if( maj_nr == 0)
+            return name == src.name;
+          else
+            return maj_nr == src.maj_nr &&
+                   min_nr == src.min_nr;
+        }
+        return false;
+      }
+
+      /**
+       * Return media source as string for debuging purposes.
+       */
+      virtual std::string asString() const
+      {
+       std::string tmp1;
+        if(maj_nr != 0)
+       {
+         tmp1 = "[" + str::numstring(maj_nr) + "," +
+                      str::numstring(min_nr) + "]";
+       }
+        return type + "<" + name + tmp1 + ">";
+      }
+
+      unsigned int maj_nr;  //!< A major number if source is a device.
+      unsigned int min_nr;  //!< A minor number if source is a device.
+      std::string  type;    //!< A media handler specific source type.
+      std::string  name;    //!< A media handler specific source name.
+      std::string  bdir;    //!< Directory, the media may be bound to.
+      bool         iown;    //!< True, if mounted by media manager.
+    };
+
+    /** \relates MediaSource Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const MediaSource & obj )
+    { return str << obj.asString(); }
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * Attach point of a media source.
+     */
+    class AttachPoint
+    {
+    public:
+      AttachPoint(const Pathname &_path=Pathname(),
+                  bool            _temp=true)
+        : path(_path)
+        , temp(_temp)
+      {}
+
+      bool empty() const { return path.empty(); }
+
+      Pathname path;   //!< The path name (mount point).
+      bool     temp;    //!< If it was created temporary.
+    };
+
+    /** \relates AttachPoint Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const AttachPoint & obj );
+
+    ///////////////////////////////////////////////////////////////////
+    typedef zypp::RW_pointer<MediaSource> MediaSourceRef;
+    typedef zypp::RW_pointer<AttachPoint> AttachPointRef;
+
+
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * A simple structure containing references
+     * to a media source and its attach point.
+     */
+    struct AttachedMedia
+    {
+      AttachedMedia()
+      {}
+
+      AttachedMedia(const MediaSourceRef &_mediaSource,
+                    const AttachPointRef &_attachPoint)
+       : mediaSource( _mediaSource)
+       , attachPoint( _attachPoint)
+      {}
+
+      MediaSourceRef mediaSource;
+      AttachPointRef attachPoint;
+    };
+
+    /** \relates AttachedMedia Stream output */
+    std::ostream & operator<<( std::ostream & str, const AttachedMedia & obj );
+
+  } // namespace media
+} // namespace zypp
+
+
+#endif // ZYPP_MEDIA_MEDIASOURCE_H
+
diff --git a/zypp/media/MediaUserAuth.cc b/zypp/media/MediaUserAuth.cc
new file mode 100644 (file)
index 0000000..20f43a1
--- /dev/null
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaUserAuth.cc
+ *
+ */
+
+#include <list>
+#include <boost/format.hpp>
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+
+#include "zypp/media/MediaException.h"
+#include "zypp/media/MediaUserAuth.h"
+
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+
+AuthData::AuthData(const Url & url)
+  : _url(url)
+{
+  _username = url.getUsername();
+  _password = url.getPassword();
+}
+
+
+bool AuthData::valid() const
+{
+  return username().size() && password().size();
+}
+
+std::ostream & AuthData::dumpOn( std::ostream & str ) const
+{
+  str << "username: '" << _username << "'" << std::endl
+      << "password: " << (_password.empty() ? "<empty>" : "<non-empty>")
+      << std::endl;
+  return str;
+}
+
+std::ostream & AuthData::dumpAsIniOn( std::ostream & str ) const
+{
+  if (_url.isValid())
+    str
+      << "[" << _url.asString(
+        url::ViewOptions()
+        - url::ViewOptions::WITH_USERNAME
+        - url::ViewOptions::WITH_PASSWORD)
+      << "]" << endl;
+
+  str
+    << "username = " << _username << endl
+    << "password = " << _password << endl;
+
+  return str;
+}
+
+bool CurlAuthData::valid() const
+{
+  return username().size() && password().size();
+}
+
+
+std::ostream & CurlAuthData::dumpOn( std::ostream & str ) const
+{
+  AuthData::dumpOn(str) << " auth_type: " << _auth_type_str
+    << " (" << _auth_type << ")" << std::endl;
+  return str;
+}
+
+long CurlAuthData::auth_type_str2long(std::string & auth_type_str)
+{
+  curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW);
+
+  std::vector<std::string>                  list;
+  std::vector<std::string>::const_iterator  it;
+  long                                      auth_type = CURLAUTH_NONE;
+
+  zypp::str::split(auth_type_str, std::back_inserter(list), ",");
+
+  for(it = list.begin(); it != list.end(); ++it)
+  {
+    if(*it == "basic")
+    {
+      auth_type |= CURLAUTH_BASIC;
+    }
+    else
+    if(*it == "digest")
+    {
+      auth_type |= CURLAUTH_DIGEST;
+    }
+    else
+    if((curl_info && (curl_info->features & CURL_VERSION_NTLM)) &&
+       (*it == "ntlm"))
+    {
+      auth_type |= CURLAUTH_NTLM;
+    }
+    else
+    if((curl_info && (curl_info->features & CURL_VERSION_SPNEGO)) &&
+       (*it == "spnego" || *it == "negotiate"))
+    {
+      // there is no separate spnego flag for this auth type
+      auth_type |= CURLAUTH_GSSNEGOTIATE;
+    }
+    else
+    if((curl_info && (curl_info->features & CURL_VERSION_GSSNEGOTIATE)) &&
+       (*it == "gssnego" || *it == "negotiate"))
+    {
+      auth_type |= CURLAUTH_GSSNEGOTIATE;
+    }
+    else
+    {
+      std::string msg = boost::str(
+        boost::format (_("Unsupported HTTP authentication method '%s'")) % *it);
+
+      ZYPP_THROW(MediaException(msg));
+    }
+  }
+
+  return auth_type;
+}
+
+std::string CurlAuthData::auth_type_long2str(long auth_type)
+{
+  std::list<std::string> auth_list;
+
+  if(auth_type & CURLAUTH_GSSNEGOTIATE)
+    auth_list.push_back("negotiate");
+
+  if(auth_type & CURLAUTH_NTLM)
+    auth_list.push_back("ntlm");
+
+  if(auth_type & CURLAUTH_DIGEST)
+    auth_list.push_back("digest");
+
+  if(auth_type & CURLAUTH_BASIC)
+    auth_list.push_back("basic");
+
+  return str::join(auth_list, ",");
+}
+
+
+std::ostream & operator << (std::ostream & str, const AuthData & auth_data)
+{
+  auth_data.dumpOn(str);
+  return str;
+}
+
+std::ostream & operator << (std::ostream & str, const CurlAuthData & auth_data)
+{
+  auth_data.dumpOn(str);
+  return str;
+}
+
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/MediaUserAuth.h b/zypp/media/MediaUserAuth.h
new file mode 100644 (file)
index 0000000..afa58de
--- /dev/null
@@ -0,0 +1,170 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MediaUserAuth.h
+ * Convenience interface for handling authentication data of media user.
+ */
+#ifndef ZYPP_MEDIA_USER_AUTH_H
+#define ZYPP_MEDIA_USER_AUTH_H
+
+#include <curl/curl.h>
+
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/Url.h"
+#include "zypp/base/PtrTypes.h"
+
+namespace zypp {
+  namespace media {
+
+///////////////////////////////////////////////////////////////////
+
+
+/**
+ * Class for handling media authentication data. This is the most generic
+ * class containing only username and password members.
+ */
+class AuthData
+{
+public:
+  AuthData()
+  {}
+
+  AuthData(const Url & url);
+
+  AuthData(const std::string & username, const std::string & password)
+    : _username(username), _password(password)
+  {}
+
+  virtual ~AuthData() {};
+
+  /**
+   * Checks validity of authentication data.
+   * \return true if the object contains non-empty username and
+   *  non-empty password, false otherwise.
+   */
+  virtual bool valid() const;
+
+  void setUrl(const Url & url) { _url = url; }
+  void setUsername(const std::string & username) { _username = username; }
+  void setPassword(const std::string & password) { _password = password; }
+
+  Url url() const { return _url; }
+  std::string username() const { return _username; }
+  std::string password() const { return _password; }
+
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+  virtual std::ostream & dumpAsIniOn( std::ostream & str ) const;
+
+private:
+  Url _url;
+  std::string _username;
+  std::string _password;
+};
+
+typedef shared_ptr<AuthData> AuthData_Ptr;
+
+/**
+ * Curl HTTP authentication data.
+ */
+class CurlAuthData : public AuthData {
+public:
+  /**
+   * Default constructor. Initializes username and password to empty strings
+   * and authetication type to CURLAUTH_NONE.
+   */
+  CurlAuthData() : AuthData(), _auth_type_str(), _auth_type(CURLAUTH_NONE)
+  {}
+
+  CurlAuthData(const AuthData & authData)
+    : AuthData(authData)
+    , _auth_type_str()
+    , _auth_type(CURLAUTH_NONE)
+  {}
+
+  CurlAuthData(std::string & username, std::string & password, std::string & auth_type)
+    : AuthData(username,password), _auth_type_str(auth_type)
+  {
+    _auth_type = auth_type_str2long(auth_type);
+  }
+
+  CurlAuthData(std::string & username, std::string & password, long auth_type)
+    : AuthData(username,password), _auth_type(auth_type)
+  {
+    _auth_type_str = auth_type_long2str(auth_type);
+  }
+
+  /**
+   * Checks validity of authentication data.
+   * \return true if the object contains non-empty username,
+   *  non-empty password, and specifies authentication type; false otherwise.
+   */
+  virtual bool valid() const;
+
+  /**
+   * Set HTTP authentication type(s) to use.
+   * \param comma separated list of HTTP authentication type names
+   */
+  void setAuthType(std::string auth_type)
+  {
+    _auth_type_str = auth_type; _auth_type = auth_type_str2long(auth_type);
+  }
+
+  /*
+   * Set HTTP authentication type(s) to use.
+   * \param HTTP authentication type as in long ORed form.
+   * \see curl.h for available auth types
+   */
+  void setAuthType(long auth_type)
+  {
+    _auth_type = auth_type;
+    _auth_type_str = auth_type_long2str(auth_type);
+  }
+
+  long authType() { return _auth_type; } const
+  std::string authTypeAsString() { return _auth_type_str; } const
+
+  std::string getUserPwd() const { return username() + ":" + password(); }
+
+
+  /**
+   * Converts a string of comma separated list of authetication type names
+   * into a long of ORed CURLAUTH_* identifiers.
+   * The method also automatically leaves out any auth types declared
+   * not supported by curl_version_info().
+   *
+   * \throws MediaException if an invalid authentication type name is
+   *         encountered.
+   */
+  static long auth_type_str2long(std::string & auth_type_str);
+
+  /**
+   * Converts a long of ORed CURLAUTH_* identifiers into a string of comma
+   * separated list of authentication type names.
+   */
+  static std::string auth_type_long2str(long auth_type);
+
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+private:
+  std::string _auth_type_str;
+  long _auth_type;
+};
+
+typedef shared_ptr<CurlAuthData> CurlAuthData_Ptr;
+
+std::ostream & operator << (std::ostream & str, const AuthData & auth_data);
+std::ostream & operator << (std::ostream & str, const CurlAuthData & auth_data);
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_USER_AUTH_H
diff --git a/zypp/media/MetaLinkParser.cc b/zypp/media/MetaLinkParser.cc
new file mode 100644 (file)
index 0000000..f974cfa
--- /dev/null
@@ -0,0 +1,481 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MetaLinkParser.cc
+ *
+ */
+
+#include "zypp/media/MetaLinkParser.h"
+#include "zypp/base/Logger.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include <vector>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+enum state {
+  STATE_START,
+  STATE_METALINK,
+  STATE_FILES,
+  STATE_FILE,
+  STATE_M4FILE,
+  STATE_SIZE,
+  STATE_M4SIZE,
+  STATE_VERIFICATION,
+  STATE_HASH,
+  STATE_M4HASH,
+  STATE_PIECES,
+  STATE_M4PIECES,
+  STATE_PHASH,
+  STATE_M4PHASH,
+  STATE_RESOURCES,
+  STATE_URL,
+  STATE_M4URL,
+  NUMSTATES
+};
+
+struct stateswitch {
+  enum state from;
+  string ename;
+  enum state to;
+  int docontent;
+};
+
+static struct stateswitch stateswitches[] = {
+  { STATE_START,        "metalink",     STATE_METALINK, 0 },
+  { STATE_METALINK,     "files",        STATE_FILES, 0 },
+  { STATE_METALINK,     "file",         STATE_M4FILE, 0 },
+  { STATE_FILES,        "file",         STATE_FILE, 0 },
+  { STATE_FILE,         "size",         STATE_SIZE, 1 },
+  { STATE_FILE,         "verification", STATE_VERIFICATION, 0 },
+  { STATE_FILE,         "resources",    STATE_RESOURCES, 0 },
+  { STATE_VERIFICATION, "hash",         STATE_HASH, 1 },
+  { STATE_VERIFICATION, "pieces",       STATE_PIECES, 0 },
+  { STATE_PIECES,       "hash",         STATE_PHASH, 1 },
+  { STATE_RESOURCES,    "url",          STATE_URL, 1 },
+  { STATE_M4FILE,       "size",         STATE_M4SIZE, 1 },
+  { STATE_M4FILE,       "hash",         STATE_M4HASH, 1},
+  { STATE_M4FILE,       "url",          STATE_M4URL, 1},
+  { STATE_M4FILE,       "pieces",       STATE_M4PIECES, 0},
+  { STATE_M4PIECES,     "hash",         STATE_M4PHASH, 1 },
+  { NUMSTATES }
+};
+
+struct ml_url {
+  int priority;
+  string url;
+};
+
+struct ml_parsedata {
+  XML_Parser parser;
+  int depth;
+  enum state state;
+  int statedepth;
+  char *content;
+  int lcontent;
+  int acontent;
+  int docontent;
+  struct stateswitch *swtab[NUMSTATES];
+  enum state sbtab[NUMSTATES];
+
+  int called;
+  int gotfile;
+  off_t size;
+  vector<struct ml_url> urls;
+  int nurls;
+  size_t blksize;
+
+  vector<unsigned char> piece;
+  int npiece;
+  int piecel;
+
+  vector<unsigned char> sha1;
+  int nsha1;
+  vector<unsigned char> zsync;
+  int nzsync;
+
+  vector<unsigned char> chksum;
+  int chksuml;
+};
+
+static const char *
+find_attr(const char *txt, const char **atts)
+{
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, txt))
+        return atts[1];
+    }
+  return 0;
+}
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+  struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
+  struct stateswitch *sw;
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth++;
+      return;
+    }
+  pd->depth++;
+  if (!pd->swtab[pd->state])
+    return;
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
+    if (sw->ename == name)
+      break;
+  if (sw->from != pd->state)
+    return;
+  if ((sw->to == STATE_FILE || sw->to == STATE_M4FILE) && pd->gotfile++)
+    return;    /* ignore all but the first file */
+  //printf("start depth %d name %s\n", pd->depth, name);
+  pd->state = sw->to;
+  pd->docontent = sw->docontent;
+  pd->statedepth = pd->depth;
+  pd->lcontent = 0;
+  *pd->content = 0;
+  switch(pd->state)
+    {
+    case STATE_URL:
+    case STATE_M4URL:
+      {
+       const char *priority = find_attr("priority", atts);
+       const char *preference = find_attr("preference", atts);
+       int prio;
+        pd->urls.push_back(ml_url());
+        if (priority)
+         prio = atoi(priority);
+       else if (preference)
+         prio = 101 - atoi(preference);
+       else
+         prio = 999999;
+       pd->urls.back().priority = prio;
+       break;
+      }
+    case STATE_PIECES:
+    case STATE_M4PIECES:
+      {
+       const char *type = find_attr("type", atts);
+       const char *length = find_attr("length", atts);
+       size_t blksize;
+
+       if (!type || !length)
+         {
+           pd->state = pd->sbtab[pd->state];
+           pd->statedepth--;
+           break;
+         }
+       blksize = strtoul(length, 0, 10);
+       if (!blksize || (pd->blksize && pd->blksize != blksize))
+         {
+           pd->state = pd->sbtab[pd->state];
+           pd->statedepth--;
+           break;
+         }
+       pd->blksize = blksize;
+       pd->npiece = 0;
+        pd->piece.clear();
+       if (!strcmp(type, "sha1") || !strcmp(type, "sha-1"))
+         pd->piecel = 20;
+       else if (!strcmp(type, "zsync"))
+         pd->piecel = 4;
+       else
+         {
+           pd->state = pd->sbtab[pd->state];
+           pd->statedepth--;
+           break;
+         }
+       break;
+      }
+    case STATE_HASH:
+    case STATE_M4HASH:
+      {
+       const char *type = find_attr("type", atts);
+       if (!type)
+         type = "?";
+       if ((!strcmp(type, "sha1") || !strcmp(type, "sha-1")) && pd->chksuml < 20)
+         pd->chksuml = 20;
+       else if (!strcmp(type, "sha256") || !strcmp(type, "sha-256"))
+         pd->chksuml = 32;
+       else
+         {
+           pd->state = pd->sbtab[pd->state];
+           pd->statedepth--;
+           pd->docontent = 0;
+         }
+       break;
+      }
+    case STATE_PHASH:
+    case STATE_M4PHASH:
+      {
+       const char *piece = find_attr("piece", atts);
+       if (pd->state == STATE_PHASH && (!piece || atoi(piece) != pd->npiece))
+         {
+           pd->state = pd->sbtab[pd->state];
+           pd->statedepth--;
+         }
+        break;
+      }
+    default:
+      break;
+    }
+}
+
+static int
+hexstr2bytes(unsigned char *buf, const char *str, int buflen)
+{
+  int i;
+  for (i = 0; i < buflen; i++)
+    {
+#define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
+                : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
+                : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
+                : -1)
+      int v = c2h(*str);
+      str++;
+      if (v < 0)
+        return 0;
+      buf[i] = v;
+      v = c2h(*str);
+      str++;
+      if (v < 0)
+        return 0;
+      buf[i] = (buf[i] << 4) | v;
+#undef c2h
+    }
+  return buflen;
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
+  // printf("end depth %d-%d name %s\n", pd->depth, pd->statedepth, name);
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth--;
+      return;
+    }
+  pd->depth--;
+  pd->statedepth--;
+  switch (pd->state)
+    {
+    case STATE_SIZE:
+    case STATE_M4SIZE:
+      pd->size = (off_t)strtoull(pd->content, 0, 10);
+      break;
+    case STATE_HASH:
+    case STATE_M4HASH:
+      pd->chksum.clear();
+      pd->chksum.resize(pd->chksuml, 0);
+      if (strlen(pd->content) != size_t(pd->chksuml) * 2 || !hexstr2bytes(&pd->chksum[0], pd->content, pd->chksuml))
+       {
+         pd->chksum.clear();
+          pd->chksuml = 0;
+       }
+      break;
+    case STATE_PHASH:
+    case STATE_M4PHASH:
+      if (strlen(pd->content) != size_t(pd->piecel) * 2)
+       break;
+      pd->piece.resize(pd->piecel * (pd->npiece + 1), 0);
+      if (!hexstr2bytes(&pd->piece[pd->piecel * pd->npiece], pd->content, pd->piecel))
+       {
+         pd->piece.resize(pd->piecel * pd->npiece, 0);
+         break;
+       }
+      pd->npiece++;
+      break;
+    case STATE_PIECES:
+    case STATE_M4PIECES:
+      if (pd->piecel == 4)
+       {
+         pd->zsync = pd->piece;
+         pd->nzsync = pd->npiece;
+       }
+      else
+       {
+         pd->sha1 = pd->piece;
+         pd->nsha1 = pd->npiece;
+       }
+      pd->piecel = pd->npiece = 0;
+      pd->piece.clear();
+      break;
+    case STATE_URL:
+    case STATE_M4URL:
+      if (*pd->content)
+       {
+         pd->urls[pd->nurls].url = string(pd->content);
+         pd->nurls++;
+       }
+      break;
+    default:
+      break;
+    }
+  pd->state = pd->sbtab[pd->state];
+  pd->docontent = 0;
+}
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+  struct ml_parsedata *pd = reinterpret_cast<struct ml_parsedata *>(userData);
+  int l;
+  char *c;
+  if (!pd->docontent)
+    return;
+  l = pd->lcontent + len + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = reinterpret_cast<char *>(realloc(pd->content, l + 256));
+      pd->acontent = l + 256;
+    }
+  c = pd->content + pd->lcontent;
+  pd->lcontent += len;
+  while (len-- > 0)
+    *c++ = *s++;
+  *c = 0;
+}
+
+
+MetaLinkParser::MetaLinkParser()
+{
+  struct stateswitch *sw;
+  int i;
+  pd = new ml_parsedata();
+  pd->size = off_t(-1);
+  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
+    {
+      if (!pd->swtab[sw->from])
+        pd->swtab[sw->from] = sw;
+      pd->sbtab[sw->to] = sw->from;
+    }
+  pd->content = reinterpret_cast<char *>(malloc(256));
+  pd->acontent = 256;
+  pd->lcontent = 0;
+  pd->parser = XML_ParserCreate(NULL);
+  XML_SetUserData(pd->parser, pd);
+  XML_SetElementHandler(pd->parser, startElement, endElement);
+  XML_SetCharacterDataHandler(pd->parser, characterData);
+}
+
+MetaLinkParser::~MetaLinkParser()
+{
+  XML_ParserFree(pd->parser);
+  free(pd->content);
+  delete pd;
+}
+
+void
+MetaLinkParser::parse(const Pathname &filename)
+{
+  parse(InputStream(filename));
+}
+
+void
+MetaLinkParser::parse(const InputStream &is)
+{
+  char buf[4096];
+  if (!is.stream())
+    ZYPP_THROW(Exception("MetaLinkParser: no such file"));
+  while (is.stream().good())
+    {
+      is.stream().read(buf, sizeof(buf));
+      parseBytes(buf, is.stream().gcount());
+    }
+  parseEnd();
+}
+
+void
+MetaLinkParser::parseBytes(const char *buf, size_t len)
+{
+  if (!len)
+    return;
+  if (XML_Parse(pd->parser, buf, len, 0) == XML_STATUS_ERROR)
+    ZYPP_THROW(Exception("Parse Error"));
+}
+
+static bool urlcmp(const ml_url &a, const ml_url &b)
+{
+  return a.priority < b.priority;
+}
+
+void
+MetaLinkParser::parseEnd()
+{
+  if (XML_Parse(pd->parser, 0, 0, 1) == XML_STATUS_ERROR)
+    ZYPP_THROW(Exception("Parse Error"));
+  if (pd->nurls)
+    stable_sort(pd->urls.begin(), pd->urls.end(), urlcmp);
+}
+
+std::vector<Url>
+MetaLinkParser::getUrls()
+{
+  std::vector<Url> urls;
+  int i;
+  for (i = 0; i < pd->nurls; ++i)
+    urls.push_back(Url(pd->urls[i].url));
+  return urls;
+}
+
+MediaBlockList
+MetaLinkParser::getBlockList()
+{
+  size_t i;
+  MediaBlockList bl(pd->size);
+  if (pd->chksuml == 20)
+    bl.setFileChecksum("SHA1", pd->chksuml, &pd->chksum[0]);
+  else if (pd->chksuml == 32)
+    bl.setFileChecksum("SHA256", pd->chksuml, &pd->chksum[0]);
+  if (pd->size != off_t(-1) && pd->blksize)
+    {
+      size_t nb = (pd->size + pd->blksize - 1) / pd->blksize;
+      off_t off = 0;
+      size_t size = pd->blksize;
+      for (i = 0; i < nb; i++)
+       {
+         if (i == nb - 1)
+           {
+             size = pd->size % pd->blksize;
+             if (!size)
+               size = pd->blksize;
+           }
+          size_t blkno = bl.addBlock(off, size);
+          if (int(i) < pd->nsha1)
+           {
+             bl.setChecksum(blkno, "SHA1", 20, &pd->sha1[20 * i]);
+             if (int(i) < pd->nzsync)
+               {
+                 unsigned char *p = &pd->zsync[4 * i];
+                 bl.setRsum(blkno, 4, p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24, pd->blksize);
+               }
+           }
+         off += pd->blksize;
+       }
+    }
+  return bl;
+}
+
+  } // namespace media
+} // namespace zypp
+
diff --git a/zypp/media/MetaLinkParser.h b/zypp/media/MetaLinkParser.h
new file mode 100644 (file)
index 0000000..81d62b9
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/MetaLinkParser.h
+ *
+*/
+#ifndef ZYPP_MEDIA_METALINKPARSER_H
+#define ZYPP_MEDIA_METALINKPARSER_H
+
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/media/MediaBlockList.h"
+#include "zypp/Url.h"
+
+namespace zypp {
+  namespace media {
+
+struct ml_parsedata;
+
+class MetaLinkParser : private zypp::base::NonCopyable {
+public:
+  MetaLinkParser();
+  ~MetaLinkParser();
+
+  /**
+   * parse a file consisting of metalink xml data
+   * \throws Exception
+   **/
+  void parse(const Pathname &filename);
+
+  /**
+   * parse an InputStream consisting of metalink xml data
+   * \throws Exception
+   **/
+  void parse(const InputStream &is);
+
+  /**
+   * parse a chunk of a file consisting of metalink xml data.
+   * \throws Exception
+   **/
+  void parseBytes(const char* bytes, size_t len);
+  /**
+   * tells the parser that all chunks are now processed
+   * \throws Exception
+   **/
+  void parseEnd();
+
+  /**
+   * return the download urls from the parsed metalink data
+   **/
+  std::vector<Url> getUrls();
+  /**
+   * return the block list from the parsed metalink data
+   **/
+  MediaBlockList getBlockList();
+
+private:
+  struct ml_parsedata *pd;
+};
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_METALINKPARSER_H
diff --git a/zypp/media/Mount.cc b/zypp/media/Mount.cc
new file mode 100644 (file)
index 0000000..66da0df
--- /dev/null
@@ -0,0 +1,352 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/Mount.cc
+ *
+*/
+
+#include <mntent.h>
+
+#include <cstdio>
+#include <climits>
+#include <cerrno>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+#include "zypp/base/ExternalDataSource.h"
+#include "zypp/base/Logger.h"
+#include "zypp/media/Mount.h"
+#include "zypp/media/MediaException.h"
+
+#ifndef N_
+#define N_(STR) STR
+#endif
+
+using namespace std;
+
+namespace zypp {
+  namespace media {
+
+    std::ostream & operator<<( std::ostream & str, const MountEntry & obj )
+    {
+      str << obj.src << " on " << obj.dir << " type " << obj.type;
+      if ( ! obj.opts.empty() )
+        str << " (" << obj.opts << ")";
+      return str;
+    }
+
+
+Mount::Mount()
+{
+    process = 0;
+    exit_code = -1;
+}
+
+Mount::~Mount()
+{
+   MIL <<  "~Mount()" << endl;
+
+   if ( process )
+      delete process;
+
+   process = NULL;
+
+   MIL << "~Mount() end" << endl;
+}
+
+void Mount::mount( const std::string & source,
+                   const std::string & target,
+                   const std::string & filesystem,
+                   const std::string & options,
+                   const Environment & environment )
+{
+    const char *const argv[] = {
+       "/bin/mount",
+       "-t", filesystem.c_str(),
+       "-o", options.c_str(),
+       source.c_str(),
+       target.c_str(),
+       NULL
+     };
+
+    std::string err;
+
+    this->run(argv, environment, ExternalProgram::Stderr_To_Stdout);
+
+    if ( process == NULL )
+    {
+      ZYPP_THROW(MediaMountException("Mounting media failed", source, target));
+    }
+
+    string value;
+    string output = process->receiveLine();
+
+    // parse error messages
+    while ( output.length() > 0)
+    {
+       string::size_type       ret;
+
+       // extract \n
+       ret = output.find_first_of ( "\n" );
+       if ( ret != string::npos )
+       {
+           value.assign ( output, 0, ret );
+       }
+       else
+       {
+           value = output;
+       }
+
+       DBG << "stdout: " << value << endl;
+
+       if  ( value.find ( "is already mounted on" ) != string::npos )
+       {
+           err = "Media already mounted";
+       }
+       else if  ( value.find ( "ermission denied" ) != string::npos )
+       {
+           err = "Permission denied";
+       }
+       else if  ( value.find ( "wrong fs type" ) != string::npos )
+       {
+           err = "Invalid filesystem on media";
+       }
+       else if  ( value.find ( "No medium found" ) != string::npos )
+       {
+           err = "No medium found";
+       }
+       else if  ( value.find ( "Not a directory" ) != string::npos )
+       {
+           if( filesystem == "nfs" || filesystem == "nfs4" )
+           {
+               err = "Nfs path is not a directory";
+           }
+           else
+           {
+              err = "Unable to find directory on the media";
+           }
+       }
+
+       output = process->receiveLine();
+    }
+
+    int status = Status();
+
+    if ( status == 0 )
+    {
+       // return codes overwites parsed error message
+       err = "";
+    }
+    else if ( status != 0 && err == "" )
+    {
+        err = "Mounting media failed";
+    }
+
+    if ( err != "" ) {
+      WAR << "mount " << source << " " << target << ": " << err << endl;
+      ZYPP_THROW(MediaMountException(err, source, target, value));
+    } else {
+      MIL << "mounted " << source << " " << target << endl;
+    }
+}
+
+void Mount::umount( const std::string & path )
+{
+    const char *const argv[] = {
+       "/bin/umount",
+       path.c_str(),
+       NULL
+     };
+
+    std::string err;
+
+    this->run(argv, ExternalProgram::Stderr_To_Stdout);
+
+    if ( process == NULL )
+    {
+        ZYPP_THROW(MediaUnmountException("E_mount_failed", path));
+    }
+
+    string value;
+    string output = process->receiveLine();
+
+    // parse error messages
+    while ( output.length() > 0)
+    {
+       string::size_type       ret;
+
+       // extract \n
+       ret = output.find_first_of ( "\n" );
+       if ( ret != string::npos )
+       {
+           value.assign ( output, 0, ret );
+       }
+       else
+       {
+           value = output;
+       }
+
+       DBG << "stdout: " << value << endl;
+
+       // if  ( value.find ( "not mounted" ) != string::npos )
+       // {
+       //    err = Error::E_already_mounted;
+       // }
+
+       if  ( value.find ( "device is busy" ) != string::npos )
+       {
+           err = "Device is busy";
+       }
+
+       output = process->receiveLine();
+    }
+
+    int status = Status();
+
+    if ( status == 0 )
+    {
+       // return codes overwites parsed error message
+       err = "";
+    }
+    else if ( status != 0 && err == "" )
+    {
+       err = "Unmounting media failed";
+    }
+
+    if ( err != "") {
+      WAR << "umount " << path << ": " << err << endl;
+      ZYPP_THROW(MediaUnmountException(err, path));
+    } else {
+      MIL << "unmounted " << path << endl;
+    }
+}
+
+void Mount::run( const char *const *argv, const Environment& environment,
+                ExternalProgram::Stderr_Disposition disp )
+{
+  exit_code = -1;
+
+  if ( process != NULL )
+  {
+     delete process;
+     process = NULL;
+  }
+  // Launch the program
+
+  process = new ExternalProgram(argv, environment, disp, false, -1, true);
+}
+
+/*--------------------------------------------------------------*/
+/* Return the exit status of the Mount process, closing the    */
+/* connection if not already done                              */
+/*--------------------------------------------------------------*/
+int Mount::Status()
+{
+   if ( process == NULL )
+      return -1;
+
+   exit_code = process->close();
+   process->kill();
+   delete process;
+   process = 0;
+
+   DBG << "exit code: " << exit_code << endl;
+
+   return exit_code;
+}
+
+/* Forcably kill the process */
+void Mount::Kill()
+{
+  if (process) process->kill();
+}
+
+// STATIC
+MountEntries
+Mount::getEntries(const std::string &mtab)
+{
+  MountEntries             entries;
+  std::vector<std::string> mtabs;
+  bool                     verbose = false;
+
+  if( mtab.empty())
+  {
+    mtabs.push_back("/etc/mtab");
+    mtabs.push_back("/proc/mounts");
+  }
+  else
+  {
+    mtabs.push_back(mtab);
+  }
+
+  std::vector<std::string>::const_iterator t;
+  for( t=mtabs.begin(); t != mtabs.end(); ++t)
+  {
+    if( verbose)
+    {
+      DBG << "Reading mount table from '" << *t << "'" << std::endl;
+    }
+    FILE *fp = setmntent(t->c_str(), "r");
+    if( fp)
+    {
+      char          buf[PATH_MAX * 4];
+      struct mntent ent;
+
+      memset(buf,  0, sizeof(buf));
+      memset(&ent, 0, sizeof(ent));
+
+      while( getmntent_r(fp, &ent, buf, sizeof(buf)) != NULL)
+      {
+        if( ent.mnt_fsname && *ent.mnt_fsname &&
+            ent.mnt_dir    && *ent.mnt_dir    &&
+            ent.mnt_type   && *ent.mnt_type   &&
+            ent.mnt_opts   && *ent.mnt_opts)
+        {
+          MountEntry entry(
+            ent.mnt_fsname, ent.mnt_dir,
+            ent.mnt_type,   ent.mnt_opts,
+            ent.mnt_freq,   ent.mnt_passno
+          );
+
+          entries.push_back(entry);
+
+          memset(buf,  0, sizeof(buf));
+          memset(&ent, 0, sizeof(ent));
+        }
+      }
+      endmntent(fp);
+
+      if( entries.empty())
+      {
+        WAR << "Unable to read any entry from the mount table '" << *t << "'"
+           << std::endl;
+      }
+      else
+      {
+       // OK, have a non-empty mount table.
+        t = mtabs.end();
+       break;
+      }
+    }
+    else
+    {
+      int err = errno;
+      verbose = true;
+      WAR << "Failed to read the mount table '" << *t << "': "
+          << ::strerror(err)
+         << std::endl;
+      errno = err;
+    }
+  }
+  return entries;
+}
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/Mount.h b/zypp/media/Mount.h
new file mode 100644 (file)
index 0000000..1c95dda
--- /dev/null
@@ -0,0 +1,181 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/Mount.h
+ *
+*/
+
+// -*- C++ -*-
+
+#ifndef ZYPP_MEDIA_MOUNT_H
+#define ZYPP_MEDIA_MOUNT_H
+
+#include <set>
+#include <map>
+#include <string>
+#include <iosfwd>
+
+#include "zypp/ExternalProgram.h"
+#include "zypp/KVMap.h"
+
+namespace zypp {
+  namespace media {
+
+
+    /**
+     * A "struct mntent" like mount entry structure,
+     * but using std::strings.
+     */
+    struct MountEntry
+    {
+        MountEntry(const std::string &source,
+                   const std::string &target,
+                   const std::string &fstype,
+                   const std::string &options,
+                   const int         dumpfreq = 0,
+                   const int         passnum  = 0)
+            : src(source)
+            , dir(target)
+            , type(fstype)
+            , opts(options)
+            , freq(dumpfreq)
+            , pass(passnum)
+        {}
+
+        std::string src;  //!< name of mounted file system
+        std::string dir;  //!< file system path prefix
+        std::string type; //!< filesystem / mount type
+        std::string opts; //!< mount options
+        int         freq; //!< dump frequency in days
+        int         pass; //!< pass number on parallel fsck
+    };
+
+    /** \relates MountEntry
+     * A vector of mount entries.
+     */
+    typedef std::vector<MountEntry> MountEntries;
+
+    /** \relates MountEntry Stream output */
+    std::ostream & operator<<( std::ostream & str, const MountEntry & obj );
+
+    /**
+     * @short Interface to the mount program
+     */
+    class Mount
+    {
+    public:
+
+       /**
+        * For passing additional environment variables
+        * to mount
+        **/
+       typedef ExternalProgram::Environment Environment;
+
+       /**
+        * Mount options. 'key' or 'key=value' pairs, separated by ','
+        **/
+       typedef KVMap<kvmap::KVMapBase::CharSep<'=',','> > Options;
+
+    public:
+
+       /**
+       * Create an new instance.
+       */
+       Mount();
+
+       /**
+       * Clean up.
+       */
+       ~Mount();
+
+       /**
+       * mount device
+       *
+       * @param source what to mount (e.g. /dev/hda3)
+       * @param target where to mount (e.g. /mnt)
+       * @param filesystem which filesystem to use (e.g. reiserfs) (-t parameter)
+       * @param options mount options (e.g. ro) (-o parameter)
+       * @param environment optinal environment to pass (e.g. PASSWD="sennah")
+        *
+        * \throws MediaException
+        *
+       */
+
+       void mount ( const std::string& source,
+                       const std::string& target,
+                       const std::string& filesystem,
+                       const std::string& options,
+                       const Environment& environment = Environment() );
+
+       /** umount device
+        *
+        * @param path device or mountpoint to umount
+        *
+        * \throws MediaException
+        *
+        * */
+       void umount (const std::string& path);
+
+    public:
+
+       /**
+       * Return mount entries from /etc/mtab or /etc/fstab file.
+       *
+       * @param mtab The name of the (mounted) file system description
+       *             file to read from. This file should be one /etc/mtab,
+       *             /etc/fstab or /proc/mounts. Default is to try the
+       *             /etc/mtab and fail back to /proc/mounts.
+       * @returns A vector with mount entries or empty vector if reading
+       *          or parsing of the mtab file(s) failed.
+       */
+       static MountEntries
+       getEntries(const std::string &mtab = "");
+
+    private:
+
+       /** The connection to the mount process.
+        * */
+       ExternalProgram *process;
+
+       /**
+        * Run mount with the specified arguments and handle stderr.
+        * @param argv Mount arguments
+        * @param environment Addittional environment to set
+        * @param stderr_disp How to handle stderr, merged with stdout by default
+        * */
+       void run( const char *const *argv, const Environment& environment,
+                 ExternalProgram::Stderr_Disposition stderr_disp =
+                 ExternalProgram::Stderr_To_Stdout);
+
+       void run( const char *const *argv,
+                 ExternalProgram::Stderr_Disposition stderr_disp =
+                 ExternalProgram::Stderr_To_Stdout) {
+         Environment notused;
+         run( argv, notused, stderr_disp );
+       }
+
+       /** Return the exit status of the process, closing the connection if
+        * not already done.
+        * */
+       int Status();
+
+       /** Forcably kill the process
+        * */
+       void Kill();
+
+
+       /** The exit code of the process, or -1 if not yet known.
+        * */
+       int exit_code;
+    };
+
+
+  } // namespace media
+} // namespace zypp
+
+#endif
diff --git a/zypp/media/ProxyInfo.cc b/zypp/media/ProxyInfo.cc
new file mode 100644 (file)
index 0000000..6e05271
--- /dev/null
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/ProxyInfo.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/media/ProxyInfo.h"
+#include "zypp/media/proxyinfo/ProxyInfoImpl.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+    shared_ptr<ProxyInfo::Impl> ProxyInfo::Impl::_nullimpl;
+
+    ProxyInfo::ProxyInfo()
+    : _pimpl( Impl::_nullimpl )
+    {}
+    ProxyInfo::ProxyInfo(ProxyInfo::ImplPtr pimpl_r)
+    : _pimpl(pimpl_r)
+    {}
+
+    bool ProxyInfo::enabled() const
+    { return _pimpl->enabled(); }
+
+    std::string ProxyInfo::proxy(const Url & url_r) const
+    { return _pimpl->proxy(url_r); }
+
+    ProxyInfo::NoProxyList ProxyInfo::noProxy() const
+    { return _pimpl->noProxy(); }
+
+    ProxyInfo::NoProxyIterator ProxyInfo::noProxyBegin() const
+    { return _pimpl->noProxyBegin(); }
+
+    ProxyInfo::NoProxyIterator ProxyInfo::noProxyEnd() const
+    { return _pimpl->noProxyEnd(); }
+
+    bool ProxyInfo::useProxyFor( const Url & url_r ) const
+    { return _pimpl->useProxyFor( url_r ); }
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/ProxyInfo.h b/zypp/media/ProxyInfo.h
new file mode 100644 (file)
index 0000000..2e790b2
--- /dev/null
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/ProxyInfo.h
+ *
+*/
+#ifndef ZYPP_MEDIA_PROXYINFO_H
+#define ZYPP_MEDIA_PROXYINFO_H
+
+#include <string>
+#include <list>
+
+#include "zypp/base/PtrTypes.h"
+
+namespace zypp {
+
+  class Url;
+
+  namespace media {
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ProxyInfo
+    class ProxyInfo
+    {
+    public:
+      typedef intrusive_ptr<ProxyInfo> Ptr;
+      typedef intrusive_ptr<ProxyInfo> constPtr;
+      typedef std::list<std::string> NoProxyList;
+      typedef std::list<std::string>::const_iterator NoProxyIterator;
+
+      /** Implementation */
+      struct Impl;
+      typedef shared_ptr<Impl> ImplPtr;
+      /** Ctor */
+      ProxyInfo();
+      /** Ctor */
+      ProxyInfo(ProxyInfo::ImplPtr pimpl_r);
+      bool enabled() const;
+      std::string proxy(const Url & url) const;
+      NoProxyList noProxy() const;
+      NoProxyIterator noProxyBegin() const;
+      NoProxyIterator noProxyEnd() const;
+
+      /** Return \c true if  \ref enabled and \a url_r does not match \ref noProxy. */
+      bool useProxyFor( const Url & url_r ) const;
+
+    private:
+      /** Pointer to implementation */
+      RW_pointer<Impl> _pimpl;
+    };
+
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_PROXYINFO_H
diff --git a/zypp/media/TransferSettings.cc b/zypp/media/TransferSettings.cc
new file mode 100644 (file)
index 0000000..5efdbc2
--- /dev/null
@@ -0,0 +1,318 @@
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/WatchFile.h"
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/media/TransferSettings.h"
+#include "zypp/ZConfig.h"
+
+using namespace std;
+
+#define ARIA2C_BINARY "/usr/bin/aria2c"
+#define CURL_BINARY "/usr/bin/curl"
+
+namespace zypp
+{
+namespace media
+{
+    
+class TransferSettings::Impl
+{
+public:
+    Impl()
+        : _useproxy(false)
+        , _timeout(0)
+        , _connect_timeout(0)
+        , _maxConcurrentConnections(ZConfig::instance().download_max_concurrent_connections())
+        , _minDownloadSpeed(ZConfig::instance().download_min_download_speed())
+        , _maxDownloadSpeed(ZConfig::instance().download_max_download_speed())
+        , _maxSilentTries(ZConfig::instance().download_max_silent_tries())
+        , _verify_host(false)
+        , _verify_peer(false)
+        , _ca_path("/etc/ssl/certs")
+        , _head_requests_allowed(true)
+    {}
+
+    virtual ~Impl()
+    {}    
+
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+
+private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+
+public:
+    vector<string> _headers;
+    string _useragent;
+    string _username;
+    string _password;
+    bool _useproxy;
+    string _proxy;
+    string _proxy_username;
+    string _proxy_password;
+    string _authtype;
+    long _timeout;
+    long _connect_timeout;
+    Url _url;
+    Pathname _targetdir;
+
+    long _maxConcurrentConnections;
+    long _minDownloadSpeed;
+    long _maxDownloadSpeed;
+    long _maxSilentTries;
+
+    bool _verify_host;
+    bool _verify_peer;
+    Pathname _ca_path;
+    // workarounds
+    bool _head_requests_allowed;
+};
+    
+TransferSettings::TransferSettings()
+    : _impl(new TransferSettings::Impl())
+{
+
+}
+
+void TransferSettings::reset()
+{
+    _impl.reset(new TransferSettings::Impl());
+}
+
+void TransferSettings::addHeader( const std::string &header )
+{
+    _impl->_headers.push_back(header);
+}
+
+TransferSettings::Headers::const_iterator TransferSettings::headersBegin() const
+{
+    return _impl->_headers.begin();
+}
+
+TransferSettings::Headers::const_iterator TransferSettings::headersEnd() const
+{
+    return _impl->_headers.end();
+}
+
+void TransferSettings::setUserAgentString( const std::string &agent )
+{
+    _impl->_useragent = agent;
+}
+
+std::string TransferSettings::userAgentString() const
+{
+    return _impl->_useragent;
+}
+
+void TransferSettings::setUsername( const std::string &username )
+{
+    _impl->_username = username;
+}
+
+std::string TransferSettings::username() const
+{
+    return _impl->_username;
+}
+
+void TransferSettings::setPassword( const std::string &password )
+{
+    _impl->_password = password;
+}
+
+void TransferSettings::setAnonymousAuth()
+{
+    setUsername("anonymous");
+    string id = "yast@";
+    setPassword(id + VERSION);
+}
+
+std::string TransferSettings::password() const
+{
+    return _impl->_password;
+}
+
+std::string TransferSettings::userPassword() const
+{
+    string userpwd = username();
+    if ( password().size() ) {
+        userpwd += ":" + password();
+    }
+    return userpwd;
+}
+
+void TransferSettings::setProxyEnabled( bool enabled )
+{
+    _impl->_useproxy = enabled;
+}
+
+bool TransferSettings::proxyEnabled() const
+{
+    return _impl->_useproxy;
+}
+
+void TransferSettings::setProxy( const std::string &proxy )
+{
+    _impl->_proxy = proxy;
+}
+
+std::string TransferSettings::proxy() const
+{
+    return _impl->_proxy;
+}
+
+void TransferSettings::setProxyUsername( const std::string &proxyuser )
+{
+    _impl->_proxy_username = proxyuser;
+}
+
+std::string TransferSettings::proxyUsername() const
+{
+    return _impl->_proxy_username;
+}
+
+void TransferSettings::setProxyPassword( const std::string &proxypass )
+{
+    _impl->_proxy_password = proxypass;
+}
+
+std::string TransferSettings::proxyPassword() const
+{
+    return _impl->_proxy_password;
+}
+
+std::string TransferSettings::proxyUserPassword() const
+{
+    string userpwd = proxyUsername();
+    if ( proxyPassword().size() ) {
+        userpwd += ":" + proxyPassword();
+    }
+    return userpwd;
+}
+
+void TransferSettings::setTimeout( long t )
+{
+    _impl->_timeout = t;
+}
+
+long TransferSettings::timeout() const
+{
+    return _impl->_timeout;
+}
+
+void TransferSettings::setConnectTimeout( long t )
+{
+    _impl->_connect_timeout = t;
+}
+
+long TransferSettings::connectTimeout() const
+{
+    return _impl->_connect_timeout;
+}
+
+long TransferSettings::maxConcurrentConnections() const
+{
+    return _impl->_maxConcurrentConnections;
+}
+
+void TransferSettings::setMaxConcurrentConnections(long v)
+{
+    _impl->_maxConcurrentConnections = v;
+}
+
+long TransferSettings::minDownloadSpeed() const
+{
+    return _impl->_minDownloadSpeed;
+}
+
+void TransferSettings::setMinDownloadSpeed(long v)
+{
+    _impl->_minDownloadSpeed = v;
+}
+
+long TransferSettings::maxDownloadSpeed() const
+{
+    return _impl->_maxDownloadSpeed;
+}
+
+void TransferSettings::setMaxDownloadSpeed(long v)
+{
+    _impl->_maxDownloadSpeed = v;
+}
+
+long TransferSettings::maxSilentTries() const
+{
+    return _impl->_maxSilentTries;
+}
+
+void TransferSettings::setMaxSilentTries(long v)
+{
+    _impl->_maxSilentTries = v;
+}
+
+bool TransferSettings::verifyHostEnabled() const
+{
+    return _impl->_verify_host;
+}
+
+void TransferSettings::setVerifyHostEnabled( bool enabled )
+{
+    _impl->_verify_host = enabled;
+}
+
+bool TransferSettings::verifyPeerEnabled() const
+{
+    return _impl->_verify_peer;
+}
+
+
+void TransferSettings::setVerifyPeerEnabled( bool enabled )
+{
+    _impl->_verify_peer = enabled;
+}
+
+Pathname TransferSettings::certificateAuthoritiesPath() const
+{
+    return _impl->_ca_path;
+}
+
+void TransferSettings::setCertificateAuthoritiesPath( const zypp::Pathname &path )
+{
+    _impl->_ca_path = path;
+}
+
+void TransferSettings::setAuthType( const std::string &authtype)
+{
+    _impl->_authtype = authtype;
+}
+
+std::string TransferSettings::authType() const
+{
+    return _impl->_authtype;
+}
+
+void TransferSettings::setHeadRequestsAllowed(bool allowed)
+{
+    _impl->_head_requests_allowed = allowed;    
+}    
+
+bool TransferSettings::headRequestsAllowed() const
+{
+    return _impl->_head_requests_allowed;    
+} 
+
+} // ns media
+} // ns zypp
+
diff --git a/zypp/media/TransferSettings.h b/zypp/media/TransferSettings.h
new file mode 100644 (file)
index 0000000..4806056
--- /dev/null
@@ -0,0 +1,265 @@
+
+#ifndef TRANSFER_SETTINGS_H_
+#define TRANSFER_SETTINGS_H_
+
+#include <string>
+#include <vector>
+#include "zypp/base/Flags.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+
+namespace zypp
+{
+namespace media
+{
+
+/**
+ * Holds transfer setting
+ */
+class TransferSettings
+{
+public:
+  /**
+   * Constructs a transfer program cmd line access.
+   */
+  TransferSettings();
+
+  /**
+   * Constructs the settings from a url object where.
+   * authentication/proxy  information can be extracted
+   * from the url
+   */
+  TransferSettings( const zypp::Url &url );
+
+  typedef std::vector<std::string> Headers;
+
+  /**
+   * reset the settings to the defaults
+   */
+  void reset();
+
+  /**
+   * add a header, on the form "Foo: Bar"
+   */
+  void addHeader( const std::string &header );
+
+  /**
+   * begin iterators to additional headers 
+   */
+  Headers::const_iterator headersBegin() const;
+
+  /**
+   * end iterators to additional headers 
+   */
+  Headers::const_iterator headersEnd() const;
+
+  /**
+   * sets the user agent ie: "Mozilla v3"
+   */
+  void setUserAgentString( const std::string &agent );
+
+  /**
+   * user agent string
+   */
+  std::string userAgentString() const;
+
+  /**
+   * sets the auth username
+   */
+  void setUsername( const std::string &username );
+
+  /**
+   * auth username
+   */
+  std::string username() const;
+
+  /**
+   * sets the auth password
+   */
+  void setPassword( const std::string &password );
+
+  /**
+   * auth password
+   */
+  std::string password() const;
+
+  /**
+   * returns the user and password as 
+   * a user:pass string
+   */
+  std::string userPassword() const;
+
+  /**
+   * sets anonymous authentication (ie: for ftp)
+   */
+  void setAnonymousAuth();
+
+  /**
+   * whether the proxy is used or not
+   */
+  void setProxyEnabled( bool enabled );
+
+  /**
+   * proxy is enabled
+   */
+  bool proxyEnabled() const;
+
+  /**
+   * proxy to use if it is enabled
+   */
+  void setProxy( const std::string &proxyhost );
+
+  /**
+   * proxy host
+   */
+  std::string proxy() const;
+
+  /**
+   * sets the proxy user
+   */
+  void setProxyUsername( const std::string &proxyuser );
+
+  /**
+   * proxy auth username
+   */
+  std::string proxyUsername() const;
+
+  /**
+   * sets the proxy password
+   */
+  void setProxyPassword( const std::string &proxypass );
+
+  /**
+   * proxy auth password
+   */
+  std::string proxyPassword() const;
+
+  /**
+   * returns the proxy user and password as 
+   * a user:pass string
+   */
+  std::string proxyUserPassword() const;
+
+  /**
+   * set the connect timeout
+   */
+  void setConnectTimeout( long t );
+
+  /**
+   * connection timeout
+   */
+  long connectTimeout() const;
+
+  /**
+   * set the transfer timeout
+   */
+  void setTimeout( long t );
+
+  /**
+   * transfer timeout
+   */
+  long timeout() const;
+
+  /**
+   * Maximum number of concurrent connections for a single transfer
+   */
+  long maxConcurrentConnections() const;
+
+  /**
+   * Set maximum number of concurrent connections for a single transfer
+   */
+  void setMaxConcurrentConnections(long v);
+
+  /**
+   * Minimum download speed (bytes per second)
+   * until the connection is dropped
+   */
+  long minDownloadSpeed() const;
+  
+  /**
+   * Set minimum download speed (bytes per second)
+   * until the connection is dropped
+   */
+  void setMinDownloadSpeed(long v);
+
+  /**
+   * Maximum download speed (bytes per second)
+   */
+  long maxDownloadSpeed() const;
+
+  /**
+   * Set max download speed (bytes per second)
+   */
+  void setMaxDownloadSpeed(long v);
+
+  /**
+   * Maximum silent retries
+   */
+  long maxSilentTries() const;
+
+  /**
+   * Set maximum silent retries
+   */
+  void setMaxSilentTries(long v);
+
+  /**
+   * Whether to verify host for ssl
+   */
+  bool verifyHostEnabled() const;
+
+  /**
+   * Sets whether to verify host for ssl
+   */
+  void setVerifyHostEnabled( bool enabled );
+
+  /**
+   * Whether to verify peer for ssl
+   */
+  bool verifyPeerEnabled() const;
+
+  /**
+   * Sets whether to verify host for ssl
+   */
+  void setVerifyPeerEnabled( bool enabled );
+
+  /**
+   * SSL certificate authorities path
+   * ( default: /etc/ssl/certs )
+   */
+  Pathname certificateAuthoritiesPath() const;
+
+  /**
+   * Sets the SSL certificate authorities path
+   */
+  void setCertificateAuthoritiesPath( const zypp::Pathname &path );
+
+  /**
+   * set the allowed authentication types
+   */
+  void setAuthType( const std::string &authtype );
+
+  /**
+   * get the allowed authentication types
+   */
+  std::string authType() const;
+
+  /**
+   * set whether HEAD requests are allowed
+   */
+  void setHeadRequestsAllowed(bool allowed);
+
+  /**
+   * whether HEAD requests are allowed
+   */
+  bool headRequestsAllowed() const;
+
+protected:
+  class Impl;
+  RWCOW_pointer<Impl> _impl;
+};
+
+} // ns media
+} // ns zypp
+
+#endif
diff --git a/zypp/media/UrlResolverPlugin.cc b/zypp/media/UrlResolverPlugin.cc
new file mode 100644 (file)
index 0000000..1c6572d
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/UrlResolverPlugin.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/media/UrlResolverPlugin.h"
+#include "zypp/media/MediaException.h"
+#include "zypp/PluginScript.h"
+#include "zypp/ZConfig.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    /** UrlResolverPlugin implementation. */
+    struct UrlResolverPlugin::Impl
+    {
+
+
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    Url UrlResolverPlugin::resolveUrl(const Url & o_url, HeaderList &headers)
+    {
+        if (o_url.getScheme() != "plugin")
+            return o_url;        
+        
+        Url url(o_url);
+        std::string name = url.getPathName();
+        Pathname plugin_path = (ZConfig::instance().pluginsPath()/"urlresolver")/name;    
+        if (PathInfo(plugin_path).isExist()) {
+            PluginScript scr;
+            scr.open(plugin_path);
+            // send frame to plugin
+            PluginFrame f("RESOLVEURL");
+
+            url::ParamMap params = url.getQueryStringMap();
+            url::ParamMap::const_iterator param_it;
+            for( param_it = params.begin();
+                 param_it != params.end();
+                 ++param_it)
+                f.setHeader(param_it->first, param_it->second);
+            
+            scr.send(f);
+
+            PluginFrame r(scr.receive());
+            if (r.command() == "RESOLVEDURL") {
+                // now set
+                url = Url(r.body());
+                PluginFrame::HeaderListIterator it;
+                
+                for (it = r.headerBegin();
+                     it != r.headerEnd();
+                     ++it) {
+                    std::pair<std::string, std::string> values(*it);
+                    // curl resets headers that are empty, so we use a workaround
+                    if (values.second.empty()) {
+                        values.second = "\nX-libcurl-Empty-Header-Workaround: *";
+                    }                    
+                    headers.insert(values);                    
+                }
+            }
+            else if (r.command() == "ERROR") {
+                ZYPP_THROW(MediaException(r.body()));
+            }            
+        }
+        return url;        
+    }
+
+    /** \relates UrlResolverPlugin::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin::Impl & obj )
+    {
+      return str << "UrlResolverPlugin::Impl";
+    }
+
+    UrlResolverPlugin::~UrlResolverPlugin()
+    {}
+
+    std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj )
+    {
+      return str << *obj._pimpl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/media/UrlResolverPlugin.h b/zypp/media/UrlResolverPlugin.h
new file mode 100644 (file)
index 0000000..b1f5bad
--- /dev/null
@@ -0,0 +1,75 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/media/UrlResolverPlugin.h
+ *
+*/
+#ifndef ZYPP_MEDIA_URLRESOLVERPLUGIN_H
+#define ZYPP_MEDIA_URLRESOLVERPLUGIN_H
+
+#include <iosfwd>
+#include <map>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/Url.h"
+#include "zypp/PathInfo.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+    /** 
+     *
+     */
+    class UrlResolverPlugin
+    {
+      friend std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj );
+
+    public:
+
+      class Impl;
+
+      typedef std::multimap<std::string, std::string> HeaderList;
+
+      /**
+       * Resolves an url using the installed plugins
+       * If no plugin is found the url is resolved as
+       * its current value.
+       *
+       * Custom headers are inserted in the provided header list
+       */
+      static Url resolveUrl(const Url &url, HeaderList &headers);
+
+    public:
+      /** Dtor */
+      ~UrlResolverPlugin();
+
+    private:
+
+      /** Default ctor */
+      UrlResolverPlugin();
+
+      /** Pointer to implementation */
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates UrlResolverPlugin Stream output */
+    std::ostream & operator<<( std::ostream & str, const UrlResolverPlugin & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MEDIA_URLRESOLVERPLUGIN_H
diff --git a/zypp/media/ZsyncParser.cc b/zypp/media/ZsyncParser.cc
new file mode 100644 (file)
index 0000000..55c02a8
--- /dev/null
@@ -0,0 +1,146 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/ZsyncParser.cc
+ *
+ */
+
+#include "zypp/media/ZsyncParser.h"
+#include "zypp/base/Logger.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+ZsyncParser::ZsyncParser()
+{
+  filesize = off_t(-1);
+  blksize = 0;
+  sql = rsl = csl = 0;
+}
+
+static int
+hexstr2bytes(unsigned char *buf, const char *str, int buflen)
+{
+  int i;
+  for (i = 0; i < buflen; i++)
+    {
+#define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
+                : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
+                : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
+                : -1)
+      int v = c2h(*str);
+      str++;
+      if (v < 0)
+        return 0;
+      buf[i] = v;
+      v = c2h(*str);
+      str++;
+      if (v < 0)
+        return 0;
+      buf[i] = (buf[i] << 4) | v;
+#undef c2h
+    }
+  return buflen;
+}
+
+void
+ZsyncParser::parse(string filename)
+{
+  char buf[4096];
+
+  std::ifstream is(filename.c_str());
+  if (!is)
+    ZYPP_THROW(Exception("ZsyncParser: no such file"));
+  is.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
+  off_t filesize = off_t(-1);
+  while (is.good())
+    {
+      is.getline(buf, sizeof(buf));
+      if (!*buf)
+       break;
+      if (!strncmp(buf, "Length: ", 8))
+        filesize = (off_t)strtoull(buf + 8, 0, 10);
+      else if (!strncmp(buf, "Hash-Lengths: ", 14))
+        (void)sscanf(buf + 14, "%d,%d,%d", &sql, &rsl, &csl);
+      else if (!strncmp(buf, "Blocksize: ", 11))
+        blksize = atoi(buf + 11);
+      else if (!strncmp(buf, "URL: http://", 12) || !strncmp(buf, "URL: https://", 13) || !strncmp(buf, "URL: ftp://", 11))
+       urls.push_back(buf + 5);
+      else if (!strncmp(buf, "SHA-1: ", 7))
+       {
+         unsigned char sha1[20];
+         if (hexstr2bytes(sha1, buf + 7, 20) == 20)
+           bl.setFileChecksum("SHA1", 20, sha1);
+       }
+    }
+  if (filesize == off_t(-1))
+    ZYPP_THROW(Exception("Parse Error"));
+  if (blksize <= 0 || (blksize & (blksize - 1)) != 0)
+    ZYPP_THROW(Exception("Parse Error: illegal block size"));
+  bl.setFilesize(filesize);
+
+  if (filesize)
+    {
+      if (csl < 3 || csl > 16 || rsl < 1 || rsl > 4 || sql < 1 || sql > 2)
+       ZYPP_THROW(Exception("Parse Error: illegal hash lengths"));
+      size_t nblks = (filesize + blksize - 1) / blksize;
+      size_t i;
+      off_t off = 0;
+      size_t size = blksize;
+      for (i = 0; i < nblks; i++)
+       {
+         if (i == nblks - 1)
+           {
+             size = filesize % blksize;
+             if (!size)
+               size = blksize;
+           }
+         size_t blkno = bl.addBlock(off, size);
+         unsigned char rp[16];
+         rp[0] = rp[1] = rp[2] = rp[3] = 0;
+         is.read((char *)rp + 4 - rsl, rsl);
+         bl.setRsum(blkno, rsl, rp[0] << 24 | rp[1] << 16 | rp[2] << 8 | rp[3], blksize);
+         is.read((char *)rp, csl);
+         bl.setChecksum(blkno, "MD4", csl, rp, blksize);
+         off += size;
+       }
+    }
+  is.close();
+}
+
+std::vector<Url>
+ZsyncParser::getUrls()
+{
+  std::vector<Url> ret;
+  size_t i;
+  for (i = 0; i < urls.size(); i++)
+    ret.push_back(Url(urls[i]));
+  return ret;
+}
+
+MediaBlockList
+ZsyncParser::getBlockList()
+{
+  return bl;
+}
+
+  } // namespace media
+} // namespace zypp
+
diff --git a/zypp/media/ZsyncParser.h b/zypp/media/ZsyncParser.h
new file mode 100644 (file)
index 0000000..eada336
--- /dev/null
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/ZsyncParser.h
+ *
+*/
+#ifndef ZYPP_MEDIA_ZSYNCPARSER_H
+#define ZYPP_MEDIA_ZSYNCPARSER_H
+
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/media/MediaBlockList.h"
+#include "zypp/Url.h"
+
+namespace zypp {
+  namespace media {
+
+class ZsyncParser : private zypp::base::NonCopyable {
+public:
+  ZsyncParser();
+
+  /**
+   * parse a file consisting of zlink data
+   * \throws Exception
+   **/
+  void parse(std::string filename);
+  /**
+   * return the download urls from the parsed metalink data
+   **/
+  std::vector<Url> getUrls();
+  /**
+   * return the block list from the parsed metalink data
+   **/
+  MediaBlockList getBlockList();
+
+private:
+  off_t filesize;
+  size_t blksize;
+  int sql;
+  int rsl;
+  int csl;
+  MediaBlockList bl;
+  std::vector<std::string> urls;
+};
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_ZSYNCPARSER_H
diff --git a/zypp/media/proxyinfo/ProxyInfoImpl.h b/zypp/media/proxyinfo/ProxyInfoImpl.h
new file mode 100644 (file)
index 0000000..93e0432
--- /dev/null
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfoImpl.h
+ *
+*/
+#ifndef ZYPP_MEDIA_PROXYINFO_PROXYINFOIMPL_H
+#define ZYPP_MEDIA_PROXYINFO_PROXYINFOIMPL_H
+
+#include <string>
+#include <list>
+
+#include "zypp/Url.h"
+#include "zypp/base/String.h"
+#include "zypp/media/ProxyInfo.h"
+
+namespace zypp {
+  namespace media {
+
+    struct ProxyInfo::Impl
+    {
+      /** Ctor */
+      Impl()
+      {}
+
+      /** Dtor */
+      virtual ~Impl()
+      {}
+
+    public:
+      /**  */
+      virtual bool enabled() const = 0;
+      /**  */
+      virtual std::string proxy(const Url & url_r) const = 0;
+      /**  */
+      virtual ProxyInfo::NoProxyList noProxy() const = 0;
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyBegin() const = 0;
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyEnd() const = 0;
+
+      /** Return \c true if  \ref enabled and \a url_r does not match \ref noProxy. */
+      bool useProxyFor( const Url & url_r ) const
+      {
+        if ( ! enabled() && proxy( url_r ).empty() )
+          return false;
+
+        ProxyInfo::NoProxyList noproxy( noProxy() );
+        if ( noproxy.size() == 1 && noproxy.front() == "*" )
+          return false; // just an asterisk disables all.
+
+        // No proxy: Either an exact match, or the previous character
+        // is a '.', so host is within the same domain.
+        // A leading '.' in the pattern is ignored. Some implementations
+        // need '.foo.ba' to prevent 'foo.ba' from matching 'xfoo.ba'.
+        std::string host( str::toLower( url_r.getHost() ) );
+        for_( it, noproxy.begin(), noproxy.end() )
+        {
+          std::string pattern( str::toLower( (*it)[0] == '.' ? it->c_str() + 1 : it->c_str() ) );
+          if ( str::hasSuffix( host, pattern )
+               && ( host.size() == pattern.size()
+                    || host[host.size()-pattern.size()-1] == '.' ) )
+            return false;
+        }
+        return true;
+      }
+
+    public:
+      /** Default Impl: empty sets. */
+      static shared_ptr<Impl> _nullimpl;
+    };
+
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_PROXYINFO_PROXYINFOIMPL_H
diff --git a/zypp/media/proxyinfo/ProxyInfoLibproxy.cc b/zypp/media/proxyinfo/ProxyInfoLibproxy.cc
new file mode 100644 (file)
index 0000000..9baaa22
--- /dev/null
@@ -0,0 +1,88 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfoLibproxy.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/media/proxyinfo/ProxyInfoLibproxy.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+    ProxyInfoLibproxy::ProxyInfoLibproxy()
+    : ProxyInfo::Impl()
+    {
+      _factory = px_proxy_factory_new();
+      _enabled = !(_factory == NULL);
+    }
+
+    ProxyInfoLibproxy::~ProxyInfoLibproxy()
+    {
+      if (_enabled) {
+       px_proxy_factory_free(_factory);
+       _factory = NULL;
+       _enabled = false;
+      }
+    }
+
+    std::string ProxyInfoLibproxy::proxy(const Url & url_r) const
+    {
+      if (!_enabled)
+       return "";
+
+      const url::ViewOption vopt =
+             url::ViewOption::WITH_SCHEME
+             + url::ViewOption::WITH_HOST
+             + url::ViewOption::WITH_PORT
+             + url::ViewOption::WITH_PATH_NAME;
+
+      char **proxies = px_proxy_factory_get_proxies(_factory,
+                                                   (char *)url_r.asString(vopt).c_str());
+      if (!proxies)
+             return "";
+
+      /* cURL can only handle HTTP proxies, not SOCKS. And can only handle
+        one. So look through the list and find an appropriate one. */
+      char *result = NULL;
+
+      for (int i = 0; proxies[i]; i++) {
+             if (!result &&
+                 !strncmp(proxies[i], "http://", 7))
+                     result = proxies[i];
+             else
+                     free(proxies[i]);
+      }
+      free(proxies);
+
+      if (!result)
+             return "";
+
+      std::string sresult = result;
+      free(result);
+      return sresult;
+    }
+
+    ProxyInfo::NoProxyIterator ProxyInfoLibproxy::noProxyBegin() const
+    { return _no_proxy.begin(); }
+
+    ProxyInfo::NoProxyIterator ProxyInfoLibproxy::noProxyEnd() const
+    { return _no_proxy.end(); }
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/proxyinfo/ProxyInfoLibproxy.h b/zypp/media/proxyinfo/ProxyInfoLibproxy.h
new file mode 100644 (file)
index 0000000..b181064
--- /dev/null
@@ -0,0 +1,57 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfoLibproxy.h
+ *
+*/
+#ifndef ZYPP_MEDIA_PROXYINFO_PROXYINFOLIBPROXY_H
+#define ZYPP_MEDIA_PROXYINFO_PROXYINFOLIBPROXY_H
+
+#include <string>
+#include <map>
+
+#include <proxy.h>
+
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/media/ProxyInfo.h"
+#include "zypp/media/proxyinfo/ProxyInfoImpl.h"
+
+namespace zypp {
+  namespace media {
+
+
+    class ProxyInfoLibproxy : public ProxyInfo::Impl
+    {
+    public:
+      ProxyInfoLibproxy();
+      /**  */
+      ~ProxyInfoLibproxy();
+      /**  */
+      bool enabled() const
+      { return _enabled; }
+      /**  */
+      std::string proxy(const Url & url_r) const;
+      /**  */
+      ProxyInfo::NoProxyList noProxy() const
+      { return _no_proxy; }
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyBegin() const;
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyEnd() const;
+    private:
+      DefaultIntegral<bool,false> _enabled;
+      ProxyInfo::NoProxyList _no_proxy;
+      pxProxyFactory *_factory;
+    };
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_PROXYINFO_PROXYINFOLIBPROXY_H
diff --git a/zypp/media/proxyinfo/ProxyInfoSysconfig.cc b/zypp/media/proxyinfo/ProxyInfoSysconfig.cc
new file mode 100644 (file)
index 0000000..0af3daf
--- /dev/null
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfoSysconfig.cc
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/media/proxyinfo/ProxyInfoSysconfig.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+  namespace media {
+
+    ProxyInfoSysconfig::ProxyInfoSysconfig(const Pathname & path)
+    : ProxyInfo::Impl()
+    {
+      map<string,string> data = sysconfig::read(
+       path.relative()
+         ? "/etc/sysconfig" + path
+         : path);
+      map<string,string>::const_iterator it = data.find("PROXY_ENABLED");
+      if (it != data.end())
+       _enabled = it->second != "no";
+      it = data.find("HTTP_PROXY");
+      if (it != data.end())
+       _proxies["http"] = it->second;
+      it = data.find("HTTPS_PROXY");
+      if (it != data.end())
+       _proxies["https"] = it->second;
+      it = data.find("FTP_PROXY");
+      if (it != data.end())
+       _proxies["ftp"] = it->second;
+      it = data.find("NO_PROXY");
+      if (it != data.end())
+       str::split(it->second, std::back_inserter(_no_proxy), ", \t");
+    }
+
+    std::string ProxyInfoSysconfig::proxy(const Url & url_r) const
+    { 
+      map<string,string>::const_iterator it = _proxies.find(url_r.getScheme());
+      if (it != _proxies.end())
+       return it->second;
+      return "";
+    }
+
+    ProxyInfo::NoProxyIterator ProxyInfoSysconfig::noProxyBegin() const
+    { return _no_proxy.begin(); }
+
+    ProxyInfo::NoProxyIterator ProxyInfoSysconfig::noProxyEnd() const
+    { return _no_proxy.end(); }
+
+  } // namespace media
+} // namespace zypp
diff --git a/zypp/media/proxyinfo/ProxyInfoSysconfig.h b/zypp/media/proxyinfo/ProxyInfoSysconfig.h
new file mode 100644 (file)
index 0000000..cb9577b
--- /dev/null
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfoSysconfig.h
+ *
+*/
+#ifndef ZYPP_MEDIA_PROXYINFO_PROXYINFOSYSCONFIG_H
+#define ZYPP_MEDIA_PROXYINFO_PROXYINFOSYSCONFIG_H
+
+#include <string>
+#include <map>
+
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/media/ProxyInfo.h"
+#include "zypp/media/proxyinfo/ProxyInfoImpl.h"
+
+namespace zypp {
+  namespace media {
+
+
+    class ProxyInfoSysconfig : public ProxyInfo::Impl
+    {
+    public:
+      ProxyInfoSysconfig(const Pathname & path);
+      /**  */
+      bool enabled() const
+      { return _enabled; }
+      /**  */
+      std::string proxy(const Url & url_r) const;
+      /**  */
+      ProxyInfo::NoProxyList noProxy() const
+      { return _no_proxy; }
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyBegin() const;
+      /**  */
+      virtual ProxyInfo::NoProxyIterator noProxyEnd() const;
+    private:
+      DefaultIntegral<bool,false> _enabled;
+      ProxyInfo::NoProxyList _no_proxy;
+      std::map<std::string,std::string> _proxies;
+    };
+
+///////////////////////////////////////////////////////////////////
+
+  } // namespace media
+} // namespace zypp
+
+#endif // ZYPP_MEDIA_PROXYINFO_PROXYINFOSYSCONFIG_H
diff --git a/zypp/media/proxyinfo/ProxyInfos.h b/zypp/media/proxyinfo/ProxyInfos.h
new file mode 100644 (file)
index 0000000..408b9f3
--- /dev/null
@@ -0,0 +1,23 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/proxyinfo/ProxyInfos.h
+ *
+*/
+#ifndef ZYPP_MEDIA_PROXYINFO_PROXYINFOS_H
+#define ZYPP_MEDIA_PROXYINFO_PROXYINFOS_H
+
+#include <string>
+#include <list>
+
+#include "zypp/media/proxyinfo/ProxyInfoSysconfig.h"
+#ifdef _WITH_LIBPROXY_SUPPORT_
+#include "zypp/media/proxyinfo/ProxyInfoLibproxy.h"
+#endif
+
+#endif // ZYPP_MEDIA_PROXYINFO_PROXYINFOS_H
diff --git a/zypp/misc/CheckAccessDeleted.cc b/zypp/misc/CheckAccessDeleted.cc
new file mode 100644 (file)
index 0000000..514f368
--- /dev/null
@@ -0,0 +1,298 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/misc/CheckAccessDeleted.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/misc/CheckAccessDeleted.h"
+
+using std::endl;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+    //
+    // lsof output lines are a sequence of NUL terminated fields,
+    // where the 1st char determines the fiels type.
+    //
+    // (pcuL) pid command userid loginname
+    // (ftkn).filedescriptor type linkcount filename
+    //
+    /////////////////////////////////////////////////////////////////
+    /** Add \c cache to \c data if the process is accessing deleted files.
+     * \c pid string in \c cache is the proc line \c (pcuLR), \c iles
+     * are lready in place. Always clear the \c cache.files!
+    */
+    inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, CheckAccessDeleted::ProcInfo & cache_r )
+    {
+      if ( cache_r.files.empty() )
+        return;
+
+      // at least one file access so keep it:
+      data_r.push_back( CheckAccessDeleted::ProcInfo() );
+      CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
+
+      std::string pline;
+      cache_r.pid.swap( pline );
+      cache_r.files.swap( pinfo.files ); // clears cache.files
+
+      for_( ch, pline.begin(), pline.end() )
+      {
+        switch ( *ch )
+        {
+          case 'p':
+            pinfo.pid = &*(ch+1);
+            break;
+          case 'R':
+            pinfo.ppid = &*(ch+1);
+            break;
+          case 'u':
+            pinfo.puid = &*(ch+1);
+            break;
+          case 'L':
+            pinfo.login = &*(ch+1);
+            break;
+          case 'c':
+            pinfo.command = &*(ch+1);
+            break;
+        }
+        if ( *ch == '\n' ) break;              // end of data
+        do { ++ch; } while ( *ch != '\0' );    // skip to next field
+      }
+
+      if ( pinfo.command.size() == 15 )
+      {
+        // the command name might be truncated, so we check against /proc/<pid>/exe
+        Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
+        if ( ! command.empty() )
+          pinfo.command = command.basename();
+      }
+
+      //MIL << " Take " << pinfo << endl;
+    }
+
+    /** Add line to cache if it refers to a deleted executable or library file:
+     * - Either the link count \c(k) is \c 0, or no link cout is present.
+     * - The type \c (t) is set to \c REG or \c DEL
+     * - The filedescriptor \c (f) is set to \c txt, \c mem or \c DEL
+    */
+    inline void addCacheIf( CheckAccessDeleted::ProcInfo & cache_r, const std::string & line_r, bool verbose_r  )
+    {
+      const char * f = 0;
+      const char * t = 0;
+      const char * n = 0;
+
+      for_( ch, line_r.c_str(), ch+line_r.size() )
+      {
+        switch ( *ch )
+        {
+          case 'k':
+            if ( *(ch+1) != '0' )      // skip non-zero link counts
+              return;
+            break;
+          case 'f':
+            f = ch+1;
+            break;
+          case 't':
+            t = ch+1;
+            break;
+          case 'n':
+            n = ch+1;
+            break;
+        }
+        if ( *ch == '\n' ) break;              // end of data
+        do { ++ch; } while ( *ch != '\0' );    // skip to next field
+      }
+
+      if ( !t || !f || !n )
+        return;        // wrong filedescriptor/type/name
+
+      if ( !(    ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
+              || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
+        return;        // wrong type
+
+      if ( !(    ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
+              || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
+              || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
+              || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
+        return;        // wrong filedescriptor type
+
+      if ( str::contains( n, "(stat: Permission denied)" ) )
+        return;        // Avoid reporting false positive due to insufficient permission.
+
+      if ( ! verbose_r )
+      {
+        if ( ! ( str::contains( n, "/lib" ) || str::contains( n, "bin/" ) ) )
+          return; // Try to avoid reporting false positive unless verbose.
+      }
+
+      if ( *f == 'm' || *f == 'D' )    // skip some wellknown nonlibrary memorymapped files
+      {
+        static const char * black[] = {
+            "/SYSV"
+          , "/var/run/"
+          , "/dev/"
+        };
+        for_( it, arrayBegin( black ), arrayEnd( black ) )
+        {
+          if ( str::hasPrefix( n, *it ) )
+            return;
+        }
+      }
+
+      if ( std::find( cache_r.files.begin(), cache_r.files.end(), n ) == cache_r.files.end() )
+      {
+        // Add if no duplicate
+        cache_r.files.push_back( n );
+      }
+    }
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  CheckAccessDeleted::size_type CheckAccessDeleted::check( bool verbose_r )
+  {
+    _data.clear();
+    std::vector<ProcInfo> data;
+
+    static const char* argv[] =
+    {
+      "lsof", "-n", "-FpcuLRftkn0", NULL
+    };
+    ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
+
+    CheckAccessDeleted::ProcInfo cache;
+    for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+    {
+      if ( line[0] == 'p' )
+      {
+        addDataIf( data, cache );
+        cache.pid = line; //
+      }
+      else
+      {
+        addCacheIf( cache, line, verbose_r );
+      }
+    }
+    addDataIf( data, cache );
+
+    int ret = prog.close();
+    if ( ret != 0 )
+    {
+      Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
+      err.remember( prog.execError() );
+      ZYPP_THROW( err );
+    }
+
+    _data.swap( data );
+    return _data.size();
+  }
+
+  std::string CheckAccessDeleted::findService( const Pathname & command_r )
+  {
+    ProcInfo p;
+    p.command = command_r.basename();
+    return p.service();
+  }
+  std::string CheckAccessDeleted::findService( const char * command_r )
+  { return findService( Pathname( command_r ) ); }
+
+  std::string CheckAccessDeleted::findService( const std::string & command_r )
+  { return findService( Pathname( command_r ) ); }
+
+  std::string CheckAccessDeleted::findService( pid_t pid_r )
+  { return findService( filesystem::readlink( Pathname("/proc")/str::numstring(pid_r)/"exe" ) ); }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  std::string CheckAccessDeleted::ProcInfo::service() const
+  {
+    if ( command.empty() )
+      return std::string();
+    // TODO: This needs to be implemented smarter... be carefull
+    // as we don't know whether the target is up.
+
+    static const Pathname initD( "/etc/init.d" );
+    { // init.d script with same name
+      PathInfo pi( initD/command );
+      if ( pi.isFile() && pi.isX() )
+        return command;
+    }
+    { // init.d script with name + 'd'
+      std::string alt( command+"d" );
+      PathInfo pi( initD/alt );
+      if ( pi.isFile() && pi.isX() )
+        return alt;
+    }
+    if ( *command.rbegin() == 'd' )
+    { // init.d script with name - trailing'd'
+      std::string alt( command );
+      alt.erase( alt.size()-1 );
+      PathInfo pi( initD/alt );
+      WAR <<pi << endl;
+      if ( pi.isFile() && pi.isX() )
+        return alt;
+    }
+    return std::string();
+  }
+
+  /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
+  {
+    return dumpRange( str << "CheckAccessDeleted ",
+                      obj.begin(),
+                      obj.end() );
+  }
+
+   /******************************************************************
+  **
+  **   FUNCTION NAME : operator<<
+  **   FUNCTION TYPE : std::ostream &
+  */
+  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
+  {
+    if ( obj.pid.empty() )
+      return str << "<NoProc>";
+
+    return dumpRangeLine( str << obj.command
+                              << '<' << obj.pid
+                              << '|' << obj.ppid
+                              << '|' << obj.puid
+                              << '|' << obj.login
+                              << '>',
+                          obj.files.begin(),
+                          obj.files.end() );
+  }
+
+ /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/misc/CheckAccessDeleted.h b/zypp/misc/CheckAccessDeleted.h
new file mode 100644 (file)
index 0000000..92c0190
--- /dev/null
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/misc/CheckAccessDeleted.h
+ *
+*/
+#ifndef ZYPP_MISC_CHECKACCESSDELETED_H
+#define ZYPP_MISC_CHECKACCESSDELETED_H
+
+#include <iosfwd>
+#include <vector>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /**
+   * Check for running processes which access deleted executables or libraries.
+   *
+   * Executed after commit, this gives a hint which processes/services
+   * need to be restarted.
+   *
+   * Per default upon construtcion or explicit call to \ref check,
+   * information about running processes which access deleted files
+   * or libraries is collected and provided as a \ref ProcInfo
+   * container.
+   */
+  class CheckAccessDeleted
+  {
+
+    public:
+      /**
+       * Data about one running process accessing deleted files.
+       */
+      struct ProcInfo
+      {
+        std::string pid;               //!< process ID
+        std::string ppid;              //!< parent process ID
+        std::string puid;              //!< process user ID
+        std::string login;             //!< process login name
+        std::string command;           //!< process command name
+        std::vector<std::string> files;        //!< list of deleted executables or libraries accessed
+
+        /** Guess if command was started by an \c /etc/init.d/ script.
+         * The name of an \c /etc/init.d/ script that might be used to restart the
+         * command.
+         * \warning This is just a guess.
+        */
+        std::string service() const;
+      };
+
+      typedef size_t                                           size_type;
+      typedef ProcInfo                                 value_type;
+      typedef std::vector<ProcInfo>::const_iterator    const_iterator;
+
+    public:
+      /** Default ctor performs check immediately.
+       * Pass \c false and the initial check is omitted.
+       * \throws Exception if \ref check throws.
+       * \see \ref check.
+       */
+      CheckAccessDeleted( bool doCheck_r = true )
+      { if ( doCheck_r ) check(); }
+
+    public:
+      /** Check for running processes which access deleted executables or libraries.
+       *
+       * Per default \ref check will try guess and collect executables and
+       * libraries only by looking at the files path and name. (e.g named
+       * \c lib* or located in \c *bin/).
+       *
+       * A verbose check will omit this test and collect all processes uning
+       * any deleted file.
+       *
+       * \return the number of processes found.
+       * \throws Exception On error collecting the data (e.g. no lsof installed)
+       */
+      size_type check( bool verbose_r = false );
+
+      bool empty() const               { return _data.empty(); }
+      size_type size() const           { return _data.size(); }
+      const_iterator begin() const     { return _data.begin(); }
+      const_iterator end() const       { return _data.end(); }
+
+    public:
+      /** Guess if \c command was started by an \c /etc/init.d/ script.
+       * The name of an \c /etc/init.d/ script that might be used to restart the
+       * command. \c command may be specifies by name, full path or pid.
+       * \warning This is just a guess.
+       */
+      static std::string findService( const char * command_r );
+      /** \overload Taking a string.*/
+      static std::string findService( const std::string & command_r );
+      /** \overload Taking a pathname. */
+      static std::string findService( const Pathname & command_r );
+      /** \overload taking the pid. */
+      static std::string findService( pid_t pid_r );
+
+    private:
+      std::vector<ProcInfo> _data;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates CheckAccessDeleted Stream output */
+  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj );
+
+  /** \relates CheckAccessDeleted::ProcInfo Stream output */
+  std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MISC_CHECKACCESSDELETED_H
diff --git a/zypp/misc/DefaultLoadSystem.cc b/zypp/misc/DefaultLoadSystem.cc
new file mode 100644 (file)
index 0000000..abb24fb
--- /dev/null
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/misc/DefaultLoadSystem.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/misc/DefaultLoadSystem.h"
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/zypp_detail/ZYppReadOnlyHack.h"
+#include "zypp/Target.h"
+#include "zypp/RepoManager.h"
+#include "zypp/sat/Pool.h"
+
+using std::endl;
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace misc
+  { /////////////////////////////////////////////////////////////////
+
+    void defaultLoadSystem( const Pathname & sysRoot_r, LoadSystemFlags flags_r )
+    {
+      MIL << str::form( "*** Load system at '%s' (%lx)", sysRoot_r.c_str(), (unsigned long)flags_r ) << endl;
+
+      if ( ! PathInfo( sysRoot_r ).isDir() )
+        ZYPP_THROW( Exception(str::form("sysRoot_r argument needs to be a directory. (%s)", sysRoot_r.c_str())) );
+
+      if ( ZYppFactory::instance().haveZYpp() )
+        ZYPP_THROW( Exception("ZYpp instance is already created. (Call this method earlier.)") );
+
+      if ( flags_r.testFlag( LS_READONLY ) )
+        zypp_readonly_hack::IWantIt ();
+
+      sat::Pool satpool( sat::Pool::instance() );
+
+      if ( 1 )
+      {
+        MIL << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
+        getZYpp()->initializeTarget( sysRoot_r );
+        getZYpp()->target()->load();
+        MIL << satpool.systemRepo() << endl;
+      }
+
+      if ( 1 )
+      {
+        RepoManager repoManager( sysRoot_r );
+        RepoInfoList repos = repoManager.knownRepositories();
+        for_( it, repos.begin(), repos.end() )
+        {
+          RepoInfo & nrepo( *it );
+
+          if ( ! nrepo.enabled() )
+            continue;
+
+          if ( ! flags_r.testFlag( LS_NOREFRESH ) )
+          {
+            if ( repoManager.isCached( nrepo )
+               && ( nrepo.type() == repo::RepoType::RPMPLAINDIR // refreshes always
+                  || repoManager.checkIfToRefreshMetadata( nrepo, nrepo.url() ) == RepoManager::REFRESH_NEEDED ) )
+            {
+              MIL << str::form( "*** clean cache for repo '%s'\t", nrepo.name().c_str() ) << endl;
+              repoManager.cleanCache( nrepo );
+              MIL << str::form( "*** refresh repo '%s'\t", nrepo.name().c_str() ) << endl;
+              repoManager.refreshMetadata( nrepo );
+            }
+          }
+
+          if ( ! repoManager.isCached( nrepo ) )
+          {
+            MIL << str::form( "*** build cache for repo '%s'\t", nrepo.name().c_str() ) << endl;
+            repoManager.buildCache( nrepo );
+          }
+
+          MIL << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << std::flush;
+          try
+          {
+            repoManager.loadFromCache( nrepo );
+            MIL << satpool.reposFind( nrepo.alias() ) << endl;
+          }
+          catch ( const Exception & exp )
+          {
+            ERR << "*** load repo failed: " << exp.asString() + "\n" + exp.historyAsString() << endl;
+            ZYPP_RETHROW ( exp );
+          }
+        }
+      }
+      MIL << str::form( "*** Read system at '%s'", sysRoot_r.c_str() ) << endl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace misc
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/misc/DefaultLoadSystem.h b/zypp/misc/DefaultLoadSystem.h
new file mode 100644 (file)
index 0000000..3b8b1aa
--- /dev/null
@@ -0,0 +1,63 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/misc/DefaultLoadSystem.h
+ *
+*/
+#ifndef ZYPP_MISC_DEFAULTLOADSYSTEM_H
+#define ZYPP_MISC_DEFAULTLOADSYSTEM_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/base/Flags.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace misc
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+     * Bits for tuning \ref defaultLoadSystem.
+     *
+     * Use \ref LoadSystemFlags as a type-safe way of
+     * storing OR-combinations.
+     */
+    enum LoadSystemFlag
+    {
+      LS_READONLY      = (1 << 0),     //!< // Create readonly ZYpp instance.
+      LS_NOREFRESH     = (1 << 1)      //!< // Don't refresh existing repos.
+    };
+
+    /** \relates LoadSystemFlag Type-safe way of storing OR-combinations. */
+    ZYPP_DECLARE_FLAGS_AND_OPERATORS( LoadSystemFlags, LoadSystemFlag );
+
+    /**
+     * Create the ZYpp instance and load target and enabled repositories.
+     *
+     * \see LoadSystemFlag for options.
+     *
+     * \throws Exception on error
+     *
+     * \todo properly handle service refreshs
+     */
+    void defaultLoadSystem( const Pathname & sysRoot_r = "/", LoadSystemFlags flags_r = LoadSystemFlags() );
+
+    /** \overload */
+    inline void defaultLoadSystem( LoadSystemFlags flags_r )
+    { defaultLoadSystem( "/", flags_r ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace misc
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_MISC_DEFAULTLOADSYSTEM_H
diff --git a/zypp/parser/HistoryLogReader.cc b/zypp/parser/HistoryLogReader.cc
new file mode 100644 (file)
index 0000000..53b284f
--- /dev/null
@@ -0,0 +1,280 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+/** \file HistoryLogReader.cc
+ *
+ */
+#include <iostream>
+
+#include "zypp/base/InputStream.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/Logger.h"
+#include "zypp/parser/ParseException.h"
+
+#include "zypp/parser/HistoryLogReader.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryLogReader::Impl
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  struct HistoryLogReader::Impl
+  {
+    Impl( const Pathname & historyFile, const ProcessItem & callback );
+    ~Impl()
+    {}
+
+    HistoryItem::Ptr createHistoryItem(HistoryItem::FieldVector & fields);
+    void parseLine(const string & line, unsigned int lineNr);
+
+    void readAll(const ProgressData::ReceiverFnc & progress);
+    void readFrom(const Date & date,
+        const ProgressData::ReceiverFnc & progress);
+    void readFromTo(
+        const Date & fromDate, const Date & toDate,
+        const ProgressData::ReceiverFnc & progress);
+
+    Pathname _filename;
+    ProcessItem _callback;
+    bool _ignoreInvalid;
+  };
+
+  HistoryLogReader::Impl::Impl( const Pathname & historyFile,
+                                const ProcessItem & callback )
+    : _filename(historyFile), _callback(callback), _ignoreInvalid(false)
+  {}
+
+  HistoryItem::Ptr
+  HistoryLogReader::Impl::createHistoryItem(HistoryItem::FieldVector & fields)
+  {
+    HistoryActionID aid(str::trim(fields[1]));
+    switch (aid.toEnum())
+    {
+    case HistoryActionID::INSTALL_e:
+        return HistoryItemInstall::Ptr(new HistoryItemInstall(fields));
+      break;
+
+    case HistoryActionID::REMOVE_e:
+      return HistoryItemRemove::Ptr(new HistoryItemRemove(fields));
+      break;
+
+    case HistoryActionID::REPO_ADD_e:
+      return HistoryItemRepoAdd::Ptr(new HistoryItemRepoAdd(fields));
+      break;
+
+    case HistoryActionID::REPO_REMOVE_e:
+      return HistoryItemRepoRemove::Ptr(new HistoryItemRepoRemove(fields));
+      break;
+
+    case HistoryActionID::REPO_CHANGE_ALIAS_e:
+      return HistoryItemRepoAliasChange::Ptr(new HistoryItemRepoAliasChange(fields));
+      break;
+
+    case HistoryActionID::REPO_CHANGE_URL_e:
+      return HistoryItemRepoUrlChange::Ptr(new HistoryItemRepoUrlChange(fields));
+      break;
+
+    default:
+      WAR << "Unknown history log action type: " << fields[1] << endl;
+    }
+
+    return HistoryItem::Ptr();
+  }
+
+  void HistoryLogReader::Impl::parseLine(const string & line, unsigned int lineNr)
+  {
+    HistoryItem::FieldVector fields;
+    HistoryItem::Ptr item_ptr;
+
+    // parse into fields
+    str::splitEscaped(line, back_inserter(fields), "|", true);
+
+    if (fields.size() <= 2)
+    {
+      ParseException
+        e(str::form("Error in history log on line #%u.", lineNr));
+      e.addHistory(
+          str::form("Bad number of fields. Got %zd, expected more than %d.",
+              fields.size(), 2));
+      ZYPP_THROW(e);
+    }
+
+    try
+    {
+      item_ptr = createHistoryItem(fields);
+    }
+    catch (const Exception & e)
+    {
+      ZYPP_CAUGHT(e);
+      ERR << "Invalid history log entry on line #" << lineNr << ":" << endl
+          << line << endl;
+
+      if (!_ignoreInvalid)
+      {
+        ParseException newe(
+            str::form("Error in history log on line #%u.", lineNr ) );
+        newe.remember(e);
+        ZYPP_THROW(newe);
+      }
+    }
+
+    if (item_ptr)
+      _callback(item_ptr);
+    else if (!_ignoreInvalid)
+    {
+      ParseException
+        e(str::form("Error in history log on line #%u.", lineNr));
+      e.addHistory("Unknown entry type.");
+      ZYPP_THROW(e);
+    }
+  }
+
+  void HistoryLogReader::Impl::readAll(const ProgressData::ReceiverFnc & progress)
+  {
+    InputStream is(_filename);
+    iostr::EachLine line(is);
+
+    ProgressData pd;
+    pd.sendTo( progress );
+    pd.toMin();
+
+    for (; line; line.next(), pd.tick() )
+    {
+      // ignore comments
+      if ((*line)[0] == '#')
+        continue;
+
+      parseLine(*line, line.lineNo());
+    }
+
+    pd.toMax();
+  }
+
+  void HistoryLogReader::Impl::readFrom(const Date & date,
+      const ProgressData::ReceiverFnc & progress)
+  {
+    InputStream is(_filename);
+    iostr::EachLine line(is);
+
+    ProgressData pd;
+    pd.sendTo( progress );
+    pd.toMin();
+
+    bool pastDate = false;
+    for (; line; line.next(), pd.tick())
+    {
+      const string & s = *line;
+
+      // ignore comments
+      if (s[0] == '#')
+        continue;
+
+      if (pastDate)
+        parseLine(s, line.lineNo());
+      else
+      {
+        Date logDate(s.substr(0, s.find('|')), HISTORY_LOG_DATE_FORMAT);
+        if (logDate > date)
+        {
+          pastDate = true;
+          parseLine(s, line.lineNo());
+        }
+      }
+    }
+
+    pd.toMax();
+  }
+
+  void HistoryLogReader::Impl::readFromTo(
+      const Date & fromDate, const Date & toDate,
+      const ProgressData::ReceiverFnc & progress)
+  {
+    InputStream is(_filename);
+    iostr::EachLine line(is);
+
+    ProgressData pd;
+    pd.sendTo(progress);
+    pd.toMin();
+
+    bool pastFromDate = false;
+    for (; line; line.next(), pd.tick())
+    {
+      const string & s = *line;
+
+      // ignore comments
+      if (s[0] == '#')
+        continue;
+
+      Date logDate(s.substr(0, s.find('|')), HISTORY_LOG_DATE_FORMAT);
+
+      // past toDate - stop reading
+      if (logDate >= toDate)
+        break;
+
+      // past fromDate - start reading
+      if (!pastFromDate && logDate > fromDate)
+        pastFromDate = true;
+
+      if (pastFromDate)
+        parseLine(s, line.lineNo());
+    }
+
+    pd.toMax();
+  }
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryLogReader
+  //
+  /////////////////////////////////////////////////////////////////////
+
+  HistoryLogReader::HistoryLogReader( const Pathname & historyFile,
+                                      const ProcessItem & callback )
+    : _pimpl(new HistoryLogReader::Impl(historyFile, callback))
+  {}
+
+  HistoryLogReader::~HistoryLogReader()
+  {}
+
+  void HistoryLogReader::setIgnoreInvalidItems(bool ignoreInvalid)
+  { _pimpl->_ignoreInvalid = ignoreInvalid; }
+
+  bool HistoryLogReader::ignoreInvalidItems() const
+  { return _pimpl->_ignoreInvalid; }
+
+  void HistoryLogReader::readAll(const ProgressData::ReceiverFnc & progress)
+  { _pimpl->readAll(progress); }
+
+  void HistoryLogReader::readFrom(const Date & date,
+      const ProgressData::ReceiverFnc & progress)
+  { _pimpl->readFrom(date, progress); }
+
+  void HistoryLogReader::readFromTo(
+      const Date & fromDate, const Date & toDate,
+      const ProgressData::ReceiverFnc & progress)
+  { _pimpl->readFromTo(fromDate, toDate, progress); }
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/HistoryLogReader.h b/zypp/parser/HistoryLogReader.h
new file mode 100644 (file)
index 0000000..e31eb91
--- /dev/null
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+/** \file zypp/parser/HistoryLogReader.h
+ *
+ */
+#ifndef ZYPP_PARSER_HISTORYLOGREADER_H_
+#define ZYPP_PARSER_HISTORYLOGREADER_H_
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ProgressData.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/HistoryLogData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class Date;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME: HistoryLogReader
+  //
+  /**
+   * Reads a zypp history log file and calls the ProcessItem function passed
+   * in the constructor for each item read.
+   *
+   * Example:
+   * <code>
+   *
+   * struct HistoryItemCollector
+   * {
+   *   vector<HistoryItem::Ptr> items;
+   *
+   *   bool operator()( const HistoryItem::Ptr & item_ptr )
+   *   {
+   *     items.push_back(item_ptr);
+   *     return true;
+   *   }
+   * }
+   *
+   * ...
+   *
+   * HistoryItemCollector ic;
+   * HistoryLogReader reader("/var/log/zypp/history", boost::ref(ic));
+   *
+   * try
+   * {
+   *   reader.readAll();
+   * }
+   * catch (const Exception & e)
+   * {
+   *   cout << e.asUserHistory() << endl;
+   * }
+   *
+   * </code>
+   *
+   * \see http://en.opensuse.org/Libzypp/Package_History
+   */
+  class HistoryLogReader
+  {
+  public:
+    typedef function< bool( const HistoryItem::Ptr & )> ProcessItem;
+
+  public:
+    HistoryLogReader( const Pathname & repo_file,
+                      const ProcessItem & callback );
+    ~HistoryLogReader();
+
+    /**
+     * Read the whole log file.
+     *
+     * \param progress An optional progress data receiver function.
+     */
+    void readAll(
+      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+
+    /**
+     * Read log from specified \a date.
+     *
+     * \param date     Date from which to read.
+     * \param progress An optional progress data receiver function.
+     *
+     * \see readFromTo()
+     */
+    void readFrom( const Date & date,
+      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+
+    /**
+     * Read log between \a fromDate and \a toDate.
+     *
+     * The date comparison's precision goes to seconds. Omitted time parts
+     * get replaced by zeroes, so if e.g. the time is not specified at all, the
+     * date means midnight of the specified date. So
+     *
+     * <code>
+     * fromDate = Date("2009-01-01", "%Y-%m-%d");
+     * toDate   = Date("2009-01-02", "%Y-%m-%d");
+     * </code>
+     *
+     * will yield log entries from midnight of January, 1st untill
+     * one second before midnight of January, 2nd.
+     *
+     * \param fromDate Date from which to read.
+     * \param toDate   Date on which to stop reading.
+     * \param progress An optional progress data receiver function.
+     */
+    void readFromTo( const Date & fromDate, const Date & toDate,
+      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+
+    /**
+     * Set the reader to ignore invalid log entries and continue with the rest.
+     *
+     * \param ignoreInvalid <tt>true</tt> will cause the reader to ignore invalid entries
+     */
+    void setIgnoreInvalidItems( bool ignoreInvalid = false );
+
+    /**
+     * Whether the reader is set to ignore invalid log entries.
+     *
+     * \see setIngoreInvalidItems()
+     */
+    bool ignoreInvalidItems() const;
+
+  private:
+    /** Implementation */
+    class Impl;
+    RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+
+  /////////////////////////////////////////////////////////////////
+} // namespace parser
+///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_PARSER_HISTORYLOGREADER_H_ */
diff --git a/zypp/parser/IniDict.cc b/zypp/parser/IniDict.cc
new file mode 100644 (file)
index 0000000..a68e02a
--- /dev/null
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/IniDict.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include <map>
+#include <string>
+#include "zypp/parser/IniDict.h"
+
+using namespace std;
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : IniDict
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : IniDict::IniDict
+    // METHOD TYPE : Ctor
+    //
+    IniDict::IniDict( const InputStream &is,
+                      const ProgressData::ReceiverFnc & progress )
+    {
+      read(is, progress );
+    }
+
+    IniDict::IniDict()
+    {
+    }
+
+    void IniDict::read( const InputStream &is,
+                        const ProgressData::ReceiverFnc & progress )
+    {
+      parse(is, progress );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : IniDict::~IniDict
+    // METHOD TYPE : Dtor
+    //
+    IniDict::~IniDict()
+    {}
+
+    void IniDict::consume( const std::string &section )
+    {
+      _dict[section]; // remember even empty sections
+    }
+
+    void IniDict::consume( const std::string &section, const std::string &key, const std::string &value )
+    {
+      _dict[section][key] = value;
+    }
+
+
+    IniDict::entry_const_iterator IniDict::entriesBegin(const std::string &section) const
+    {
+      SectionSet::const_iterator secit = _dict.find(section);
+      if ( secit == _dict.end() )
+      {
+        return _empty_map.begin();
+      }
+
+      return (secit->second).begin();
+    }
+
+    IniDict::entry_const_iterator IniDict::entriesEnd(const std::string &section) const
+    {
+      SectionSet::const_iterator secit = _dict.find(section);
+      if ( secit == _dict.end() )
+      {
+        return _empty_map.end();
+      }
+
+      return (secit->second).end();
+    }
+
+
+    IniDict::section_const_iterator IniDict::sectionsBegin() const
+    {
+      return make_map_key_begin( _dict );
+    }
+
+    IniDict::section_const_iterator IniDict::sectionsEnd() const
+    {
+      return make_map_key_end( _dict );
+    }
+
+    void IniDict::insertEntry( const std::string &section,
+                               const std::string &key,
+                               const std::string &value )
+    {
+      consume( section, key, value );
+    }
+
+    void IniDict::deleteSection( const std::string &section )
+    {
+      _dict.erase(section);
+    }
+
+    bool IniDict::hasSection( const std::string &section ) const
+    {
+      SectionSet::const_iterator secit = _dict.find(section);
+      if ( secit == _dict.end() )
+        return false;
+      return true;
+    }
+
+    bool IniDict::hasEntry( const std::string &section,
+                            const std::string &entry ) const
+    {
+      SectionSet::const_iterator secit = _dict.find(section);
+      if ( secit == _dict.end() )
+        return false;
+
+      EntrySet::const_iterator entryit = (secit->second).find(entry);
+      if ( entryit == (secit->second).end() )
+        return false;
+
+      return true;
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const IniDict & obj )
+    {
+      for ( IniDict::section_const_iterator si = obj.sectionsBegin();
+            si != obj.sectionsEnd();
+            ++si )
+      {
+        str << "[" << *si << "]" << endl;
+        for ( IniDict::entry_const_iterator ei = obj.entriesBegin(*si);
+              ei != obj.entriesEnd(*si);
+              ++ei )
+        {
+          str << ei->first << " = " << ei->second << endl;
+        }
+        str << endl;
+      }
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/IniDict.h b/zypp/parser/IniDict.h
new file mode 100644 (file)
index 0000000..fe71b23
--- /dev/null
@@ -0,0 +1,169 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/IniDict.h
+ *
+*/
+#ifndef ZYPP_PARSER_INIDICT_H
+#define ZYPP_PARSER_INIDICT_H
+
+#include <iosfwd>
+#include <map>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/parser/IniParser.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : IniDict
+    //
+    /**
+     * Parses a INI file and offers its structure as a
+     * dictionary.
+     * 
+     */
+    class IniDict : public IniParser
+    {
+      friend std::ostream & operator<<( std::ostream & str, const IniDict & obj );
+    public:
+      typedef std::map<std::string, std::string> EntrySet;
+      typedef std::map<std::string, EntrySet> SectionSet;
+      typedef MapKVIteratorTraits<SectionSet>::Key_const_iterator section_const_iterator;
+      typedef EntrySet::const_iterator entry_const_iterator;
+      
+      /**
+       * \name Section Iterators
+       * Iterate trough ini file sections
+       * \code
+       * for ( IniDict::section_const_iterator it = dict.sectionsBegin(); 
+       *       it != dict.sectionsEnd();
+       *       ++it )
+       * {
+       *   MIL << (*it) << endl;
+       * }
+       * \endcode
+       */
+       //@{
+      section_const_iterator sectionsBegin() const;
+      section_const_iterator sectionsEnd() const;
+      //@}
+      
+      /**
+       * \name Entries Iterators
+       * Iterate trough ini file entries in a section
+       * \code
+       * for ( IniDict::entry_const_iterator it = dict.entriesBegin("updates"); 
+       *       it != dict.entriesEnd("updates");
+       *       ++it )
+       * {
+       *   MIL << (*it).first << endl;
+       * }
+       * \endcode
+       */
+       
+      //@{
+      entry_const_iterator entriesBegin(const std::string &section) const;
+      entry_const_iterator entriesEnd(const std::string &section) const;
+      //@{
+      
+      /**
+       * Creates a dictionary from a InputStream
+       * containing a ini structured file
+       */
+      IniDict( const InputStream &is,
+               const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+
+      /**
+       * Creates a mepty dictionary
+       */
+      IniDict();
+      
+      /** Dtor */
+      ~IniDict();
+
+      /**
+       * Fill a dictionary from a InputStream
+       * containing a ini structured file
+       */
+      void read( const InputStream &is,
+                 const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+
+      /**
+       * \short add an entry
+       * \param section
+       * \param key
+       * \param value
+       */
+      void insertEntry( const std::string &section,
+                        const std::string &key,
+                        const std::string &value );
+      
+      /**
+       * \short add an entry
+       * \param section
+       * \param key
+       * \param value
+       */
+      void deleteSection( const std::string &section );
+
+      /**
+       * \short True if there is a section with that name
+       * \param section Section Name
+       */
+      bool hasSection( const std::string &section ) const;
+
+      /**
+       * \short True if an entry exists in the section
+       * \param section Section name
+       * \param entry entry name
+       *
+       * \note If the given section does not exist, this will
+       * of course return false.
+       */
+      bool hasEntry( const std::string &section,
+                     const std::string &entry ) const;
+    public:
+
+      /** Called when a section is found. */
+      virtual void consume( const std::string &section );
+      /** Called when a key value is found. */
+      virtual void consume( const std::string &section,
+                            const std::string &key,
+                            const std::string &value );
+
+    private:
+      SectionSet _dict;
+      /**
+       * empty map used to simulate
+       * iteration in non existant
+       * sections
+       */
+      EntrySet _empty_map;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates IniDict Stream output */
+    std::ostream & operator<<( std::ostream & str, const IniDict & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_INIDICT_H
diff --git a/zypp/parser/IniParser.cc b/zypp/parser/IniParser.cc
new file mode 100644 (file)
index 0000000..f58264a
--- /dev/null
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/IniParser.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/parser/ParseException.h"
+#include "zypp/parser/IniParser.h"
+#include "zypp/ProgressData.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace parser
+{ /////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : IniParser::IniParser
+//     METHOD TYPE : Ctor
+//
+IniParser::IniParser()
+  : _line_nr(0)
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : IniParser::~IniParser
+//     METHOD TYPE : Dtor
+//
+IniParser::~IniParser()
+{}
+
+void IniParser::beginParse()
+{}
+
+void IniParser::consume( const std::string &section, const std::string &key, const std::string &value )
+{}
+
+void IniParser::consume( const std::string &section )
+{}
+
+void IniParser::endParse()
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : IniParser::parse
+//     METHOD TYPE : void
+//
+void IniParser::parse( const InputStream & input_r, const ProgressData::ReceiverFnc & progress )
+{
+  MIL << "Start parsing " << input_r << endl;
+  _inputname = input_r.name();
+  beginParse();
+
+  ProgressData ticks( makeProgressData( input_r ) );
+  ticks.sendTo(progress);
+  ticks.toMin();
+
+  iostr::EachLine line( input_r );
+  for ( ; line; line.next() )
+  {
+    std::string trimmed = str::trim(*line);
+
+    if (trimmed.empty() || trimmed[0] == ';' || trimmed[0] == '#')
+      continue ; /* Comment lines */
+
+    if (trimmed[0] == '[')
+    {
+      std::string section = trimmed.substr(1, trimmed.find(']')-1);
+      consume(section);
+      section.swap(_current_section);
+      continue;
+    }
+
+    std::string::size_type pos = trimmed.find('=');
+
+    if (pos != std::string::npos)
+    {
+      std::string key = str::rtrim(trimmed.substr(0, pos));
+      if(key.find_first_of(" \t") != std::string::npos) {
+       std::string msg = str::form("%s: Key in line %d contains whitespace", _inputname.c_str(), line.lineNo());
+       ZYPP_THROW(ParseException(msg));
+      }
+      std::string value = str::ltrim(trimmed.substr(pos+1));
+      consume( _current_section, key, value);
+    }
+    else
+    {
+      std::string msg = str::form("%s: Line %d is missing '=' sign", _inputname.c_str(), line.lineNo());
+      ZYPP_THROW(ParseException(msg));
+    }
+
+    // set progress and allow cancel
+    if ( ! ticks.set( input_r.stream().tellg() ) )
+      ZYPP_THROW(AbortRequestException());
+  }
+  ticks.toMax();
+
+  endParse();
+  _inputname.clear();
+  MIL << "Done parsing " << input_r << endl;
+}
+
+/////////////////////////////////////////////////////////////////
+} // namespace parser
+///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/IniParser.h b/zypp/parser/IniParser.h
new file mode 100644 (file)
index 0000000..53d9c1a
--- /dev/null
@@ -0,0 +1,81 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/IniParser.h
+ *
+*/
+#ifndef ZYPP_PARSER_INIPARSER_H
+#define ZYPP_PARSER_INIPARSER_H
+
+#include <iosfwd>
+#include <string>
+#include <list>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/ProgressData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace parser
+{ /////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : IniParser
+//
+/** Basic SUSEtags parser.
+ * Will replace parser/tagfile/ and  parser/taggedfile/ stuff.
+*/
+class IniParser : private base::NonCopyable
+{
+public:
+  /** Default ctor */
+  IniParser();
+  /** Dtor */
+  virtual ~IniParser();
+  /** Parse the stream.
+   * \throw ParseException on errors. Invoke \ref consume
+   * for each tag. \ref consume might throw other exceptions
+   * as well.
+  */
+  void parse( const InputStream & imput_r, const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+
+public:
+  /** Called when start parsing. */
+  virtual void beginParse();
+  /** Called when a section is found. */
+  virtual void consume( const std::string &section );
+  /** Called when a key value is found. */
+  virtual void consume( const std::string &section, const std::string &key, const std::string &value );
+  /** Called when the parse is done. */
+  virtual void endParse();
+public:
+  /** Name of the current InputStream. */
+  const std::string & inputname() const
+  {
+    return _inputname;
+  }
+
+private:
+  std::string _inputname;
+  std::string _current_section;
+  int _line_nr;
+  //ProgressData _ticks;
+};
+
+/////////////////////////////////////////////////////////////////
+} // namespace parser
+///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_INIPARSER_H
diff --git a/zypp/parser/ParseException.cc b/zypp/parser/ParseException.cc
new file mode 100644 (file)
index 0000000..3e37b26
--- /dev/null
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/ParseException.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/parser/ParseException.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseException::ParseException
+    // METHOD TYPE : Ctor
+    //
+    ParseException::ParseException()
+    : Exception( "Parse exception" )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseException::ParseException
+    // METHOD TYPE : Ctor
+    //
+    ParseException::ParseException( const std::string & msg_r )
+    : Exception( msg_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseException::~ParseException
+    // METHOD TYPE : Dtor
+    //
+    ParseException::~ParseException() throw()
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseException::dumpOn
+    // METHOD TYPE : std::ostream &
+    //
+    std::ostream & ParseException::dumpOn( std::ostream & str ) const
+    {
+      return Exception::dumpOn( str );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/ParseException.h b/zypp/parser/ParseException.h
new file mode 100644 (file)
index 0000000..c205e69
--- /dev/null
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/tagfile/ParseException.h
+ *
+*/
+#ifndef ZYPP_PARSER_TAGFILE_PARSEEXCEPTION_H
+#define ZYPP_PARSER_TAGFILE_PARSEEXCEPTION_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/UserRequestException.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseException
+    //
+    /** */
+    class ParseException : public Exception
+    {
+    public:
+      /** Default ctor */
+      ParseException();
+      /** Ctor */
+      ParseException( const std::string & msg_r );
+        /** Dtor */
+      virtual ~ParseException() throw();
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_TAGFILE_PARSEEXCEPTION_H
diff --git a/zypp/parser/ParserProgress.h b/zypp/parser/ParserProgress.h
new file mode 100644 (file)
index 0000000..8467723
--- /dev/null
@@ -0,0 +1,104 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_ParserProgress_H
+#define ZYPP_ParserProgress_H
+
+#include "boost/shared_ptr.hpp"
+#include "boost/function.hpp"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace parser
+{ /////////////////////////////////////////////////////////////////
+
+  class ParserProgress
+  {
+    public:
+      typedef boost::shared_ptr<ParserProgress> Ptr;
+      
+      /**
+       * initializes a progress objetc, with a callback functor
+       * if you are not reporting percentage, then set
+       * the total_steps to the goal, and report using the same
+       * unit, then
+       */
+      ParserProgress( boost::function<void (long int)> fnc, long int total_steps = 100 )
+      : _fnc(fnc), _previous_progress(0), _total_steps(total_steps)
+      {
+        
+      };
+      
+      ~ParserProgress()
+      {};
+      
+      /**
+       * report progress, which in most cases
+       * executes the functor associated with
+       * this progress object to update progress
+       * information
+       */
+      void progress(long int p)
+      {
+        //std::cout << "real " << p << std::endl;
+        if ( _total_steps != 100 )
+        {
+          long int current_done = p;
+          p = (long int)(((double) current_done/(double) _total_steps)*100);
+        }
+        
+        if (_fnc && ( p !=  _previous_progress ))
+        {
+          _previous_progress = p;
+          _fnc(p);
+        }
+      }
+      
+      void setTotalSteps( long int total_steps )
+      {
+        _total_steps = total_steps;
+      }
+      
+      /**
+       * report progress finished
+       */
+      void finish()
+      {
+        int p = 100;
+        if (_fnc && ( p !=  _previous_progress ))
+        {
+          _previous_progress = p;
+          _fnc(p);
+        }
+      }
+      
+      /**
+       * report progress started
+       */
+      void start()
+      {
+        int p = 0;
+        if (_fnc && ( p !=  _previous_progress ))
+        {
+          _previous_progress = p;
+          _fnc(p);
+        }
+      }
+      
+    private:
+      boost::function<void (long int)> _fnc;
+      long int _previous_progress;
+      long int _total_steps;
+  };
+
+} // namespace parser
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ParserProgress_H
diff --git a/zypp/parser/ProductFileReader.cc b/zypp/parser/ProductFileReader.cc
new file mode 100644 (file)
index 0000000..c6d07ff
--- /dev/null
@@ -0,0 +1,287 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/ProductFileReader.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/Functional.h"
+
+#include "zypp/PathInfo.h"
+
+#include "zypp/parser/ProductFileReader.h"
+#include "zypp/parser/xml/ParseDef.h"
+#include "zypp/parser/xml/ParseDefConsume.h"
+#include "zypp/parser/xml/Reader.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    //
+    // class ProductFileData::Upgrade
+    //
+    /////////////////////////////////////////////////////////////////
+
+    struct ProductFileData::Upgrade::Impl
+    {
+      std::string                 _name;
+      std::string                 _summary;
+      std::string                 _repository;
+      std::string                 _product;
+      DefaultIntegral<bool,false> _notify;
+      std::string                 _status;
+    };
+
+    ProductFileData::Upgrade::Upgrade( Impl * allocated_r )
+      : _pimpl( allocated_r ? allocated_r : new Impl )
+    {}
+
+    std::string ProductFileData::Upgrade::name()       const { return _pimpl->_name; }
+    std::string ProductFileData::Upgrade::summary()    const { return _pimpl->_summary; }
+    std::string ProductFileData::Upgrade::repository() const { return _pimpl->_repository; }
+    std::string ProductFileData::Upgrade::product()    const { return _pimpl->_product; }
+    bool        ProductFileData::Upgrade::notify()     const { return _pimpl->_notify; }
+    std::string ProductFileData::Upgrade::status()     const { return _pimpl->_status; }
+
+    /////////////////////////////////////////////////////////////////
+    //
+    // class ProductFileData
+    //
+    /////////////////////////////////////////////////////////////////
+
+    struct ProductFileData::Impl
+    {
+      IdString    _vendor;
+      IdString    _name;
+      Edition     _edition;
+      Arch        _arch;
+
+      std::string _shortName;
+      std::string _summary;
+
+      std::string _productline;
+      std::string _registerTarget;
+      std::string _registerRelease;
+
+      std::string _updaterepokey;
+
+      Upgrades    _upgrades;
+    };
+
+    ProductFileData::ProductFileData( Impl * allocated_r )
+      : _pimpl( allocated_r ? allocated_r : new Impl )
+    {}
+
+    IdString    ProductFileData::vendor()  const { return _pimpl->_vendor; }
+    IdString    ProductFileData::name()    const { return _pimpl->_name; }
+    Edition     ProductFileData::edition() const { return _pimpl->_edition; }
+    Arch        ProductFileData::arch()    const { return _pimpl->_arch; }
+
+    std::string ProductFileData::shortName()   const { return _pimpl->_shortName; }
+    std::string ProductFileData::summary()     const { return _pimpl->_summary; }
+
+    std::string ProductFileData::productline()     const { return _pimpl->_productline; }
+    std::string ProductFileData::registerTarget()  const { return _pimpl->_registerTarget; }
+    std::string ProductFileData::registerRelease() const { return _pimpl->_registerRelease; }
+
+    std::string ProductFileData::updaterepokey() const { return _pimpl->_updaterepokey; }
+
+    const ProductFileData::Upgrades & ProductFileData::upgrades() const { return _pimpl->_upgrades; }
+
+    std::ostream & operator<<( std::ostream & str, const ProductFileData & obj )
+    {
+      str << str::form( "|product|%s|%s|%s|%s|",
+                        obj.name().c_str(),
+                        obj.edition().c_str(),
+                        obj.arch().c_str(),
+                        obj.vendor().c_str() );
+      if ( ! obj.upgrades().empty() )
+      {
+        for_( it, obj.upgrades().begin(), obj.upgrades().end() )
+          str << endl << "    " << *it;
+      }
+      return str;
+    }
+
+    std::ostream & operator<<( std::ostream & str, const ProductFileData::Upgrade & obj )
+    {
+      str << str::form( "|upgrade|%s|%s|%s|%s|%s|",
+                        obj.name().c_str(),
+                        obj.repository().c_str(),
+                        obj.product().c_str(),
+                        obj.status().c_str(),
+                        (obj.notify() ? "notify" : "noNotify") );
+      return str;
+    }
+    /////////////////////////////////////////////////////////////////
+    //
+    // class ProductFileReader
+    //
+    /////////////////////////////////////////////////////////////////
+
+    struct ProductNode : public xml::ParseDef
+    {
+      ProductNode( ProductFileData::Impl & pdata_r )
+        : ParseDef( "product", MANDTAORY )
+        , _pdata( pdata_r )
+      {
+        (*this)
+            ("vendor",        OPTIONAL,   xml::parseDefAssign( _pdata._vendor ) )
+            ("name",          MANDTAORY,  xml::parseDefAssign( _pdata._name ) )
+            ("version",       MANDTAORY,  xml::parseDefAssign( _version ) )
+            ("release",       MANDTAORY,  xml::parseDefAssign( _release ) )
+            ("arch",          MANDTAORY,  xml::parseDefAssign( _pdata._arch ) )
+            ("shortsummary",  OPTIONAL,   xml::parseDefAssign( _pdata._shortName ) )
+            ("summary",       MULTIPLE_OPTIONAL, xml::parseDefAssign( _ttext )( "lang", _tlocale )
+                                         >>bind( &ProductNode::doneLocalizedDefault, this, _1, ref(_pdata._summary) ))
+            ("productline",   OPTIONAL,   xml::parseDefAssign( _pdata._productline ) )
+            ("register",      OPTIONAL)
+            ("updaterepokey", OPTIONAL,   xml::parseDefAssign( _pdata._updaterepokey ) )
+            ("upgrades",      OPTIONAL)
+            ;
+
+        (*this)["register"]
+            ("target",        OPTIONAL,   xml::parseDefAssign( _pdata._registerTarget ) )
+            ("release",       OPTIONAL,   xml::parseDefAssign( _pdata._registerRelease ) )
+            ;
+
+        (*this)["upgrades"]
+            ("upgrade",       MULTIPLE_OPTIONAL, xml::parseDefAssign()
+                                                 >> bind( &ProductNode::doneUpgrade, this, _1 ))
+            ;
+
+        (*this)["upgrades"]["upgrade"]
+            ("name",          OPTIONAL,   xml::parseDefAssign( _upgrade._name ) )
+            ("summary",       OPTIONAL,   xml::parseDefAssign( _upgrade._summary ) )
+            ("repository",    OPTIONAL,   xml::parseDefAssign( _upgrade._repository ) )
+            ("product",       OPTIONAL,   xml::parseDefAssign( _upgrade._product ) )
+            ("notify",        OPTIONAL,   xml::parseDefAssign( _upgrade._notify ) )
+            ("status",        OPTIONAL,   xml::parseDefAssign( _upgrade._status ) )
+            ;
+
+        // </product> callback to build edition.
+        setConsumer( xml::parseDefAssign() >> bind( &ProductNode::done, this, _1 ) );
+        // xml::ParseDef::_debug = true;
+      }
+
+      /** collect _upgrade */
+      void doneUpgrade( const xml::Node & _node )
+      {
+        ProductFileData::Upgrade cdata( new ProductFileData::Upgrade::Impl( _upgrade ) );
+        _pdata._upgrades.push_back( cdata );
+        _upgrade = ProductFileData::Upgrade::Impl();
+      }
+      /** collect localized data */
+      void doneLocalizedDefault( const xml::Node & _node, std::string & store_r )
+      {
+       // take 1st or default
+       if ( store_r.empty() || _tlocale.empty() )
+         store_r = _ttext;
+      }
+
+      /** finaly */
+      void done( const xml::Node & _node )
+      {
+        _pdata._edition = Edition( _version, _release );
+      }
+
+      private:
+        ProductFileData::Impl & _pdata;
+
+        std::string             _version;
+        std::string             _release;
+
+       std::string             _ttext;
+       std::string             _tlocale;
+
+        ProductFileData::Upgrade::Impl _upgrade;
+    };
+
+    bool ProductFileReader::parse( const InputStream & input_r ) const
+    {
+      MIL << "+++" << input_r << endl;
+      bool ret = true;
+
+      ProductFileData::Impl * pdataImpl = 0;
+      ProductFileData pdata( (pdataImpl = new ProductFileData::Impl) );
+
+      try
+      {
+        xml::Reader reader( input_r );
+        ProductNode rootNode( *pdataImpl );
+        rootNode.take( reader );
+
+      }
+      catch ( const Exception & err )
+      {
+        // parse error
+        ERR << err << endl;
+        ERR << "---" << ret << " - " << input_r << endl;
+        return ret;
+      }
+
+      if ( _consumer )
+      {
+        ret = _consumer( pdata );
+      }
+
+      MIL << "---" << ret << " - " << input_r << endl;
+      return ret;
+    }
+
+    /////////////////////////////////////////////////////////////////
+
+    bool ProductFileReader::scanDir( const Consumer & consumer_r, const Pathname & dir_r )
+    {
+      std::list<Pathname> retlist;
+      int res = filesystem::readdir( retlist, dir_r, /*dots*/false );
+      if ( res != 0 )
+      {
+        WAR << "scanDir " << dir_r << " failed (" << res << ")" << endl;
+        return true;
+      }
+
+      ProductFileReader reader( consumer_r );
+      for_( it, retlist.begin(), retlist.end() )
+      {
+        if ( PathInfo( *it, PathInfo::LSTAT ).isFile() && ! reader.parse( *it ) )
+        {
+          return false; // consumer_r request to stop parsing.
+        }
+      }
+      return true;
+    }
+
+    ProductFileData ProductFileReader::scanFile( const Pathname & file_r )
+    {
+      if ( ! PathInfo( file_r ).isFile() )
+      {
+        WAR << "scanFile " << PathInfo( file_r ) << " is not a file." << endl;
+        return ProductFileData();
+      }
+
+      ProductFileData ret;
+      ProductFileReader reader( functor::getFirst( ret ), file_r );
+      return ret;
+   }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/ProductFileReader.h b/zypp/parser/ProductFileReader.h
new file mode 100644 (file)
index 0000000..6acd911
--- /dev/null
@@ -0,0 +1,177 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/ProductFileReader.h
+ *
+*/
+#ifndef ZYPP_PARSER_PRODUCTSDREADER_H
+#define ZYPP_PARSER_PRODUCTSDREADER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/InputStream.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/IdString.h"
+#include "zypp/Edition.h"
+#include "zypp/Arch.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ProductFileData
+    //
+    /** Data returned by \ref ProductFileReader
+     * \see \ref ProductFileReader
+    */
+    class ProductFileData
+    {
+      public:
+        class Impl;
+        /** Ctor takes ownership of \c allocated_r. */
+        ProductFileData( Impl * allocated_r = 0 );
+
+        /** Whether this is an empty object without valid data. */
+        bool empty() const
+        { return name().empty(); }
+
+      public:
+        IdString    vendor()  const;
+        IdString    name()    const;
+        Edition     edition() const;
+        Arch        arch()    const;
+
+       std::string shortName() const;
+       std::string summary()   const;
+
+      public:
+        std::string productline()     const;
+        std::string registerTarget()  const;
+        std::string registerRelease() const;
+
+      public:
+        std::string updaterepokey() const;
+
+      public:
+        ///////////////////////////////////////////////////////////////////
+        /** \see http://en.opensuse.org/Product_Management/Code11/Upgrade */
+        struct Upgrade
+        {
+          public:
+            class Impl;
+            /** Ctor takes ownership of \c allocated_r. */
+            Upgrade( Impl * allocated_r = 0 );
+
+          public:
+            std::string name()    const;
+            std::string summary() const;
+            std::string repository() const;
+            std::string product() const;
+            bool        notify()  const;
+            std::string status()  const;
+
+          private:
+            RWCOW_pointer<Impl> _pimpl;
+        };
+        ///////////////////////////////////////////////////////////////////
+
+        typedef std::vector<Upgrade> Upgrades;
+        const Upgrades & upgrades() const;
+
+      private:
+        RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates  ProductFileData Stream output */
+    std::ostream & operator<<( std::ostream & str, const ProductFileData & obj );
+
+    /** \relates  ProductFileData::Upgrade Stream output */
+    std::ostream & operator<<( std::ostream & str, const ProductFileData::Upgrade & obj );
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ProductFileReader
+    //
+    /** Parser for /etc/products.d enries (just relevant entires).
+     *
+     * \code
+     * #include "zypp/base/Functional.h" // functor::getAll
+     *
+     * std::vector<ProductFileData> result;
+     * ProductFileReader::scanDir( functor::getAll( std::back_inserter( result ) ),
+     *                             "/etc/products.d" );
+     * \endcode
+     */
+    class ProductFileReader
+    {
+    public:
+      /** Callback being invoked for each \ref ProductFileData parsed.
+       * Return \c false to stop parsing.
+       */
+      typedef function<bool( const ProductFileData & )> Consumer;
+
+    public:
+      ProductFileReader()
+      {}
+
+      ProductFileReader( const Consumer & consumer_r )
+      : _consumer( consumer_r )
+      {}
+
+      ProductFileReader( const Consumer & consumer_r, const InputStream & input_r )
+      : _consumer( consumer_r )
+      { parse( input_r ); }
+
+    public:
+      const Consumer & consumer() const
+      { return _consumer; }
+
+      void setConsumer( const Consumer & consumer_r )
+      { _consumer = consumer_r; }
+
+    public:
+      /** Parse the input stream and call \c _consumer for each
+       * parsed section.
+       *
+       * Returns \c false if the \c _consumer requested to stop parsing.
+       */
+      bool parse( const InputStream & input_r = InputStream() ) const;
+
+    public:
+      /** Parse all files (no symlinks) in \c dir_r and call \c consumer_r
+       * for each \ref ProductFileData parsed.
+       *
+       * Returns \c false if the \c _consumer requested to stop parsing.
+       */
+      static bool scanDir( const Consumer & consumer_r, const Pathname & dir_r );
+
+      /** Parse one file (or symlink) and return the  \ref ProductFileData parsed.
+       */
+      static ProductFileData scanFile( const Pathname & file_r );
+
+   private:
+      Consumer _consumer;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+   /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_PRODUCTSDREADER_H
diff --git a/zypp/parser/RepoFileReader.cc b/zypp/parser/RepoFileReader.cc
new file mode 100644 (file)
index 0000000..eefacc5
--- /dev/null
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/RepoFileReader.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/parser/IniDict.h"
+#include "zypp/parser/RepoFileReader.h"
+
+using std::endl;
+using zypp::parser::IniDict;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+   * \short List of RepoInfo's from a file.
+   * \param file pathname of the file to read.
+   */
+    static void repositories_in_stream( const InputStream &is,
+                                        const RepoFileReader::ProcessRepo &callback,
+                                        const ProgressData::ReceiverFnc &progress )
+    {
+      parser::IniDict dict(is);
+      for ( parser::IniDict::section_const_iterator its = dict.sectionsBegin();
+            its != dict.sectionsEnd();
+            ++its )
+      {
+        RepoInfo info;
+        info.setAlias(*its);
+        Url url;
+
+        for ( IniDict::entry_const_iterator it = dict.entriesBegin(*its);
+              it != dict.entriesEnd(*its);
+              ++it )
+        {
+          //MIL << (*it).first << endl;
+          if (it->first == "name" )
+            info.setName(it-> second);
+          else if ( it->first == "enabled" )
+            info.setEnabled( str::strToTrue( it->second ) );
+          else if ( it->first == "priority" )
+            info.setPriority( str::strtonum<unsigned>( it->second ) );
+          else if ( it->first == "baseurl" && !it->second.empty())
+            url = it->second;
+          else if ( it->first == "path" )
+            info.setPath( Pathname(it->second) );
+          else if ( it->first == "type" )
+            info.setType(repo::RepoType(it->second));
+          else if ( it->first == "autorefresh" )
+            info.setAutorefresh( str::strToTrue( it->second ) );
+          else if ( it->first == "mirrorlist" && !it->second.empty())
+            info.setMirrorListUrl(Url(it->second));
+          else if ( it->first == "gpgkey" && !it->second.empty())
+          {
+            std::vector<std::string> keys;
+            str::split( it->second, std::back_inserter(keys) );
+            if ( ! keys.empty() ) 
+              info.setGpgKeyUrl( Url(*keys.begin()) );
+          }
+          else if ( it->first == "gpgcheck" )
+            info.setGpgCheck( str::strToTrue( it->second ) );
+         else if ( it->first == "keeppackages" )
+           info.setKeepPackages( str::strToTrue( it->second ) );
+         else if ( it->first == "service" )
+           info.setService( it->second );
+          else if ( it->first == "proxy" )
+          {
+           if (it->second != "_none_" )
+            { 
+              str::regex ex("^(.*):([0-9]+)$");
+              str::smatch what;
+              if(str::regex_match(it->second, what, ex)){
+               url.setQueryParam("proxy", what[1]);
+               url.setQueryParam("proxyport", what[2]);
+              }
+            }
+          } else
+            ERR << "Unknown attribute in [" << *its << "]: " << it->second << " ignored" << endl;
+        }
+        if (url.isValid())
+            info.addBaseUrl(url);
+        info.setFilepath(is.path());
+        MIL << info << endl;
+        // add it to the list.
+        callback(info);
+        //if (!progress.tick())
+        //  ZYPP_THROW(AbortRequestException());
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : RepoFileReader
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    RepoFileReader::RepoFileReader( const Pathname & repo_file,
+                                    const ProcessRepo & callback,
+                                    const ProgressData::ReceiverFnc &progress )
+      : _callback(callback)
+    {
+      repositories_in_stream(InputStream(repo_file), _callback, progress);
+    }
+
+    RepoFileReader::RepoFileReader( const InputStream &is,
+                                    const ProcessRepo & callback,
+                                    const ProgressData::ReceiverFnc &progress )
+      : _callback(callback)
+    {
+      repositories_in_stream(is, _callback, progress);
+    }
+
+    RepoFileReader::~RepoFileReader()
+    {}
+
+
+    std::ostream & operator<<( std::ostream & str, const RepoFileReader & obj )
+    {
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/RepoFileReader.h b/zypp/parser/RepoFileReader.h
new file mode 100644 (file)
index 0000000..e326b56
--- /dev/null
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/RepoFileReader.h
+ *
+*/
+#ifndef ZYPP_REPO_REPOFILEREADER_H
+#define ZYPP_REPO_REPOFILEREADER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/ProgressData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+     * \short Read repository data from a .repo file
+     *
+     * After each repo is read, a \ref RepoInfo is prepared and \ref _callback
+     * is called with the object passed in.
+     *
+     * The \ref _callback is provided on construction.
+     *
+     * \code
+     * RepoFileReader reader(repo_file, 
+     *                bind( &SomeClass::callbackfunc, &SomeClassInstance, _1, _2 ) );
+     * \endcode
+     */
+    class RepoFileReader
+    {
+      friend std::ostream & operator<<( std::ostream & str, const RepoFileReader & obj );
+    public:
+      
+     /**
+      * Callback definition.
+      * First parameter is a \ref RepoInfo object with the resource
+      * second parameter is the resource type.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+      typedef function< bool( const RepoInfo & )> ProcessRepo;
+      
+      /** Implementation  */
+      class Impl;
+
+    public:
+     /**
+      * \short Constructor. Creates the reader and start reading.
+      *
+      * \param repo_file A valid .repo file
+      * \param callback Callback that will be called for each repository.
+      * \param progress Optional progress function. \see ProgressData
+      *
+      * \throws AbortRequestException If the callback returns false
+      * \throws Exception If a error occurs at reading / parsing
+      *
+      */
+      RepoFileReader( const Pathname & repo_file,
+                      const ProcessRepo & callback,
+                      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+
+     /**
+      * \short Constructor. Creates the reader and start reading.
+      *
+      * \param is A valid input stream
+      * \param callback Callback that will be called for each repository.
+      * \param progress Optional progress function. \see ProgressData
+      *
+      * \throws AbortRequestException If the callback returns false
+      * \throws Exception If a error occurs at reading / parsing
+      *
+      */
+      RepoFileReader( const InputStream &is,
+                      const ProcessRepo & callback,
+                      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+     
+      /**
+       * Dtor
+       */
+      ~RepoFileReader();
+    private:
+      ProcessRepo _callback;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RepoFileReader Stream output */
+    std::ostream & operator<<( std::ostream & str, const RepoFileReader & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_REPOFILEREADER_H
diff --git a/zypp/parser/RepoindexFileReader.cc b/zypp/parser/RepoindexFileReader.cc
new file mode 100644 (file)
index 0000000..6e4e4d4
--- /dev/null
@@ -0,0 +1,204 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/RepoindexFileReader.cc
+ * Implementation of repoindex.xml file reader.
+ */
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/InputStream.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;
+
+namespace zypp
+{
+  namespace parser
+  {
+
+
+  ///////////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : RepoindexFileReader::Impl
+  //
+  class RepoindexFileReader::Impl : private base::NonCopyable
+  {
+  public:
+    /**
+     * CTOR
+     *
+     * \see RepoindexFileReader::RepoindexFileReader(Pathname,ProcessResource)
+     */
+    Impl(const InputStream &is, const ProcessResource & callback);
+
+    /**
+     * Callback provided to the XML parser.
+     */
+    bool consumeNode( Reader & reader_r );
+
+
+  private:
+    /** Function for processing collected data. Passed-in through constructor. */
+    ProcessResource _callback;
+    string _target_distro;
+  };
+  ///////////////////////////////////////////////////////////////////////
+
+  RepoindexFileReader::Impl::Impl(const InputStream &is,
+                                  const ProcessResource & callback)
+    : _callback(callback)
+  {
+    Reader reader( is );
+    MIL << "Reading " << is.path() << endl;
+    reader.foreachNode( bind( &RepoindexFileReader::Impl::consumeNode, this, _1 ) );
+  }
+
+  // --------------------------------------------------------------------------
+
+  /*
+   * xpath and multiplicity of processed nodes are included in the code
+   * for convenience:
+   *
+   * // xpath: <xpath> (?|*|+)
+   *
+   * if multiplicity is ommited, then the node has multiplicity 'one'.
+   */
+
+  // --------------------------------------------------------------------------
+
+  bool RepoindexFileReader::Impl::consumeNode( Reader & reader_r )
+  {
+    if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
+    {
+      // xpath: /repoindex
+      if ( reader_r->name() == "repoindex" )
+      {
+        return true;
+      }
+
+      // xpath: /repoindex/data (+)
+      if ( reader_r->name() == "repo" )
+      {
+        XmlString s;
+
+        RepoInfo info;
+
+        // enabled or disabled is controlled by the
+        // reposToEnable/Disable list, unless the
+        // enabled attribute is set
+        info.setEnabled(false);
+
+        // Set some defaults that are not contained in the repo information
+        info.setAutorefresh( true );
+
+        // 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(str::form(_("One or both of '%s' or '%s' attributes is required."), "url", "path"));
+        //! \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
+        s = reader_r->getAttribute("alias");
+        if (!s.get())
+          throw ParseException(str::form(_("Required attribute '%s' is missing."), "alias"));
+        info.setAlias(s.asString());
+
+        // optional type
+        s = reader_r->getAttribute("type");
+        if (s.get())
+          info.setType(repo::RepoType(s.asString()));
+
+        // optional name
+        s = reader_r->getAttribute("name");
+        if (s.get())
+          info.setName(s.asString());
+
+        // optional targetDistro
+        s = reader_r->getAttribute("distro_target");
+        if (s.get())
+          info.setTargetDistribution(s.asString());
+
+        // optional priority
+        s = reader_r->getAttribute("priority");
+        if (s.get()) {
+          info.setPriority(str::strtonum<unsigned>(s.asString()));
+        }
+
+        // optional enabled
+        s = reader_r->getAttribute("enabled");
+        if (s.get()) {
+          info.setEnabled(str::strToTrue(s.asString()));
+        }
+
+        DBG << info << endl;
+
+        // ignore the rest
+        _callback(info);
+        return true;
+      }
+    }
+
+    return true;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : RepoindexFileReader
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  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()
+  {}
+
+
+  } // ns parser
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/RepoindexFileReader.h b/zypp/parser/RepoindexFileReader.h
new file mode 100644 (file)
index 0000000..c720f58
--- /dev/null
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/RepoindexFileReader.h
+ * Interface of repoindex.xml file reader.
+ */
+#ifndef zypp_source_yum_RepoindexFileReader_H
+#define zypp_source_yum_RepoindexFileReader_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/Pathname.h"
+
+namespace zypp
+{
+  class RepoInfo;
+
+  namespace parser
+  {
+
+  /**
+   * Reads through a repoindex.xml file and collects repositories.
+   *
+   * After each repository is read, a \ref RepoInfo
+   * is prepared and \ref _callback
+   * is called with this object passed in.
+   *
+   * The \ref _callback is provided on construction.
+   *
+   *
+   * \code
+   * RepoindexFileReader reader(repoindex_file,
+   *                  bind( &SomeClass::callbackfunc, &SomeClassInstance, _1) );
+   * \endcode
+   */
+  class RepoindexFileReader : private base::NonCopyable
+  {
+  public:
+   /**
+    * Callback definition.
+    * First parameter is a \ref RepoInfo object with the resource
+    * FIXME return value is ignored
+    */
+    typedef function< bool(
+        const RepoInfo & )>
+      ProcessResource;
+
+   /**
+    * CTOR. Creates also \ref xml::Reader and starts reading.
+    *
+    * \param repoindexFile is the repoindex.xml file you want to read
+    * \param callback is a function.
+    *
+    * \see RepoindexFileReader::ProcessResource
+    */
+    RepoindexFileReader( const zypp::Pathname & repoindexFile,
+                         const ProcessResource & callback);
+
+    /**
+     * \short Constructor. Creates the reader and start reading.
+     *
+     * \param is a valid input stream
+     * \param callback Callback that will be called for each repository.
+     *
+     * \see RepoindexFileReader::ProcessResource
+     */
+     RepoindexFileReader( const InputStream &is,
+                          const ProcessResource & callback );
+
+    /**
+     * DTOR
+     */
+    ~RepoindexFileReader();
+
+  private:
+    class Impl;
+    RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+  };
+
+
+  } // ns parser
+} // ns zypp
+
+#endif /*zypp_source_yum_RepoindexFileReader_H*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/ServiceFileReader.cc b/zypp/parser/ServiceFileReader.cc
new file mode 100644 (file)
index 0000000..49c3c31
--- /dev/null
@@ -0,0 +1,131 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/RepoFileReader.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/parser/IniDict.h"
+#include "zypp/parser/ServiceFileReader.h"
+#include "zypp/ServiceInfo.h"
+
+using std::endl;
+using zypp::parser::IniDict;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    class ServiceFileReader::Impl
+    {
+    public:
+      static void parseServices( const Pathname & file,
+          const ServiceFileReader::ProcessService & callback );
+    };
+
+    void ServiceFileReader::Impl::parseServices( const Pathname & file,
+                                  const ServiceFileReader::ProcessService & callback/*,
+                                  const ProgressData::ReceiverFnc &progress*/ )
+    {
+      InputStream is(file);
+      if( is.stream().fail() )
+      {
+        ZYPP_THROW(Exception("Failed to open service file"));
+      }
+
+      parser::IniDict dict(is);
+      for ( parser::IniDict::section_const_iterator its = dict.sectionsBegin();
+            its != dict.sectionsEnd();
+            ++its )
+      {
+        MIL << (*its) << endl;
+
+        ServiceInfo service(*its);
+
+        for ( IniDict::entry_const_iterator it = dict.entriesBegin(*its);
+              it != dict.entriesEnd(*its);
+              ++it )
+        {
+          // MIL << (*it).first << endl;
+          if ( it->first == "name" )
+            service.setName( it->second );
+          else if ( it->first == "url" && ! it->second.empty() )
+            service.setUrl( Url (it->second) );
+          else if ( it->first == "enabled" )
+            service.setEnabled( str::strToTrue( it->second ) );
+          else if ( it->first == "autorefresh" )
+            service.setAutorefresh( str::strToTrue( it->second ) );
+          else if ( it->first == "type" )
+            service.setType( repo::ServiceType(it->second) );
+          else if ( it->first == "repostoenable" )
+          {
+            std::vector<std::string> aliases;
+            str::splitEscaped( it->second, std::back_inserter(aliases) );
+            for_( ait, aliases.begin(), aliases.end() )
+            {
+              service.addRepoToEnable( *ait );
+            }
+          }
+          else if ( it->first == "repostodisable" )
+          {
+            std::vector<std::string> aliases;
+            str::splitEscaped( it->second, std::back_inserter(aliases) );
+            for_( ait, aliases.begin(), aliases.end() )
+            {
+              service.addRepoToDisable( *ait );
+            }
+          }
+          else
+            ERR << "Unknown attribute " << it->first << " ignored" << endl;
+        }
+
+        MIL << "Linking ServiceInfo with file " << file << endl;
+        service.setFilepath(file);
+
+        // add it to the list.
+        if ( !callback(service) )
+          ZYPP_THROW(AbortRequestException());
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : RepoFileReader
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ServiceFileReader::ServiceFileReader( const Pathname & repo_file,
+                                    const ProcessService & callback/*,
+                                    const ProgressData::ReceiverFnc &progress */)
+    {
+      Impl::parseServices(repo_file, callback/*, progress*/);
+      //MIL << "Done" << endl;
+    }
+
+    ServiceFileReader::~ServiceFileReader()
+    {}
+
+    std::ostream & operator<<( std::ostream & str, const ServiceFileReader & obj )
+    {
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/ServiceFileReader.h b/zypp/parser/ServiceFileReader.h
new file mode 100644 (file)
index 0000000..85bbc1c
--- /dev/null
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/ServiceFileReader.h
+ *
+*/
+#ifndef ZYPP_REPO_SERVICEFILEREADER_H
+#define ZYPP_REPO_SERVICEFILEREADER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ProgressData.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class ServiceInfo;
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+     * \short Read service data from a .service file
+     *
+     * After each service is read, a \ref ServiceInfo is prepared and \ref _callback
+     * is called with the object passed in.
+     *
+     * The \ref _callback is provided on construction.
+     *
+     * \code
+     * ServiceFileReader reader(service_file, 
+     *                bind( &SomeClass::callbackfunc, &SomeClassInstance, _1 ) );
+     * \endcode
+     */
+    class ServiceFileReader
+    {
+      friend std::ostream & operator<<( std::ostream & str, const ServiceFileReader & obj );
+    public:
+      
+     /**
+      * Callback definition.
+      * First parameter is a \ref ServiceInfo object with the resource.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+      typedef function< bool( const ServiceInfo & )> ProcessService;
+      
+      /** Implementation  */
+      class Impl;
+
+    public:
+     /**
+      * \short Constructor. Creates the reader and start reading.
+      *
+      * \param serviceFile A valid .repo file
+      * \param callback Callback that will be called for each repository.
+      *
+      * \throws AbortRequestException If the callback returns false
+      * \throws Exception If a error occurs at reading / parsing
+      *
+      */
+      ServiceFileReader( const Pathname & serviceFile,
+                      const ProcessService & callback);
+     
+      /**
+       * Dtor
+       */
+      ~ServiceFileReader();
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ServiceFileReader Stream output */
+    std::ostream & operator<<( std::ostream & str, const ServiceFileReader & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_SERVICEFILEREADER_H
diff --git a/zypp/parser/plaindir/RepoParser.cc b/zypp/parser/plaindir/RepoParser.cc
new file mode 100644 (file)
index 0000000..feb64aa
--- /dev/null
@@ -0,0 +1,71 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PathInfo.h"
+
+#include "zypp/parser/plaindir/RepoParser.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace parser
+{ /////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+namespace plaindir
+{ /////////////////////////////////////////////////////////////////
+
+static void recursive_timestamp( const Pathname &dir, time_t & max )
+{
+  std::list<std::string> dircontent;
+  if ( filesystem::readdir( dircontent, dir, false/*no dots*/ ) != 0 )
+    return; // readdir logged the error
+
+  for_( it, dircontent.begin(), dircontent.end() )
+  {
+    PathInfo pi( dir + *it, PathInfo::LSTAT );
+    if ( pi.isDir() )
+    {
+      recursive_timestamp( pi.path(), max );
+      if ( pi.mtime() > max )
+        max = pi.mtime();
+    }
+  }
+}
+
+RepoStatus dirStatus( const Pathname &dir )
+{
+  time_t t = 0;
+  PathInfo pi(dir);
+  if ( pi.isDir() )
+  {
+    t = pi.mtime();
+    recursive_timestamp(dir,t);
+  }
+  RepoStatus status;
+  status.setTimestamp(Date(t));
+  status.setChecksum(str::numstring(t));
+  return status;
+}
+
+/////////////////////////////////////////////////////////////////
+} // namespace plaindir
+///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace parser
+///////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/plaindir/RepoParser.h b/zypp/parser/plaindir/RepoParser.h
new file mode 100644 (file)
index 0000000..0481569
--- /dev/null
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_PARSER_PLAINDIR_REPOPARSER_H
+#define ZYPP_PARSER_PLAINDIR_REPOPARSER_H
+
+#include "zypp/RepoStatus.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace plaindir
+    { /////////////////////////////////////////////////////////////////
+
+      /**
+       * \short Gives a cookie for a dir
+       */
+      RepoStatus dirStatus( const Pathname &dir );
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace plaindir
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_PLAINDIR_REPOPARSER_H
diff --git a/zypp/parser/schema/repoindex.rnc b/zypp/parser/schema/repoindex.rnc
new file mode 100644 (file)
index 0000000..f183cc2
--- /dev/null
@@ -0,0 +1,40 @@
+# repoindex.xml RNC schema
+
+start = repoindex-element
+
+repoindex-element =
+  element repoindex {
+    element repo {
+      # Internal repo identifier
+      attribute alias { xsd:string },
+
+      # Human-readable name of the repository
+      attribute name { xsd:string }?,
+
+      # More detailed description of what's inside
+      attribute description { xsd:string }?,
+      (
+        # Path relative to the base url of the service
+        # WARNING 'repo/' is always prepended to this path
+        #         (this is inherited from NU services)
+        attribute path { xsd:string }
+        |
+        # Full URI of the repository (can also be outside of the service base)
+        attribute url { xsd:anyURI }
+      ),
+      # unique identifier of the target distribution (distroname-version-arch)
+      # if not specified, all the repositories will be added by the client
+      attribute distro_target { xsd:string }?,
+
+      # An optional repository type. Will be determined automatically if not
+      # specified.
+      attribute type { xsd:string }?,
+
+      # Repository priority. Starts from 1 (the highest); the higher number,
+      # the lowe priority. 
+      attribute priority { xsd:integer }?,
+
+      # legacy attribute, currently not used
+      attribute pub { xsd:boolean }? 
+    }
+  }
diff --git a/zypp/parser/susetags/ContentFileReader.cc b/zypp/parser/susetags/ContentFileReader.cc
new file mode 100644 (file)
index 0000000..3985bdf
--- /dev/null
@@ -0,0 +1,276 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/susetags/ContentFileReader.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/parser/ParseException.h"
+
+#include "zypp/parser/susetags/ContentFileReader.h"
+#include "zypp/parser/susetags/RepoIndex.h"
+
+using std::endl;
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace susetags
+    { /////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : ContentFileReader::Impl
+      //
+      /** ContentFileReader implementation. */
+      struct ContentFileReader::Impl
+      {
+       public:
+         Impl( const ContentFileReader & parent_r )
+         : _parent( parent_r )
+         {}
+
+         RepoIndex & repoindex()
+         {
+           if ( !_repoindex )
+             _repoindex = new RepoIndex;
+           return *_repoindex;
+         }
+
+         bool hasRepoIndex() const
+         { return _repoindex; }
+
+         RepoIndex_Ptr handoutRepoIndex()
+         {
+           RepoIndex_Ptr ret;
+           ret.swap( _repoindex );
+           _repoindex = 0;
+           return ret;
+         }
+
+       public:
+         bool setFileCheckSum( std::map<std::string, CheckSum> & map_r, const std::string & value ) const
+         {
+           bool error = false;
+           std::vector<std::string> words;
+           if ( str::split( value, std::back_inserter( words ) ) == 3 )
+           {
+             map_r[words[2]] = CheckSum( words[0], words[1] );
+           }
+           else
+           {
+             error = true;
+           }
+           return error;
+         }
+
+       public:
+         std::string _inputname;
+
+       private:
+         const ContentFileReader & _parent;
+         RepoIndex_Ptr      _repoindex;
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : ContentFileReader
+      //
+      ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::ContentFileReader
+      //       METHOD TYPE : Ctor
+      //
+      ContentFileReader::ContentFileReader()
+      {}
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::~ContentFileReader
+      //       METHOD TYPE : Dtor
+      //
+      ContentFileReader::~ContentFileReader()
+      {}
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::beginParse
+      //       METHOD TYPE : void
+      //
+      void ContentFileReader::beginParse()
+      {
+       _pimpl.reset( new Impl(*this) );
+        // actually mandatory, but in case they were forgotten...
+        _pimpl->repoindex().descrdir = "suse/setup/descr";
+        _pimpl->repoindex().datadir = "suse";
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::endParse
+      //       METHOD TYPE : void
+      //
+      void ContentFileReader::endParse()
+      {
+       // consume oldData
+       if ( _pimpl->hasRepoIndex() )
+       {
+         if ( _repoIndexConsumer )
+           _repoIndexConsumer( _pimpl->handoutRepoIndex() );
+       }
+
+       MIL << "[Content]" << endl;
+       _pimpl.reset();
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::userRequestedAbort
+      //       METHOD TYPE : void
+      //
+      void ContentFileReader::userRequestedAbort( unsigned lineNo_r )
+      {
+       ZYPP_THROW( AbortRequestException( errPrefix( lineNo_r ) ) );
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::errPrefix
+      //       METHOD TYPE : std::string
+      //
+      std::string ContentFileReader::errPrefix( unsigned lineNo_r,
+                                               const std::string & msg_r,
+                                               const std::string & line_r ) const
+      {
+       return str::form( "%s:%u:%s | %s",
+                         _pimpl->_inputname.c_str(),
+                         lineNo_r,
+                         line_r.c_str(),
+                         msg_r.c_str() );
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : ContentFileReader::parse
+      //       METHOD TYPE : void
+      //
+      void ContentFileReader::parse( const InputStream & input_r,
+                                    const ProgressData::ReceiverFnc & fnc_r )
+      {
+       MIL << "Start parsing content repoindex" << input_r << endl;
+       if ( ! input_r.stream() )
+       {
+         std::ostringstream s;
+         s << "Can't read bad stream: " << input_r;
+         ZYPP_THROW( ParseException( s.str() ) );
+       }
+       beginParse();
+       _pimpl->_inputname = input_r.name();
+
+       ProgressData ticks( makeProgressData( input_r ) );
+       ticks.sendTo( fnc_r );
+       if ( ! ticks.toMin() )
+         userRequestedAbort( 0 );
+
+       iostr::EachLine line( input_r );
+       for( ; line; line.next() )
+       {
+         // strip 1st word from line to separate tag and value.
+         std::string value( *line );
+         std::string key( str::stripFirstWord( value, /*ltrim_first*/true ) );
+
+         if ( key.empty() || *key.c_str() == '#' ) // empty or comment line
+         {
+           continue;
+         }
+
+         // strip modifier if exists
+         std::string modifier;
+         std::string::size_type pos = key.rfind( '.' );
+         if ( pos != std::string::npos )
+         {
+           modifier = key.substr( pos+1 );
+           key.erase( pos );
+         }
+
+         //
+         // ReppoIndex related data:
+         //
+         else if ( key == "DESCRDIR" )
+         {
+           _pimpl->repoindex().descrdir = value;
+         }
+         else if ( key == "DATADIR" )
+         {
+           _pimpl->repoindex().datadir = value;
+         }
+         else if ( key == "KEY" )
+         {
+           if ( _pimpl->setFileCheckSum( _pimpl->repoindex().signingKeys, value ) )
+           {
+             ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [KEY algorithm checksum filename]", *line ) ) );
+           }
+         }
+         else if ( key == "META" )
+         {
+           if ( _pimpl->setFileCheckSum( _pimpl->repoindex().metaFileChecksums, value ) )
+           {
+             ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
+           }
+         }
+          else if ( key == "HASH" )
+         {
+           if ( _pimpl->setFileCheckSum( _pimpl->repoindex().mediaFileChecksums, value ) )
+           {
+             ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
+           }
+         }
+          else
+         {
+            DBG << errPrefix( line.lineNo(), "ignored", *line ) << endl;
+          }
+
+
+         if ( ! ticks.set( input_r.stream().tellg() ) )
+           userRequestedAbort( line.lineNo() );
+       }
+
+       //
+       // post processing
+       //
+       if ( ! ticks.toMax() )
+         userRequestedAbort( line.lineNo() );
+
+       endParse();
+       MIL << "Done parsing " << input_r << endl;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace susetags
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/susetags/ContentFileReader.h b/zypp/parser/susetags/ContentFileReader.h
new file mode 100644 (file)
index 0000000..addf838
--- /dev/null
@@ -0,0 +1,103 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/susetags/ContentFileReader.h
+ *
+*/
+#ifndef ZYPP_PARSER_SUSETAGS_CONTENTFILEREADER_H
+#define ZYPP_PARSER_SUSETAGS_CONTENTFILEREADER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/InputStream.h"
+
+#include "zypp/ProgressData.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace susetags
+    { /////////////////////////////////////////////////////////////////
+
+      class RepoIndex;
+      DEFINE_PTR_TYPE(RepoIndex);
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : ContentFileReader
+      //
+      /** Parse repoindex part from a \c content file.
+       * This is all the downloader needs.
+      */
+      class ContentFileReader : private base::NonCopyable
+      {
+       public:
+         typedef function<void(const RepoIndex_Ptr &)> RepoIndexConsumer;
+
+       public:
+         /** Default ctor */
+         ContentFileReader();
+         /** Dtor */
+         virtual ~ContentFileReader();
+          /** Parse the stream.
+          * \throw ParseException on errors.
+          * \throw AbortRequestException on user request.
+          * Invokes \ref consume for each tag. \ref consume might throw
+          * other exceptions as well.
+          */
+         virtual void parse( const InputStream & imput_r,
+                             const ProgressData::ReceiverFnc & fnc_r = ProgressData::ReceiverFnc() );
+
+       public:
+         /** Consumer to call when repo index was parsed. */
+         void setRepoIndexConsumer( const RepoIndexConsumer & fnc_r )
+         { _repoIndexConsumer = fnc_r; }
+
+       protected:
+         /** Called when start parsing. */
+         virtual void beginParse();
+         /** Called when the parse is done. */
+         virtual void endParse();
+
+       protected:
+          /** Called when user(callback) request to abort.
+          * \throws AbortRequestException unless overloaded.
+          */
+         virtual void userRequestedAbort( unsigned lineNo_r );
+
+       protected:
+         /** Prefix exception message with line information. */
+         std::string errPrefix( unsigned lineNo_r,
+                                const std::string & msg_r = std::string(),
+                                const std::string & line_r = "-" ) const;
+
+       private:
+         class Impl;
+         RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+         RepoIndexConsumer _repoIndexConsumer;
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace susetags
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_SUSETAGS_CONTENTFILEREADER_H
diff --git a/zypp/parser/susetags/RepoIndex.cc b/zypp/parser/susetags/RepoIndex.cc
new file mode 100644 (file)
index 0000000..41e62a8
--- /dev/null
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/susetags/RepoIndex.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/parser/susetags/RepoIndex.h"
+
+using std::endl;
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace susetags
+    { /////////////////////////////////////////////////////////////////
+
+      IMPL_PTR_TYPE(RepoIndex);
+
+      std::ostream & RepoIndex::dumpOn( std::ostream & str ) const
+      {
+        return str
+            << "RepoIndex checksums: META files " << metaFileChecksums.size()
+            << ", HASH files " << mediaFileChecksums.size()
+            << ", KEY files " << signingKeys.size();
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace susetags
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/susetags/RepoIndex.h b/zypp/parser/susetags/RepoIndex.h
new file mode 100644 (file)
index 0000000..1abe432
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/susetags/RepoIndex.h
+ *
+*/
+#ifndef ZYPP_PARSER_SUSETAGS_REPOINDEX_H
+#define ZYPP_PARSER_SUSETAGS_REPOINDEX_H
+
+#include <iosfwd>
+#include <list>
+#include <map>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Arch.h"
+#include "zypp/CheckSum.h"
+#include "zypp/Pathname.h"
+#include "zypp/Locale.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace susetags
+    { /////////////////////////////////////////////////////////////////
+
+      DEFINE_PTR_TYPE(RepoIndex);
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : RepoIndex
+      //
+      /** Repository content data (from /content file).
+       * File and Checksum definitions required by Downloader and Parser.
+       * (Do not confuse with NU's repoindex.xml)
+      */
+      class RepoIndex : public base::ReferenceCounted, private base::NonCopyable
+      {
+        friend class ContentFileReader;
+       public:
+         typedef std::map<std::string, CheckSum> FileChecksumMap;
+
+         Pathname descrdir;
+         Pathname datadir;
+
+         FileChecksumMap metaFileChecksums;
+          FileChecksumMap mediaFileChecksums;
+         FileChecksumMap signingKeys;
+
+       protected:
+         /** Overload to realize std::ostream & operator\<\<. */
+         virtual std::ostream & dumpOn( std::ostream & str ) const;
+      };
+      //////////////////////////////////////////////////////////////////
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace susetags
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_SUSETAGS_REPOINDEX_H
diff --git a/zypp/parser/ws/WebpinResultFileReader.cc b/zypp/parser/ws/WebpinResultFileReader.cc
new file mode 100644 (file)
index 0000000..cee6248
--- /dev/null
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/WebpinResultFileReader.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/parser/xml/Reader.h"
+#include "zypp/parser/ws/WebpinResultFileReader.h"
+#include "zypp/ws/WebpinResult.h"
+
+using std::endl;
+using namespace zypp::xml;
+using namespace zypp::ws;
+
+namespace zypp
+{ 
+namespace parser
+{
+namespace ws
+{
+    
+class WebpinResultFileReader::Impl
+{
+public:
+    Impl( const Pathname &result_file,
+          const ProcessWebpinResult &callback );
+    
+    /**
+     * Callback provided to the XML parser.
+     */
+    bool consumeNode( Reader & reader_r );
+private:
+    shared_ptr<WebpinResult> _result;
+    ProcessWebpinResult _callback;
+};
+
+bool WebpinResultFileReader::Impl::consumeNode(Reader & reader_r)
+{
+    if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
+    {
+        // xpath: /packages
+        if ( reader_r->name() == "packages" )
+        {
+            return true;
+        }
+        
+        // xpath: /packages/package (+)
+        if ( reader_r->name() == "package" )
+        { _result.reset( new WebpinResult() ); }
+        
+        // xpath: /packages/name
+        if ( reader_r->name() == "name" )
+        { _result->setName(reader_r.nodeText().asString());}
+
+        // xpath: /packages/version
+        if ( reader_r->name() == "version" )
+        { _result->setEdition(Edition(reader_r.nodeText().asString())); }
+        
+        // xpath: /packages/summary
+        if ( reader_r->name() == "summary" )
+        { _result->setSummary(reader_r.nodeText().asString()); }
+        
+        // xpath: /packages/repoURL
+        if ( reader_r->name() == "repoURL" )
+        { _result->setRepositoryUrl(Url(reader_r.nodeText().asString())); }
+        
+        // xpath: /packages/checksum
+        if ( reader_r->name() == "checksum" )
+        { _result->setChecksum(CheckSum::sha1(reader_r.nodeText().asString())); }
+        // xpath: /packages/distro
+        if ( reader_r->name() == "distro" )
+        { _result->setDistribution(reader_r.nodeText().asString());}
+
+        return true;
+    }
+    else if ( reader_r->nodeType() == XML_READER_TYPE_END_ELEMENT )
+    {
+        // xpath: /packages/package (+)
+        if ( reader_r->name() == "package" )
+        {
+            _callback(*_result);
+        }
+    }
+
+    return true;
+}
+    
+
+WebpinResultFileReader::Impl::Impl( const Pathname &result_file,
+                                    const ProcessWebpinResult &callback )
+    : _callback(callback)
+{
+    Reader reader( result_file );
+    MIL << "Reading " << result_file << endl;
+    reader.foreachNode( bind( &WebpinResultFileReader::Impl::consumeNode, this, _1 ) );
+}
+    
+
+WebpinResultFileReader::WebpinResultFileReader( const Pathname & result_file,
+                                                const ProcessWebpinResult & callback )
+    : _pimpl(new WebpinResultFileReader::Impl(result_file, callback))
+{
+}
+    
+WebpinResultFileReader::~WebpinResultFileReader()
+{}
+    
+std::ostream & operator<<( std::ostream & str, const WebpinResultFileReader & obj )
+{
+    return str;
+}
+
+
+} // namespace ws
+} // namespace parser
+} // namespace zypp
+
diff --git a/zypp/parser/ws/WebpinResultFileReader.h b/zypp/parser/ws/WebpinResultFileReader.h
new file mode 100644 (file)
index 0000000..090021f
--- /dev/null
@@ -0,0 +1,96 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/WebpinResultFileReader.h
+ *
+*/
+#ifndef ZYPP_PARSER_WEBPINRESULTFILEREADER_H
+#define ZYPP_PARSER_WEBPINRESULTFILEREADER_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ProgressData.h"
+#include "zypp/Pathname.h"
+#include "zypp/ws/WebpinResult.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  
+  namespace parser
+  { /////////////////////////////////////////////////////////////////
+      
+   namespace ws
+   {
+       
+    /**
+     * \short Read packages and repository search result data from
+     * from webpin web search results.
+     *
+     * For each result, a \ref WebpinResult is prepared and \ref _callback
+     * is called with the object passed in.
+     *
+     * The \ref _callback is provided on construction.
+     *
+     * \code
+     * WebpinResultFileReader reader(repo_file, 
+     *     bind( &SomeClass::callbackfunc, &SomeClassInstance, _1, _2 ) );
+     * \endcode
+     */
+    class WebpinResultFileReader
+    {
+        friend std::ostream & operator<<( std::ostream & str, const WebpinResultFileReader & obj );
+    public:
+      
+     /**
+      * Callback definition.
+      * First parameter is a \ref WebpinResult object.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+        typedef function< bool( const zypp::ws::WebpinResult & )> ProcessWebpinResult;
+      
+    public:
+     /**
+      * \short Constructor. Creates the reader and start reading.
+      *
+      * \param result_file Valid result XML file from Webpin
+      * \param callback Callback that will be called for each repository.
+      * \param progress Optional progress function. \see ProgressData
+      *
+      * \throws AbortRequestException If the callback returns false
+      * \throws Exception If a error occurs at reading / parsing
+      *
+      */
+      WebpinResultFileReader( const Pathname &result_file,
+                      const ProcessWebpinResult & callback/*,
+                      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc()*/);
+     
+      /**
+       * Dtor
+       */
+      ~WebpinResultFileReader();
+    private:
+      class Impl;
+      RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates WebpinResultFileReader Stream output */
+    std::ostream & operator<<( std::ostream & str, const WebpinResultFileReader & obj );
+
+   } //namespace ws
+    /////////////////////////////////////////////////////////////////
+  } // namespace parser
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_WEBPINRESULTFILEREADER_H
diff --git a/zypp/parser/xml/Node.cc b/zypp/parser/xml/Node.cc
new file mode 100644 (file)
index 0000000..e3ac4cd
--- /dev/null
@@ -0,0 +1,92 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/Reader.cc
+ *
+*/
+extern "C"
+{
+#include <libxml/xmlreader.h>
+#include <libxml/xmlerror.h>
+}
+
+#include <iostream>
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/parser/xml/Node.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    xmlTextReaderPtr const Node::_no_reader = 0;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Node::Node
+    // METHOD TYPE : Constructor
+    //
+    Node::Node()
+    : _reader( _no_reader )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Node::Node
+    // METHOD TYPE : Constructor
+    //
+    Node::Node( xmlTextReaderPtr const & reader_r )
+    : _reader( reader_r )
+    {}
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Node & obj )
+    {
+      if ( ! obj )
+        return str << "NoNode" << endl;
+#define X(m) obj.m()
+      str << X(depth) << ":" <<  std::string( X(depth), ' ') << X(nodeType) << " <";
+      if ( X(nodeType) == XML_READER_TYPE_NONE )
+        {
+          return str << '[' << X(readState) << "]>";
+        }
+      if ( obj.prefix() )
+        str << X(prefix) << ':';
+      str << X(localName) << "> ";
+      if ( X(hasAttributes) )
+        str << " [attr " << X(attributeCount);
+      else
+        str << " [noattr";
+      if ( X(isEmptyElement) )
+        str << "|empty";
+      str << ']';
+      if ( X(hasValue) )
+        str << " {" << X(value) << '}';
+#undef X
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/parser/xml/Node.h b/zypp/parser/xml/Node.h
new file mode 100644 (file)
index 0000000..9d82004
--- /dev/null
@@ -0,0 +1,178 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/Node.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_NODE_H
+#define ZYPP_PARSER_XML_NODE_H
+
+#include <iosfwd>
+
+#include "zypp/base/SafeBool.h"
+
+#include "zypp/parser/xml/XmlString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Node
+    //
+    /** xmlTextReader based interface to \ref Reader's current node.
+     * Node provides xmlTextReader methods that do not change
+     * the readers position in the file. Mostly access to the
+     * nodes attributes.
+     **/
+    class Node : private base::SafeBool<Node>
+    {
+    public:
+      /** Default ctor. */
+      Node();
+
+      /** Ctor referencing a \ref Reader. */
+      Node( xmlTextReaderPtr const & reader_r );
+
+      /** Validate Node in a boolean context. \see \ref SafeBool. */
+      using base::SafeBool<Node>::operator bool_type;
+
+    public:
+      /** Provides the number of attributes of the current node. */
+      int attributeCount() const
+      { return xmlTextReaderAttributeCount( _reader ); }
+
+      /** The base URI of the node. */
+      XmlString baseUri() const
+      { return xmlTextReaderConstBaseUri( _reader ); }
+
+      /** Provide the column number of the current parsing point. */
+      int columnNumber() const
+      { return xmlTextReaderGetParserColumnNumber( _reader ); }
+
+      /** The depth of the node in the tree. */
+      int depth() const
+      { return xmlTextReaderDepth( _reader ); }
+
+      /** Determine the encoding of the document being read. */
+      XmlString encoding() const
+      { return xmlTextReaderConstEncoding( _reader ); }
+
+      /** Provides a copy of the attribute value with the specified
+       * qualified name. */
+      XmlString getAttribute( const char * name_r ) const
+      { return XmlString( xmlTextReaderGetAttribute( _reader, reinterpret_cast<const xmlChar *>(name_r) ),
+                          XmlString::FREE ); }
+
+      /** Provides a copy of the attribute value  with the specified
+       * index relative to the containing element. */
+      XmlString getAttributeNo( int no_r ) const
+      { return XmlString( xmlTextReaderGetAttributeNo( _reader, no_r ), XmlString::FREE ); }
+
+      /** Whether the node has attributes. */
+      int hasAttributes() const
+      { return xmlTextReaderHasAttributes( _reader ); }
+
+      /** Whether the node can have a text value. */
+      int hasValue() const
+      { return xmlTextReaderHasValue( _reader ); }
+
+      /** Whether this is an Attribute node. */
+      bool isAttribute() const
+      { return( nodeType() == XML_READER_TYPE_ATTRIBUTE ); }
+
+      /** Whether an Attribute node was generated from the default value
+       *  defined in the DTD or schema. */
+      int isDefault() const
+      { return xmlTextReaderIsDefault( _reader ); }
+
+      /** Check if the current node is empty. */
+      int isEmptyElement() const
+      { return xmlTextReaderIsEmptyElement( _reader ); }
+
+      /** Determine whether the current node is a namespace declaration
+       *  rather than a regular attribute.*/
+      int isNamespaceDecl() const
+      { return xmlTextReaderIsNamespaceDecl( _reader ); }
+
+      /** Provide the line number of the current parsing point. */
+      int lineNumber() const
+      { return xmlTextReaderGetParserLineNumber( _reader ); }
+
+      /** The local name of the node. */
+      XmlString localName() const
+      { return xmlTextReaderConstLocalName( _reader ); }
+
+      /** The qualified name of the node, equal to Prefix :LocalName. */
+      XmlString name() const
+      { return xmlTextReaderConstName( _reader ); }
+
+      /** The URI defining the namespace associated with the node. */
+      XmlString namespaceUri() const
+      { return xmlTextReaderConstNamespaceUri( _reader ); }
+
+      /** Get the node type of the current node. */
+      NodeType nodeType() const
+      { return (NodeType)xmlTextReaderNodeType( _reader ); }
+
+      /** A shorthand reference to the namespace associated with the node. */
+      XmlString prefix() const
+      { return xmlTextReaderConstPrefix( _reader ); }
+
+      /** The quotation mark character used to enclose the value of an attribute.
+       * \return \c " or \c ' or -1 in case of error. */
+      int quoteChar() const
+      { return xmlTextReaderQuoteChar( _reader ); }
+
+      /** Gets the read state of the reader. */
+      ReadState readState() const
+      { return (ReadState)xmlTextReaderReadState( _reader ); }
+
+      /** Provides the text value of the node if present. */
+      XmlString value() const
+      { return xmlTextReaderConstValue( _reader ); }
+
+      /** Provides a copy of the text value of the node if present. */
+      XmlString getValue() const
+      { return XmlString( xmlTextReaderValue( _reader ), XmlString::FREE ); }
+
+      /** The xml:lang scope within which the node resides. */
+      XmlString xmlLang() const
+      { return xmlTextReaderConstXmlLang( _reader ); }
+
+      /** Determine the XML version of the document being read. */
+      XmlString xmlVersion() const
+      { return xmlTextReaderConstXmlVersion( _reader ); }
+
+    private:
+      /** NULL Reader referenced by default ctor. */
+      static xmlTextReaderPtr const _no_reader;
+      /** Reference to the \ref Reader. */
+      xmlTextReaderPtr const & _reader;
+
+    private:
+      friend base::SafeBool<Node>::operator bool_type() const;
+      /** \ref SafeBool test. */
+      bool boolTest() const { return _reader; }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Node Stream output. */
+    std::ostream & operator<<( std::ostream & str, const Node & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_NODE_H
diff --git a/zypp/parser/xml/Parse.h b/zypp/parser/xml/Parse.h
new file mode 100644 (file)
index 0000000..85ffc86
--- /dev/null
@@ -0,0 +1,115 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/parser/xml/Parse.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_PARSE_H
+#define ZYPP_PARSER_XML_PARSE_H
+
+#include <iosfwd>
+
+#include "zypp/parser/xml/Reader.h"
+#include "zypp/parser/xml/ParseDef.h"
+#include "zypp/parser/xml/ParseDefConsume.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    /** Parse xml \c input_r and store data in \c data_r.
+     *
+     * \c _Data must be defaultconstructible and assignable.
+     *
+     * \c _Data::RootNode must be a \ref xml::ParseDef constructible
+     * from \c _Data&.
+     *
+     * \throws ParseDefException on parse errors.
+     *
+     * To parse a xml file like this:
+     * \code
+     * <test>
+     *   <setup attr="13">value</setup>
+     *   <list name="A"/>
+     *   <list name="b"/>
+     * </test>
+     * \endcode
+     *
+     * You need something like this:
+     * \code
+     *  struct XmlData
+     *  {
+     *    // data
+     *    unsigned              attr;
+     *    std::string           value;
+     *    std:list<std::string> names;
+     *
+     *    public:
+     *      // Convenience parsing to *this.
+     *      void parse( const Pathname & path_r )
+     *      { xml::rnParse( path_r, *this ); }
+     *
+     *    public:
+     *      // Parser description
+     *      struct RootNode : public xml::ParseDef
+     *      {
+     *        RootNode( XmlData & data )
+     *          : ParseDef( "test", MANDTAORY )
+     *          , _data( data )
+     *        {
+     *          (*this)
+     *              ("setup", MANDTAORY,
+     *                        xml::parseDefAssign( data.value )
+     *                                           ( "attr", data.attr ) )
+     *              // Each individual list entry is collected locally
+     *              // and appended to the list after the node is done.
+     *              ("list",  MULTIPLE_OPTIONAL,
+     *                        xml::parseDefAssign( "name", _cname )
+     *                                           >> bind( &RootNode::cdone, this, _1 ) )
+     *              ;
+     *        }
+     *
+     *        void cdone( const xml::Node & node_r )
+     *        {
+     *          _data.push_back( _cname );
+     *          _cname.clear(); // prepare for next
+     *        }
+     *
+     *        private:
+     *          XmlData &   _data; // stored just because notification callbacks are used.
+     *          std::string _cname;
+     *      };
+     *  };
+     *
+     *  XmlData xmlData;
+     *  xmlData.parse( "/tmp/mytest.xml" );
+     * \endcode
+     */
+    template<class _Data>
+    inline void rnParse( const InputStream & input_r, _Data & data_r )
+    {
+      typedef typename _Data::RootNode RootNode;
+      _Data pdata;
+
+      xml::Reader reader( input_r );
+      RootNode rootNode( pdata );
+      rootNode.take( reader );
+
+      data_r = pdata;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_PARSEDEF_H
diff --git a/zypp/parser/xml/ParseDef.cc b/zypp/parser/xml/ParseDef.cc
new file mode 100644 (file)
index 0000000..2fdda63
--- /dev/null
@@ -0,0 +1,476 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDef.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <map>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/DtorReset.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/parser/xml/ParseDef.h"
+#include "zypp/parser/xml/ParseDefException.h"
+#include "zypp/parser/xml/ParseDefConsume.h"
+#include "zypp/parser/xml/Reader.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefImplConsume
+    //
+    /** */
+    struct ParseDefImplConsume : public ParseDefConsumeRedirect
+    {
+      virtual void start( const Node & node_r )
+      {
+        debuglog( "START ", node_r );
+        ParseDefConsumeRedirect::start( node_r );
+      }
+
+      virtual void text( const Node & node_r )
+      {
+        debuglog( "TEXT  ", node_r );
+        ParseDefConsumeRedirect::text( node_r );
+      }
+
+      virtual void cdata( const Node & node_r )
+      {
+        debuglog( "CDATA ", node_r );
+        ParseDefConsumeRedirect::cdata( node_r );
+      }
+
+      virtual void done( const Node & node_r )
+      {
+        debuglog( "DONE  ", node_r );
+        ParseDefConsumeRedirect::done( node_r );
+      }
+
+      virtual void startSubnode( const Node & node_r )
+      {
+        debuglog( "--->  ", node_r );
+        ParseDefConsumeRedirect::startSubnode( node_r );
+      }
+
+      virtual void doneSubnode( const Node & node_r )
+      {
+        debuglog( "<---  ", node_r );
+        ParseDefConsumeRedirect::doneSubnode( node_r );
+      }
+
+      void debuglog( const char *const tag_r, const Node & node_r )
+      {
+        if ( ParseDef::_debug )
+          DBG << tag_r << node_r << endl;
+      }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDef::Impl
+    //
+    /** ParseDef implementation.
+     * \todo Check using share_ptr_from_this for parent in addNode.
+    */
+    class ParseDef::Impl
+    {
+      friend std::ostream & operator<<( std::ostream & str, const ParseDef::Impl & obj );
+    public:
+      typedef shared_ptr<Impl>               ImplPtr;
+      typedef std::map<std::string, ImplPtr> SubNodes;
+
+    public:
+      Impl( const std::string & name_r, Mode mode_r, const shared_ptr<ParseDefConsume> & target_r = shared_ptr<ParseDefConsume>() )
+      : _name( name_r )
+      , _mode( mode_r )
+      , _parent( NULL )
+      {
+        if ( target_r )
+          _callback.setRedirect( target_r );
+      }
+
+      ~Impl()
+      {
+        for ( SubNodes::iterator it = _subnodes.begin(); it != _subnodes.end(); ++it )
+          {
+            it->second->_parent = NULL;
+          }
+      }
+
+      bool isOptional() const
+      { return Traits::ModeBits(_mode).isEqual<Traits::TypeBits>( Traits::BIT_OPTIONAL ); }
+
+      bool isMandatory() const
+      { return Traits::ModeBits(_mode).isEqual<Traits::TypeBits>( Traits::BIT_MANDTAORY ); }
+
+      bool singleDef() const
+      { return Traits::ModeBits(_mode).isEqual<Traits::VisitBits>( Traits::BIT_ONCE ); }
+
+      bool multiDef() const
+      { return Traits::ModeBits(_mode).isEqual<Traits::VisitBits>( Traits::BIT_MULTIPLE ); }
+
+    public:
+      void addNode( const ImplPtr & subnode_r );
+
+      ImplPtr getNode( const std::string & name_r ) const
+      {
+        SubNodes::const_iterator it = _subnodes.find( name_r );
+        if ( it != _subnodes.end() )
+          return it->second;
+        return ImplPtr();
+      }
+
+      void take( Reader & reader_r );
+
+    private:
+      /** Skip the current node.
+       * \pre  Current node must be XML_READER_TYPE_ELEMENT.
+       * \post At the corresponding end node.
+               (XML_READER_TYPE_END_ELEMENT or atill at the same node,
+               if it'a an empty element <tt>&lt;node /&gt;</tt>).
+       * \return last call to xml::Reader::nextNode.
+       * \throws ParseDefValidateException if no matching end node found.
+      */
+      bool skipNode( Reader & reader_r );
+
+      std::string exstr( const std::string & what_r, const Impl & impl_r ) const
+      {
+        std::ostringstream str;
+        str << impl_r << ": " << what_r;
+        return str.str();
+      }
+      std::string exstr( const std::string & what_r, const Impl & impl_r, const Reader & reader_r ) const
+      {
+        std::ostringstream str;
+        str << impl_r << ": " << what_r << " |reading " << *reader_r;
+        return str.str();
+      }
+
+    public:
+      std::string                 _name;
+      Mode                        _mode;
+      DefaultIntegral<unsigned,0> _visited;
+
+      Impl *                      _parent;
+      SubNodes                    _subnodes;
+      ParseDefImplConsume         _callback;
+
+      DefaultIntegral<int,-1>     _parseDepth;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDef::Impl::addNode
+    // METHOD TYPE : void
+    //
+    void ParseDef::Impl::addNode( const ImplPtr & subnode_r )
+    {
+      std::pair<SubNodes::iterator, bool> res
+      = _subnodes.insert( std::make_pair( subnode_r->_name, subnode_r ) );
+
+      if ( ! res.second )
+        {
+          ZYPP_THROW( ParseDefBuildException( exstr("Multiple definiton of subnode "+subnode_r->_name, *this) ) );
+        }
+      if ( res.first->second->_parent )
+        {
+          ZYPP_THROW( ParseDefBuildException( exstr("Can not reparent subnode "+subnode_r->_name, *this) ) );
+        }
+      res.first->second->_parent = this;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDef::Impl::take
+    // METHOD TYPE : void
+    //
+    void ParseDef::Impl::take( Reader & reader_r )
+    {
+      if ( reader_r->nodeType() != XML_READER_TYPE_ELEMENT )
+        {
+          if ( reader_r->depth() == 0 )
+          {
+            // on the verry first level we skip any initial whitespace and comments...
+            do {
+              // advance to next node
+              if ( ! reader_r.nextNode() )
+                {
+                  ZYPP_THROW( ParseDefValidateException( exstr( "Unexpected EOF ", *this ) ) );
+                }
+            } while( reader_r->nodeType() != XML_READER_TYPE_ELEMENT );
+          }
+          else
+          {
+            ZYPP_THROW( ParseDefValidateException( exstr("Expected ELEMENT", *this, reader_r) ) );
+          }
+        }
+      if ( reader_r->name() != _name )
+        {
+          ZYPP_THROW( ParseDefValidateException( exstr("Wrong ELEMENT name", *this, reader_r) ) );
+        }
+      if ( _visited >= 1 && ! multiDef() )
+        {
+          ZYPP_THROW( ParseDefValidateException( exstr("Multiple definitions", *this, reader_r) ) );
+        }
+
+      ++_visited; // Accepted to parse
+      DtorReset x( _parseDepth, -1 );
+      _parseDepth = reader_r->depth();
+
+      // Parse attributes
+      _callback.start( *reader_r );
+
+      // Get content up to end node
+      // Empty element (<node />) has no separate end node, so
+      // there's nothing to parse.
+      if ( ! reader_r->isEmptyElement() )
+        {
+          // For non empty elements (<node></node>) parse known nodes
+          // text and cdata elelments skip unknown nodes.
+          for ( bool done = false; ! done ; /*advance in inside loop*/)
+            {
+              // advance to next node
+              if ( ! reader_r.nextNode() )
+                {
+                  ZYPP_THROW( ParseDefValidateException( exstr( "Unexpected EOF ", *this ) ) );
+                }
+
+              switch ( reader_r->nodeType() )
+                {
+                case XML_READER_TYPE_ELEMENT:
+                  // Parse or skip unknown. Anyway reader is located at the
+                  // corresponding end node, or an exception was thrown.
+                  {
+                    ImplPtr sub( getNode( reader_r->name().asString() ) );
+                    if ( sub )
+                      {
+                        _callback.startSubnode( *reader_r );
+                        sub->take( reader_r );
+                        _callback.doneSubnode( *reader_r );
+                      }
+                    else
+                      {
+                        if ( ParseDef::_debug )
+                          WAR << "Skip unknown node " << *reader_r << " in "<< *this << endl;
+                        skipNode( reader_r );
+                      }
+                  }
+                break;
+
+                case XML_READER_TYPE_END_ELEMENT:
+                  // This must be the corresponding end node!
+                  if ( reader_r->depth() == _parseDepth
+                       && reader_r->name() == _name )
+                    {
+                      done = true;
+                    }
+                  else
+                    {
+                      ZYPP_THROW( ParseDefValidateException( exstr("unexpected END_ELEMENT name", *this, reader_r) ) );
+                    }
+                  break;
+
+                case XML_READER_TYPE_TEXT:
+                  // collect or skip
+                  _callback.text( *reader_r );
+                  break;
+
+                case XML_READER_TYPE_CDATA:
+                  // collect or skip
+                  _callback.cdata( *reader_r );
+                  break;
+
+                default:
+                  //DBG << exstr("SKIP ", *this, reader_r) << endl;
+                  break;
+                }
+            }
+        }
+
+      // Parsing complete. Check whether all mandatory nodes were
+      // present. Finally position behind the end node.
+      for ( SubNodes::iterator it = _subnodes.begin(); it != _subnodes.end(); ++it )
+        {
+          if ( ! it->second->_visited && it->second->isMandatory() )
+            {
+              ZYPP_THROW( ParseDefValidateException( exstr("Mandatory ELEMENT missing", *(it->second), reader_r) ) );
+            }
+          it->second->_visited = 0; // reset to be ready for an other visit to this!!
+        }
+
+      _callback.done( *reader_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDef::Impl::skipNode
+    // METHOD TYPE : void
+    //
+    bool ParseDef::Impl::skipNode( xml::Reader & reader_r )
+    {
+      if ( ! reader_r.seekToEndNode( reader_r->depth(),
+                                     reader_r->name().asString() ) )
+        {
+          ZYPP_THROW( ParseDefValidateException
+                      ( exstr( str::form( "EOF while looking for [%d] <\\%s>",
+                                          reader_r->depth(),
+                                          reader_r->name().c_str() ),
+                               *this ) ) );
+        }
+      return true;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const ParseDef::Impl & obj )
+    {
+      return str << "ParseDef(" << obj._name
+                 << ", " << obj._mode
+                 << ", visits " << obj._visited
+                 << ")";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDef
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    bool ParseDef::_debug = false;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDef::ParseDef
+    // METHOD TYPE : Ctor
+    //
+    ParseDef::ParseDef( const std::string & name_r, Mode mode_r )
+    : _pimpl( new Impl( name_r, mode_r ) )
+    {}
+
+    ParseDef::ParseDef( const std::string & name_r, Mode mode_r, const shared_ptr<ParseDefConsume> & target_r )
+    : _pimpl( new Impl( name_r, mode_r, target_r ) )
+    {}
+
+    ParseDef::ParseDef( const shared_ptr<Impl> & pimpl_r )
+    : _pimpl( pimpl_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDef::~ParseDef
+    // METHOD TYPE : Dtor
+    //
+    ParseDef::~ParseDef()
+    {}
+
+    const std::string & ParseDef::name() const
+    { return _pimpl->_name; }
+
+    ParseDef::Mode ParseDef::mode() const
+    { return _pimpl->_mode; }
+
+    bool ParseDef::isOptional() const
+    { return _pimpl->isOptional(); }
+
+    bool ParseDef::isMandatory() const
+    { return _pimpl->isMandatory(); }
+
+    bool ParseDef::singleDef() const
+    { return _pimpl->singleDef(); }
+
+    bool ParseDef::multiDef() const
+    { return _pimpl->multiDef(); }
+
+    unsigned ParseDef::visited() const
+    { return _pimpl->_visited; }
+
+    ParseDef & ParseDef::addNode( ParseDef & subnode_r )
+    { _pimpl->addNode( subnode_r._pimpl.getPtr() ); return *this; }
+
+    ParseDef ParseDef::operator[]( const std::string & name_r )
+    {
+      shared_ptr<Impl> retimpl( _pimpl->getNode( name_r ) );
+      if ( ! retimpl )
+        {
+          ZYPP_THROW( ParseDefBuildException( "No subnode "+name_r ) );
+        }
+      return retimpl;
+    }
+
+    void ParseDef::setConsumer( const shared_ptr<ParseDefConsume> & target_r )
+    { _pimpl->_callback.setRedirect( target_r ); }
+
+    void ParseDef::setConsumer( ParseDefConsume * allocatedTarget_r )
+    { _pimpl->_callback.setRedirect( allocatedTarget_r ); }
+
+    void ParseDef::setConsumer( ParseDefConsume & target_r )
+    { _pimpl->_callback.setRedirect( target_r ); }
+
+    void ParseDef::cancelConsumer()
+    { _pimpl->_callback.cancelRedirect(); }
+
+    shared_ptr<ParseDefConsume> ParseDef::getConsumer() const
+    { return _pimpl->_callback.getRedirect(); }
+
+
+    void ParseDef::take( Reader & reader_r )
+    { _pimpl->take( reader_r ); }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, ParseDef::Mode obj )
+    {
+      switch ( obj )
+        {
+#define X(T) case ParseDef::T: return str << #T
+          X(OPTIONAL);
+          X(MANDTAORY);
+          X(MULTIPLE_OPTIONAL);
+          X(MULTIPLE_MANDTAORY);
+#undef X
+        }
+      return str;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const ParseDef & obj )
+    {
+      return str << obj._pimpl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/ParseDef.h b/zypp/parser/xml/ParseDef.h
new file mode 100644 (file)
index 0000000..44b5261
--- /dev/null
@@ -0,0 +1,245 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDef.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_PARSEDEF_H
+#define ZYPP_PARSER_XML_PARSEDEF_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/parser/xml/ParseDefTraits.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    class Reader;
+    class ParseDefConsume;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDef
+    //
+    /** Define a xml node structure to parse.
+     *
+     * An xml file like this:
+     * \code
+     * <?xml version="1.0" encoding="UTF-8"?>
+     * <syscontent>
+     *   <ident>
+     *     <name>mycollection</name>
+     *     <version epoch="0" ver="1.0" rel="1"/>
+     *     <description>All the cool stuff...</description>
+     *     <created>1165270942</created>
+     *   </ident>
+     *   <onsys>
+     *     <entry kind="package" name="pax" epoch="0" ver="3.4" rel="12" arch="x86_64"/>
+     *     <entry kind="product" name="SUSE_SLES" epoch="0" ver="10" arch="x86_64"/>
+     *     <entry ...
+     *   </onsys>
+     * </syscontent>
+     * \endcode
+     *
+     * Could be described by:
+     * \code
+     * using namespace xml;
+     * struct SycontentNode : public ParseDef
+     * {
+     *   SycontentNode( Mode mode_r )
+     *   : ParseDef( "syscontent", mode_r )
+     *   {
+     *     (*this)("ident",       OPTIONAL)
+     *            ("onsys",       OPTIONAL)
+     *            ;
+     *
+     *     (*this)["ident"]
+     *            ("name",        OPTIONAL)
+     *            ("version",     OPTIONAL)
+     *            ("description", OPTIONAL)
+     *            ("created",     OPTIONAL)
+     *            ;
+     *
+     *     (*this)["onsys"]
+     *            ("entry",       MULTIPLE_OPTIONAL)
+     *            ;
+     *   }
+     * };
+     * \endcode
+     *
+     * To parse it using an \ref xml::Reader:
+     * \code
+     * xml::Reader reader( input_r );
+     * SycontentNode rootNode( xml::ParseDef::MANDTAORY );
+     * // Define data consumers here.
+     * rootNode.take( reader );
+     * \endcode
+     *
+     * Whithout data consumers this will just parse the file
+     * but not retrieve any data. You may attach a consumer
+     * derived from \ref xml::ParseDefConsume to each node:
+     *
+     * \code
+     * // Parse Edition from ver/rel/eopch attributes.
+     * struct ConsumeEdition : public ParseDefConsume
+     * {
+     *   ConsumeEdition( Edition & value_r )
+     *   : _value( & value_r )
+     *   {}
+     *
+     *   virtual void start( const Node & node_r )
+     *   {
+     *     *_value = Edition( node_r.getAttribute("ver").asString(),
+     *                        node_r.getAttribute("rel").asString(),
+     *                        node_r.getAttribute("epoch").asString() );
+     *   }
+     *
+     *   Edition *_value;
+     * };
+     * \endcode
+     * \see \ref xml::ParseDefConsume
+     *
+     * \code
+     * xml::Reader reader( input_r );
+     * SycontentNode rootNode( xml::ParseDef::MANDTAORY );
+     *
+     * // Define data consumers here.
+     * Edition _edition;
+     * rootNode["ident"]["version"].setConsumer
+     * ( new ConsumeEdition( _edition ) );
+     *
+     * rootNode.take( reader );
+     * \endcode
+     *
+     * That's just one way to collect the data. You could as well
+     * use a \ref xml::ParseDefConsumeCallback, and redirect the
+     * \c start call to some arbitrary function or method.
+    */
+    class ParseDef
+    {
+      typedef ParseDefTraits Traits;
+
+    public:
+      enum Mode
+        {
+          OPTIONAL           = Traits::BIT_OPTIONAL  | Traits::BIT_ONCE,
+          MANDTAORY          = Traits::BIT_MANDTAORY | Traits::BIT_ONCE,
+          MULTIPLE_OPTIONAL  = Traits::BIT_OPTIONAL  | Traits::BIT_MULTIPLE,
+          MULTIPLE_MANDTAORY = Traits::BIT_MANDTAORY | Traits::BIT_MULTIPLE
+        };
+
+    public:
+      ParseDef( const std::string & name_r, Mode mode_r );
+      ParseDef( const std::string & name_r, Mode mode_r, const shared_ptr<ParseDefConsume> & target_r );
+
+      virtual ~ParseDef();
+
+    public:
+      const std::string & name() const;
+      Mode mode() const;
+      bool isOptional() const;
+      bool isMandatory() const;
+      bool singleDef() const;
+      bool multiDef() const;
+      unsigned visited() const;
+
+    public:
+      /** Add subnode definition.
+       * \note As ParseDef copies share their implementation you can
+       *       not add the same subnode to multiple parents.
+       * \return <tt>*this</tt>.
+       * \throws ParseDefBuildException if a subnode with the same name
+       *         is already defined, or if the subnode is already
+       *         subnode of an other ParseDef.
+      */
+      ParseDef & addNode( ParseDef & subnode_r );
+
+      ParseDef & addNode( const std::string & name_r, Mode mode_r )
+      { ParseDef tmp( name_r, mode_r ); return addNode( tmp ); }
+
+      ParseDef & addNode( const std::string & name_r, Mode mode_r, const shared_ptr<ParseDefConsume> & target_r )
+      { ParseDef tmp( name_r, mode_r, target_r ); return addNode( tmp ); }
+
+      /** Add subnode definition.
+       * \see addNode.
+       */
+      ParseDef & operator()( ParseDef & subnode_r )
+      { return addNode( subnode_r ); }
+
+      ParseDef & operator()( const std::string & name_r, Mode mode_r )
+      { return addNode( name_r, mode_r ); }
+
+      ParseDef & operator()( const std::string & name_r, Mode mode_r, const shared_ptr<ParseDefConsume> & target_r )
+      { return addNode( name_r, mode_r, target_r ); }
+
+      /** Get subnode by name.
+       * \throws ParseDefBuildException if no subnode with \a name_r exists.
+       */
+      ParseDef operator[]( const std::string & name_r );
+
+    public:
+      /** Set data consumer. */
+      void setConsumer( const shared_ptr<ParseDefConsume> & target_r );
+      /** Set data consumer.
+       * \note \a allocatedTarget_r is immediately wraped into a
+       *       shared_ptr.
+      */
+      void setConsumer( ParseDefConsume * allocatedTarget_r );
+      /** Set data consumer. */
+      void setConsumer( ParseDefConsume & target_r );
+      /** Unset data consumer. */
+      void cancelConsumer();
+
+      /** Get data consumer. */
+      shared_ptr<ParseDefConsume> getConsumer() const;
+
+      /** Parse the node.
+       * This parses the node and all defined subnodes. Unknown
+       * subnodes are skipped and leave a warning in the logfile.
+       * \pre  Current node must be XML_READER_TYPE_ELEMENT matching
+       *       this ParseDefs name.
+       * \post All data parsed. At the corresponding end node.
+       *       (XML_READER_TYPE_END_ELEMENT or atill at the same node,
+       *       if it'a an empty element <tt>&lt;node /&gt;</tt>).
+       * \throws ParseDefException on error.
+      */
+      void take( Reader & reader_r );
+
+    private:
+      /** Implementation  */
+      class Impl;
+      /** Pointer to implementation (shared!) */
+      RW_pointer<Impl> _pimpl;
+
+      ParseDef( const shared_ptr<Impl> & pimpl_r );
+      friend std::ostream & operator<<( std::ostream & str, const ParseDef & obj );
+      friend std::ostream & operator<<( std::ostream & str, const ParseDef::Impl & obj );
+
+    public:
+      static bool _debug;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ParseDef ParseDef::Mode stream output. */
+    std::ostream & operator<<( std::ostream & str, ParseDef::Mode obj );
+
+    /** \relates ParseDef Stream output. */
+    std::ostream & operator<<( std::ostream & str, const ParseDef & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_PARSEDEF_H
diff --git a/zypp/parser/xml/ParseDefConsume.cc b/zypp/parser/xml/ParseDefConsume.cc
new file mode 100644 (file)
index 0000000..be7bbc4
--- /dev/null
@@ -0,0 +1,176 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDefConsume.cc
+ *
+*/
+#include "zypp/parser/xml/ParseDefConsume.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsume
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ParseDefConsume::~ParseDefConsume()
+    {}
+
+    void ParseDefConsume::start( const Node & _node )
+    {}
+
+    void ParseDefConsume::text( const Node & _node )
+    {}
+
+    void ParseDefConsume::cdata( const Node & _node )
+    {}
+
+    void ParseDefConsume::done( const Node & _node )
+    {}
+
+    void ParseDefConsume::startSubnode( const Node & _node )
+    {}
+
+    void ParseDefConsume::doneSubnode( const Node & _node )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsumeRedirect
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ParseDefConsumeRedirect::ParseDefConsumeRedirect()
+    {}
+
+    ParseDefConsumeRedirect::ParseDefConsumeRedirect( const shared_ptr<ParseDefConsume> & target_r )
+    : _target( target_r )
+    {}
+
+    ParseDefConsumeRedirect::ParseDefConsumeRedirect( ParseDefConsume * allocatedTarget_r )
+    : _target( allocatedTarget_r )
+    {}
+
+    ParseDefConsumeRedirect::ParseDefConsumeRedirect( ParseDefConsume & target_r )
+    : _target( &target_r, NullDeleter() )
+    {}
+
+    ParseDefConsumeRedirect::~ParseDefConsumeRedirect()
+    {}
+
+    void ParseDefConsumeRedirect::setRedirect( const shared_ptr<ParseDefConsume> & target_r )
+    { _target = target_r; }
+
+    void ParseDefConsumeRedirect::setRedirect( ParseDefConsume * allocatedTarget_r )
+    { _target.reset( allocatedTarget_r ); }
+
+    void ParseDefConsumeRedirect::setRedirect( ParseDefConsume & target_r )
+    { _target.reset( &target_r, NullDeleter() ); }
+
+    void ParseDefConsumeRedirect::cancelRedirect()
+    { _target.reset(); }
+
+    shared_ptr<ParseDefConsume> ParseDefConsumeRedirect::getRedirect() const
+    { return _target; }
+
+    void ParseDefConsumeRedirect::start( const Node & _node )
+    {
+      if ( _target )
+        _target->start( _node );
+    }
+
+    void ParseDefConsumeRedirect::text( const Node & _node )
+    {
+      if ( _target )
+        _target->text( _node );
+    }
+
+    void ParseDefConsumeRedirect::cdata( const Node & _node )
+    {
+      if ( _target )
+        _target->cdata( _node );
+    }
+
+    void ParseDefConsumeRedirect::done( const Node & _node )
+    {
+      if ( _target )
+        _target->done( _node );
+    }
+
+    void ParseDefConsumeRedirect::startSubnode( const Node & _node )
+    {
+      if ( _target )
+        _target->startSubnode( _node );
+    }
+
+    void ParseDefConsumeRedirect::doneSubnode ( const Node & _node )
+    {
+      if ( _target )
+        _target->doneSubnode( _node );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsumeCallback
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ParseDefConsumeCallback::ParseDefConsumeCallback()
+    {}
+
+    ParseDefConsumeCallback::~ParseDefConsumeCallback()
+    {}
+
+    void ParseDefConsumeCallback::start( const Node & node_r )
+    {
+      if ( _start )
+        _start( node_r );
+    }
+
+    void ParseDefConsumeCallback::text( const Node & node_r )
+    {
+      if ( _text )
+        _text( node_r );
+    }
+
+    void ParseDefConsumeCallback::cdata( const Node & node_r )
+    {
+      if ( _cdata )
+        _cdata( node_r );
+    }
+
+    void ParseDefConsumeCallback::done( const Node & node_r )
+    {
+      if ( _done )
+        _done( node_r );
+    }
+
+    void ParseDefConsumeCallback::startSubnode( const Node & node_r )
+    {
+      if ( _startSubnode )
+        _startSubnode( node_r );
+    }
+
+    void ParseDefConsumeCallback::doneSubnode( const Node & node_r )
+    {
+      if ( _doneSubnode )
+        _doneSubnode( node_r );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/ParseDefConsume.h b/zypp/parser/xml/ParseDefConsume.h
new file mode 100644 (file)
index 0000000..2f75dc9
--- /dev/null
@@ -0,0 +1,374 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDefConsume.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_PARSEDEFCONSUME_H
+#define ZYPP_PARSER_XML_PARSEDEFCONSUME_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Tr1hash.h"
+#include "zypp/base/String.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/parser/xml/Node.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    class Node;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsume
+    //
+    /** Base class for ParseDef consumer.
+     */
+    struct ParseDefConsume
+    {
+      virtual ~ParseDefConsume();
+
+      virtual void start( const Node & _node );
+      virtual void text ( const Node & _node );
+      virtual void cdata( const Node & _node );
+      virtual void done ( const Node & _node );
+
+      virtual void startSubnode( const Node & _node );
+      virtual void doneSubnode ( const Node & _node );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsumeRedirect
+    //
+    /** ParseDef consumer redirecting all events to another consumer.
+     * \note Allocated <tt>ParseDefConsume *</tt> passed are
+     *       immediately wraped into a shared_ptr.
+    */
+    class ParseDefConsumeRedirect : public ParseDefConsume
+    {
+    public:
+      ParseDefConsumeRedirect();
+      ParseDefConsumeRedirect( const shared_ptr<ParseDefConsume> & target_r );
+      ParseDefConsumeRedirect( ParseDefConsume * allocatedTarget_r );
+      ParseDefConsumeRedirect( ParseDefConsume & target_r );
+
+      virtual ~ParseDefConsumeRedirect();
+
+    public:
+      void setRedirect( const shared_ptr<ParseDefConsume> & target_r );
+      void setRedirect( ParseDefConsume * allocatedTarget_r );
+      void setRedirect( ParseDefConsume & target_r );
+      void cancelRedirect();
+
+      shared_ptr<ParseDefConsume> getRedirect() const;
+
+    public:
+      virtual void start( const Node & _node );
+      virtual void text ( const Node & _node );
+      virtual void cdata( const Node & _node );
+      virtual void done ( const Node & _node );
+      virtual void startSubnode( const Node & _node );
+      virtual void doneSubnode ( const Node & _node );
+
+    private:
+      shared_ptr<ParseDefConsume> _target;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefConsumeCallback
+    //
+    /** ParseDef consumer that invokes callbacks.
+    */
+    class ParseDefConsumeCallback : public ParseDefConsume
+    {
+    public:
+      typedef function<void(const Node &)> Callback;
+
+      ParseDefConsumeCallback();
+
+      virtual ~ParseDefConsumeCallback();
+
+    public:
+      virtual void start( const Node & node_r );
+      virtual void text( const Node & node_r );
+      virtual void cdata( const Node & node_r );
+      virtual void done( const Node & node_r );
+      virtual void startSubnode( const Node & node_r );
+      virtual void doneSubnode( const Node & node_r );
+
+    public:
+      Callback _start;
+      Callback _text;
+      Callback _cdata;
+      Callback _done;
+      Callback _startSubnode;
+      Callback _doneSubnode;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /** \ref parseDefAssign exposed details */
+    namespace parse_def_assign
+    { /////////////////////////////////////////////////////////////////
+     template <class _Type> struct Assigner;
+
+      typedef shared_ptr<Assigner<void> > AssignerRef;
+
+      /** Common interface to all Assigner types. */
+      template <>
+          struct Assigner<void>
+      {
+        virtual ~Assigner()
+        {}
+        virtual void assign( const char * text_r )
+        {}
+      };
+
+      /** Assigner assigns text to types constructible from \c char*.
+       * \see \ref assigner consvenience constructor.
+      */
+      template <class _Type>
+          struct Assigner : public Assigner<void>
+      {
+        Assigner(_Type & value_r )
+          : _value( &value_r )
+        {}
+
+        virtual void assign( const char * text_r )
+        { *_value = _Type( text_r ); }
+
+        private:
+          _Type * _value;
+      };
+
+      /** \name Assigner specialisation for numeric and boolean values.
+       *  \relates Assigner
+       */
+      //@{
+      template <>
+          inline void Assigner<short>::assign( const char * text_r )              { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<int>::assign( const char * text_r )                { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<long>::assign( const char * text_r )               { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<long long>::assign( const char * text_r )          { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<unsigned short>::assign( const char * text_r )     { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<unsigned>::assign( const char * text_r )           { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<unsigned long>::assign( const char * text_r )      { str::strtonum( text_r, *_value ); }
+      template <>
+          inline void Assigner<unsigned long long>::assign( const char * text_r ) { str::strtonum( text_r, *_value ); }
+
+      template <>
+          inline void Assigner<bool>::assign( const char * text_r ) { str::strToBoolNodefault( text_r, *_value ); }
+      //@}
+
+      /** \name \relates Assigner Convenience constructor */
+      //@{
+      template <class _Type>
+          inline AssignerRef assigner( _Type & value_r )
+      { return AssignerRef( new Assigner<_Type>( value_r ) ); }
+
+      template <class _Tp, _Tp _Initial>
+          inline AssignerRef assigner( DefaultIntegral<_Tp,_Initial> & value_r )
+      { return AssignerRef( new Assigner<_Tp>( value_r.get() ) ); }
+      //@}
+
+
+      /** \ref ParseDef consumer assigning \ref Node text and attribues values to variables.
+       *
+       * This can be used with all types supported by \ref Assigner.
+       * Basically all types constructible from \c char*, or where a
+       * specialisation exists (e.g. numeric and bool).
+       *
+       * You may also set a <tt>void( const Node & )</tt> notification
+       * callback which is invoked after the node was processed.
+       *
+       * \note Use and see \ref xml::parseDefAssign convenience constructor.
+       *
+       * \code
+       * // parsedef for '<setup attr="13">value</setup>'
+       * ParseDef( "attr", MANDTAORY, xml::parseDefAssign( data.value )
+       *                                                 ( "attr", data.attr ) )
+       * \endcode
+       */
+      struct Consumer : public ParseDefConsume
+      {
+        /** Extend \ref Consumer. */
+        void add( const AssignerRef & assigner_r )
+        { _text.push_back( assigner_r ); }
+
+        /** Extend \ref Consumer. */
+        void add( const std::string & attr_r, const AssignerRef & assigner_r )
+        { _attr[attr_r].push_back( assigner_r ); }
+
+        /** Set pre notification callback. */
+        void prenotify( function<void ( const Node & )> pre_r )
+        { _pre = pre_r; }
+
+        /** Set post notification callback. */
+        void postnotify( function<void ( const Node & )> post_r )
+        { _post = post_r; }
+
+        virtual void start( const xml::Node & node_r )
+        {
+          if ( _pre )
+            _pre( node_r );
+
+          if ( ! _attr.empty() )
+            for_( it, _attr.begin(), _attr.end() )
+              assign( it->second, node_r.getAttribute( it->first.c_str() ).c_str() );
+        }
+
+        virtual void text( const xml::Node & node_r )
+        {
+          if ( ! _text.empty() )
+            assign( _text, node_r.value().c_str() );
+        }
+
+        virtual void done( const xml::Node & node_r )
+        {
+          if ( _post )
+            _post( node_r );
+        }
+
+        private:
+          void assign( const std::vector<AssignerRef> & vec_r, const char * value_r )
+          {
+            if ( value_r )
+              for_( it, vec_r.begin(), vec_r.end() )
+                (*it)->assign( value_r );
+          }
+
+        private:
+          std::tr1::unordered_map<std::string, std::vector<AssignerRef> > _attr;
+          std::vector<AssignerRef>                                        _text;
+          function<void ( const Node & )>                                 _pre;
+          function<void ( const Node & )>                                 _post;
+      };
+
+      /** Helper class to build a \ref Consumer.
+       * \relates Consumer
+       *
+       * The class constructs the consumer, allows to extend it via
+       * \ref operator(), and provides a conversion to
+       * \c shared_ptr<ParseDefConsume>, so it can be passed as a
+       * node consumer to \ref ParseDef.
+       *
+       * You may also set a <tt>void( const Node & )</tt> notification
+       * callback which is invoked before/after the node was processed.
+       *
+       * \note Use and see \ref xml::parseDefAssign convenience constructor.
+      */
+      struct Builder
+      {
+        /** Contruct \ref Consumer. */
+        Builder()
+          : _ptr( new Consumer )
+        {}
+
+        /** Contruct \ref Consumer. */
+        template <class _Type>
+            Builder( _Type & value_r )
+          : _ptr( new Consumer )
+        { operator()( value_r ); }
+
+        /** Contruct \ref Consumer. */
+        template <class _Type>
+            Builder( const std::string & attr_r, _Type & value_r )
+          : _ptr( new Consumer )
+        {  operator()( attr_r, value_r ); }
+
+        /** Extend \ref Consumer. */
+        template <class _Type>
+            Builder & operator()( _Type & value_r )
+        { _ptr->add( assigner( value_r ) ); return *this; }
+
+        /** Extend \ref Consumer. */
+        template <class _Type>
+            Builder & operator()( const std::string & attr_r, _Type & value_r )
+        { _ptr->add( attr_r, assigner( value_r ) ); return *this; }
+
+        /** Set pre notification callback. */
+        Builder & operator<<( function<void ( const Node & )> done_r )
+        { _ptr->prenotify( done_r ); return *this; }
+
+        /** Set post notification callback. */
+        Builder & operator>>( function<void ( const Node & )> done_r )
+        { _ptr->postnotify( done_r ); return *this; }
+
+        /** Type conversion so this can be passed as node consumer to \ref ParseDef. */
+        operator shared_ptr<ParseDefConsume> () const
+        { return _ptr; }
+
+        private:
+          shared_ptr<Consumer> _ptr;
+      };
+      /////////////////////////////////////////////////////////////////
+    } // namespace parse_def_assign
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name \ref ParseDef consumer assigning \ref Node text and attribues values to variables.
+     * \relates parse_def_assign::Consumer
+     * \relates parse_def_assign::Builder
+     *
+     * This function allows convenient contruction of a \ref parse_def_assign::Consumer
+     * to be passed as \ref Node conssumer to \ref ParseDef. Simply list each attributes
+     * name together with the variable it's value should be assigned to. If the attribute
+     * name is omitted, the nodes text value gets assigned.
+     *
+     * Target variables can be of any type tsupported by \ref Assigner.
+     * Basically all types constructible from \c char*, or where a
+     * specialisation exists (e.g. numeric and bool).
+     *
+     * \code
+     * void setupDone( const xml::Node & _node )
+     * { ... }
+     *
+     * // parsedef for '<setup attr="13">value</setup>'
+     * ParseDef( "attr", MANDTAORY,
+     *           xml::parseDefAssign( data.value )
+     *                              ( "attr", data.attr )
+     *                              >> &setupDone       );
+     * \endcode
+     *
+     * \see \ref xml::rnParse for more example.
+     */
+    //@{
+    inline parse_def_assign::Builder parseDefAssign()
+    { return parse_def_assign::Builder(); }
+
+    template <class _Type>
+        inline parse_def_assign::Builder parseDefAssign( _Type & value_r )
+    { return parse_def_assign::Builder( value_r ); }
+
+    template <class _Type>
+        inline parse_def_assign::Builder parseDefAssign( const std::string & attr_r, _Type & value_r )
+    { return parse_def_assign::Builder( attr_r, value_r ); }
+    //@}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_PARSEDEFCONSUME_H
diff --git a/zypp/parser/xml/ParseDefException.cc b/zypp/parser/xml/ParseDefException.cc
new file mode 100644 (file)
index 0000000..45b3f13
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDefException.cc
+ *
+*/
+#include "zypp/parser/xml/ParseDefException.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDefException::ParseDefException
+    // METHOD TYPE : Constructor
+    //
+    ParseDefException::ParseDefException( const std::string & what_r )
+    : Exception( what_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDefBuildException::ParseDefBuildException
+    // METHOD TYPE : Constructor
+    //
+    ParseDefBuildException::ParseDefBuildException( const std::string & what_r )
+    : ParseDefException( what_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDefValidateException::ParseDefValidateException
+    // METHOD TYPE : Constructor
+    //
+    ParseDefValidateException::ParseDefValidateException( const std::string & what_r )
+    : ParseDefException( what_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ParseDefDataException::ParseDefDataException
+    // METHOD TYPE : Constructor
+    //
+    ParseDefDataException::ParseDefDataException( const std::string & what_r )
+    : ParseDefException( what_r )
+    {}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/ParseDefException.h b/zypp/parser/xml/ParseDefException.h
new file mode 100644 (file)
index 0000000..e203e75
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDefException.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_PARSEDEFEXCEPTION_H
+#define ZYPP_PARSER_XML_PARSEDEFEXCEPTION_H
+
+#include <string>
+
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefException
+    //
+    /** Common base class for \ref ParseDef exceptions. */
+    struct ParseDefException : public Exception
+    {
+      ParseDefException( const std::string & what_r );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefBuildException
+    //
+    /** Exceptions when building a ParseDef tree. */
+    struct ParseDefBuildException : public ParseDefException
+    {
+      ParseDefBuildException( const std::string & what_r );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefValidateException
+    //
+    /** Parse exceptions related to the documents node structure. */
+    struct ParseDefValidateException : public ParseDefException
+    {
+      ParseDefValidateException( const std::string & what_r );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefDataException
+    //
+    /** Parse exceptions related to the nodes content. */
+    struct ParseDefDataException : public ParseDefException
+    {
+      ParseDefDataException( const std::string & what_r );
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+    ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_PARSEDEFEXCEPTION_H
diff --git a/zypp/parser/xml/ParseDefTraits.h b/zypp/parser/xml/ParseDefTraits.h
new file mode 100644 (file)
index 0000000..e7a6673
--- /dev/null
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/xml/ParseDefTraits.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_PARSEDEFTRAITS_H
+#define ZYPP_PARSER_XML_PARSEDEFTRAITS_H
+
+#include "zypp/Bit.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ParseDefTraits
+    //
+    /** */
+    struct ParseDefTraits
+    {
+      typedef unsigned char                              ModeBitsType;
+      typedef bit::BitField<ModeBitsType>                ModeBits;
+      typedef bit::Range<ModeBitsType, 0,             1> TypeBits;
+      typedef bit::Range<ModeBitsType, TypeBits::end, 1> VisitBits;
+
+      enum TypeValue
+        {
+          BIT_OPTIONAL  = bit::RangeValue<TypeBits,0>::value,
+          BIT_MANDTAORY = bit::RangeValue<TypeBits,1>::value
+        };
+
+      enum VisitValue
+        {
+          BIT_ONCE      = bit::RangeValue<VisitBits,0>::value,
+          BIT_MULTIPLE  = bit::RangeValue<VisitBits,1>::value
+        };
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_PARSEDEFTRAITS_H
diff --git a/zypp/parser/xml/Reader.cc b/zypp/parser/xml/Reader.cc
new file mode 100644 (file)
index 0000000..4e514f4
--- /dev/null
@@ -0,0 +1,258 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/Reader.cc
+ *
+*/
+extern "C"
+{
+#include <libxml/xmlreader.h>
+#include <libxml/xmlerror.h>
+}
+
+#include <iostream>
+
+#include "zypp/base/LogControl.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/String.h"
+
+#include "zypp/parser/xml/Reader.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      int ioread( void * context_r, char * buffer_r, int bufferLen_r )
+      {
+        if ( context_r && buffer_r )
+          {
+            return reinterpret_cast<InputStream *>(context_r)
+                   ->stream().read( buffer_r, bufferLen_r ).gcount();
+          }
+        INT << "XML parser error: null pointer check failed " << context_r << ' ' << (void *)buffer_r << endl;
+        return -1;
+      }
+
+      int ioclose( void * /*context_r*/ )
+      { return 0; }
+
+
+      std::list<std::string> structuredErrors;
+      void structuredErrorFunc( void * userData, xmlErrorPtr error )
+      {
+       if ( error )
+       {
+         // error->message is NL terminated
+         std::string err( str::form( "%s[%d] %s", Pathname::basename(error->file).c_str(), error->line,
+                                     str::stripSuffix( error->message, "\n" ).c_str() ) );
+         structuredErrors.push_back( err );
+         WAR << err << endl;
+       }
+#if 0
+        if ( error )
+        {
+#define X(m) SEC << " " << #m << "\t" << error->m << endl
+#define XS(m) SEC << " " << #m << "\t" << (error->m?error->m:"NA") << endl
+            X(domain);
+            X(code);
+            XS(message);
+            X(level);
+            XS(file);
+            X(line);
+            XS(str1);
+            XS(str2);
+            XS(str3);
+            X(int1);
+            X(int2);
+            X(ctxt);
+            X(node);
+#undef X
+#undef XS
+        }
+#endif
+      }
+
+      struct ParseException : public Exception
+      {
+       ParseException()
+       : Exception( "Parse error: " + ( structuredErrors.empty() ? std::string("unknown error"): structuredErrors.back() ) )
+       {
+         for_( it, structuredErrors.begin(), --structuredErrors.end() )
+           addHistory( *it );
+       }
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::Reader
+    // METHOD TYPE : Constructor
+    //
+    Reader::Reader( const InputStream & stream_r,
+                    const Validate & validate_r )
+    : _stream( stream_r )
+    , _reader( xmlReaderForIO( ioread, ioclose, &_stream,
+                               stream_r.path().asString().c_str(), "utf-8", XML_PARSE_PEDANTIC ) )
+    , _node( _reader )
+    {
+      MIL << "Start Parsing " << _stream << endl;
+      if ( ! _reader || ! stream_r.stream().good() )
+        ZYPP_THROW( Exception( "Bad input stream" ) );
+      // set error handler
+      // TODO: Fix using a global lastStructuredError string is not reentrant.
+      structuredErrors.clear();
+      xmlTextReaderSetStructuredErrorHandler( _reader, structuredErrorFunc, NULL );
+      // TODO: set validation
+
+      // advance to 1st node
+      nextNode();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::~Reader
+    // METHOD TYPE : Destructor
+    //
+    Reader::~Reader()
+    {
+      if ( _reader )
+        {
+          xmlFreeTextReader( _reader );
+        }
+      MIL << "Done Parsing " << _stream << endl;
+    }
+
+    XmlString Reader::nodeText()
+    {
+      if ( ! _node.isEmptyElement() )
+      {
+        if ( nextNode() )
+        {
+          if ( _node.nodeType() == XML_READER_TYPE_TEXT )
+          {
+            return _node.value();
+          }
+        }
+      }
+      return XmlString();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::nextNode
+    // METHOD TYPE : bool
+    //
+    bool Reader::nextNode()
+    {
+      int ret = xmlTextReaderRead( _reader );
+      if ( ret == 1 )
+        {
+          return true;
+        }
+      xmlTextReaderClose( _reader );
+      if ( ret != 0 )
+        {
+          ZYPP_THROW( ParseException() );
+        }
+      return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::nextNodeAttribute
+    // METHOD TYPE : bool
+    //
+    bool Reader::nextNodeAttribute()
+    {
+      int ret = xmlTextReaderMoveToNextAttribute( _reader );
+      if ( ret == 1 )
+        {
+          return true;
+        }
+      if ( ret != 0 )
+        {
+          ZYPP_THROW( ParseException() );
+        }
+      return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::close
+    // METHOD TYPE : void
+    //
+    void Reader::close()
+    {
+      if ( _reader )
+        {
+          xmlTextReaderClose( _reader );
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::seekToNode
+    // METHOD TYPE : bool
+    //
+    bool Reader::seekToNode( int depth_r, const std::string & name_r )
+    {
+      do
+        {
+          if ( _node.depth() == depth_r
+               && _node.name() == name_r
+               && _node.nodeType() == XML_READER_TYPE_ELEMENT )
+            {
+              break;
+            }
+        } while( nextNode() );
+
+      return ! atEnd();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Reader::seekToEndNode
+    // METHOD TYPE : bool
+    //
+    bool Reader::seekToEndNode( int depth_r, const std::string & name_r )
+    {
+      // Empty element has no separate end node: <node/>
+      do
+        {
+          if ( _node.depth() == depth_r
+               && _node.name() == name_r
+               && ( _node.nodeType() == XML_READER_TYPE_END_ELEMENT
+                    || ( _node.nodeType() == XML_READER_TYPE_ELEMENT
+                         && _node.isEmptyElement() ) ) )
+            {
+              break;
+            }
+        } while( nextNode() );
+
+      return ! atEnd();
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/Reader.h b/zypp/parser/xml/Reader.h
new file mode 100644 (file)
index 0000000..42c0d90
--- /dev/null
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/Reader.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_READER_H
+#define ZYPP_PARSER_XML_READER_H
+
+#include <iosfwd>
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/parser/xml/Node.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Validate
+    //
+    /** xmlTextReader document validation.
+     * \todo Implement RelaxNG and W3C XSD
+     **/
+    struct Validate
+    {
+      static Validate none()
+      { return Validate(); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Reader
+    //
+    /** xmlTextReader based interface to iterate xml streams.
+     *
+     * \code
+     * // Consume a node.
+     * bool consumeNode( XML::Reader & reader_r )
+     * {
+     *   DBG << *reader_r << endl;
+     *   return true;
+     * }
+     *
+     * // Consume all nodes (omitting attributes)
+     * void example()
+     * {
+     *   try
+     *     {
+     *       XML::Reader reader( "/Local/repodata/repomd.xml" );
+     *       reader.foreachNode( consumeNode );
+     *     }
+     *   catch ( const Exception & )
+     *     { ; } // parse error
+     * }
+     * \endcode
+     *
+     * \code
+     * // Consume a node.
+     * bool consumeNodeAndAttribute( XML::Reader & reader_r )
+     * {
+     *   consumeNode( reader_r );
+     *   return reader_r.foreachNodeAttribute( consumeNode );
+     * }
+     *
+     * // Consume all nodes and thair attributes.
+     * void example()
+     * {
+     *   Pathname repodata( "/Local/repodata/repomd.xml" );
+     *   try
+     *     {
+     *       XML::Reader reader( "/Local/repodata/repomd.xml" );
+     *       reader.foreachNode( consumeNodeAndAttribute );
+     *       // or:
+     *       // reader.foreachNodeOrAttribute( consumeNode )
+     *     }
+     *   catch ( const Exception & )
+     *     { ; } // parse error
+     * }
+     * \endcode
+     **/
+    class Reader : private zypp::base::NonCopyable
+    {
+    public:
+      /** Ctor. Setup xmlTextReader and advance to the 1st Node. */
+      Reader( const InputStream & stream_r,
+              const Validate & validate_r = Validate::none() );
+
+      /** Dtor. */
+      ~Reader();
+
+    public:
+
+      /**
+       *  If the curent node is not empty, advances the reader to the next
+       *  node, and returns the value
+       *
+       * \note if the node has a xml subtree you will probably jump to that node
+       * and get a empty text value back. Use it only if you are sure the node
+       * has no XML subtree.
+       */
+      XmlString nodeText();
+
+      /** */
+      bool nextNode();
+
+      /** */
+      bool nextNodeAttribute();
+
+      /** */
+      bool nextNodeOrAttribute()
+      { return( nextNodeAttribute() || nextNode() ); }
+
+      /** */
+      bool atEnd() const
+      { return( _node.readState() == XML_TEXTREADER_MODE_CLOSED ); }
+
+      /** */
+      const Node & operator*() const
+      { return _node; }
+
+      /** */
+      const Node * operator->() const
+      { return &_node; }
+
+    public:
+      /** */
+      typedef function<bool( Reader & )> ProcessNode;
+
+      /** */
+      bool foreachNode( ProcessNode fnc_r )
+      {
+        if ( _node.isAttribute() )
+          nextNode();
+        for ( ; ! atEnd(); nextNode() )
+          {
+            if ( ! fnc_r( *this ) )
+              return false;
+          }
+        return true;
+      }
+
+      /** */
+      bool foreachNodeAttribute( ProcessNode fnc_r )
+      {
+        if ( _node.isAttribute() && ! fnc_r( *this ) )
+          return false;
+        while( nextNodeAttribute() )
+          {
+            if ( ! fnc_r( *this ) )
+              return false;
+          }
+        return true;
+      }
+
+      /** */
+      bool foreachNodeOrAttribute( ProcessNode fnc_r )
+      {
+        for ( ; ! atEnd(); nextNodeOrAttribute() )
+          {
+            if ( ! fnc_r( *this ) )
+              return false;
+          }
+        return true;
+      }
+
+    public:
+      /** */
+      bool seekToNode( int depth_r, const std::string & name_r );
+
+      /** */
+      bool seekToEndNode( int depth_r, const std::string & name_r );
+
+    private:
+      void close();
+
+    private:
+      InputStream      _stream;
+      xmlTextReaderPtr _reader;
+      Node             _node;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_READER_H
diff --git a/zypp/parser/xml/XmlEscape.h b/zypp/parser/xml/XmlEscape.h
new file mode 100644 (file)
index 0000000..e1adda3
--- /dev/null
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/XmlEscape.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_XMLESCAPE_H
+#define ZYPP_PARSER_XML_XMLESCAPE_H
+
+// from IoBind Library:
+#include <zypp/parser/xml/xml_escape_parser.hpp>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    /** Escape xml special charaters (<tt>& -> &amp;</tt>; from IoBind library). */
+    inline std::string escape( const std::string & in_r )
+    { return iobind::parser::xml_escape_parser().escape( in_r ); }
+
+    /** Unescape xml special charaters (<tt>&amp; -> &</tt>; from IoBind library) */
+    inline std::string unescape( const std::string & in_r )
+    { return iobind::parser::xml_escape_parser().unescape( in_r ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_XMLESCAPE_H
diff --git a/zypp/parser/xml/XmlString.cc b/zypp/parser/xml/XmlString.cc
new file mode 100644 (file)
index 0000000..427c922
--- /dev/null
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/XmlString.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/parser/xml/XmlString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : XmlString::XmlString
+    // METHOD TYPE : Constructor
+    //
+    XmlString::XmlString( const xmlChar *const xmlstr_r,
+                          OnDelete ondelete_r )
+    {
+      if ( xmlstr_r )
+        {
+          if ( ondelete_r == FREE )
+            _xmlstr.reset( xmlstr_r, Deleter() );
+          else
+            _xmlstr.reset( xmlstr_r, NullDeleter() );
+        }
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const XmlString & obj )
+    {
+      if ( obj )
+        return str << obj.c_str();
+      return str << "NULL";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/XmlString.h b/zypp/parser/xml/XmlString.h
new file mode 100644 (file)
index 0000000..28a7abd
--- /dev/null
@@ -0,0 +1,118 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/XmlString.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_XMLSTRING_H
+#define ZYPP_PARSER_XML_XMLSTRING_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/parser/xml/libxmlfwd.h"
+#include "zypp/parser/xml/XmlEscape.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : XmlString
+    //
+    /** <tt>xmlChar *</tt> wrapper.
+     *
+     * Common handling of <tt>xmlChar *</tt> that do or do not need to
+     * be freed. If the wraped <tt>xmlChar *</tt> needs to be freed by
+     * calling \c xmlFree, pass \c FREE as 2nd argument to the ctor.
+     **/
+    class XmlString
+    {
+      /** shared_ptr custom deleter calling \c xmlFree. */
+      struct Deleter
+      {
+        void operator()( const xmlChar * xmlstr_r ) const
+        { xmlFree( (void*)(xmlstr_r) ); }
+      };
+
+    public:
+
+      /** Dtor policy. */
+      enum OnDelete { NOFREE, FREE };
+
+      /** Ctor from xmlChar.
+       * Pass \c FREE as 2nd arg if \c xmlFree needs to be called on destruction.
+      */
+      XmlString( const xmlChar *const xmlstr_r = NULL,
+                 OnDelete ondelete_r           = NOFREE );
+
+      /** Access the <tt>xmlChar *</tt>. */
+      const xmlChar * get() const
+      {
+        if ( ! _xmlstr )
+          return NULL;
+        return &(*_xmlstr);
+      }
+
+      /** Implicit conversion to <tt>xmlChar *</tt>. */
+      operator const xmlChar * () const
+      { return get(); }
+
+      /** Explicit conversion to <tt>const char *</tt>. */
+      const char * c_str() const
+      { return reinterpret_cast<const char *const>(get()); }
+
+      /** Explicit conversion to <tt>std::string</tt>. */
+      std::string asString() const
+      {
+        if ( ! _xmlstr )
+          return std::string();
+        return c_str();
+      }
+
+      bool operator==( const std::string & rhs ) const
+      { return( rhs == c_str() ); }
+
+      bool operator!=( const std::string & rhs ) const
+      { return( rhs != c_str() ); }
+
+      bool operator==( const char *const rhs ) const
+      { return( asString() == rhs ); }
+
+      bool operator!=( const char *const rhs ) const
+      { return( asString() != rhs ); }
+
+      bool operator==( const XmlString & rhs ) const
+      { return( asString() == rhs.c_str() ); }
+
+      bool operator!=( const XmlString & rhs ) const
+      { return( asString() != rhs.c_str() ); }
+
+    private:
+      /** Wraps the <tt>xmlChar *</tt>.
+       * The appropriate custom deleter is set by the ctor. */
+      shared_ptr<const xmlChar> _xmlstr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates XmlString Stream output. */
+    std::ostream & operator<<( std::ostream & str, const XmlString & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_XMLSTRING_H
diff --git a/zypp/parser/xml/libxmlfwd.cc b/zypp/parser/xml/libxmlfwd.cc
new file mode 100644 (file)
index 0000000..16af63b
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/libxmlfwd.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/parser/xml/libxmlfwd.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const ReadState & obj )
+    {
+      switch ( obj )
+        {
+#define X(T) case XML_TEXTREADER_MODE_##T: return str << #T
+          X(INITIAL);
+          X(INTERACTIVE);
+          X(ERROR);
+          X(EOF);
+          X(CLOSED);
+          X(READING);
+#undef X
+        }
+      return str << "UNKNOWN_READ_STATE";
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const NodeType & obj )
+    {
+      switch ( obj )
+        {
+#define X(T) case XML_READER_TYPE_##T: return str << #T
+          X(NONE);
+          X(ELEMENT);
+          X(ATTRIBUTE);
+          X(TEXT);
+          X(CDATA);
+          X(ENTITY_REFERENCE);
+          X(ENTITY);
+          X(PROCESSING_INSTRUCTION);
+          X(COMMENT);
+          X(DOCUMENT);
+          X(DOCUMENT_TYPE);
+          X(DOCUMENT_FRAGMENT);
+          X(NOTATION);
+          X(WHITESPACE);
+          X(SIGNIFICANT_WHITESPACE);
+          X(END_ELEMENT);
+          X(END_ENTITY);
+          X(XML_DECLARATION);
+#undef X
+        }
+      return str << "UNKNOWN_NODE_TYPE";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/parser/xml/libxmlfwd.h b/zypp/parser/xml/libxmlfwd.h
new file mode 100644 (file)
index 0000000..00df261
--- /dev/null
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/xml/libxmlfwd.h
+ *
+*/
+#ifndef ZYPP_PARSER_XML_LIBXMLFWD_H
+#define ZYPP_PARSER_XML_LIBXMLFWD_H
+
+extern "C"
+{
+#include <libxml/xmlreader.h>
+#include <libxml/xmlerror.h>
+}
+
+#include <iosfwd>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace xml
+  { /////////////////////////////////////////////////////////////////
+
+    typedef xmlTextReaderMode ReadState;
+    /** \relates ReadState Stream output. */
+    std::ostream & operator<<( std::ostream & str, const ReadState & obj );
+
+    typedef xmlReaderTypes NodeType;
+    /** \relates NodeType Stream output. */
+    std::ostream & operator<<( std::ostream & str, const NodeType & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace xml
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_XML_LIBXMLFWD_H
diff --git a/zypp/parser/xml/xml_escape_parser.cpp b/zypp/parser/xml/xml_escape_parser.cpp
new file mode 100644 (file)
index 0000000..e8fe60b
--- /dev/null
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------\\r
+|                          ____ _   __ __ ___                          |\r
+|                         |__  / \ / / . \ . \                         |\r
+|                           / / \ V /|  _/  _/                         |\r
+|                          / /__ | | | | | |                           |\r
+|                         /_____||_| |_| |_|                           |\r
+|                                                                      |\r
+\---------------------------------------------------------------------*/\r
+/** \file      zypp/parser/xml_escape_parser.cpp\r
+ *\r
+*/\r
+\r
+#include <string>\r
+#include "xml_escape_parser.hpp"\r
+\r
+namespace iobind{\r
+namespace parser{\r
+\r
+std::string xml_escape_parser::escape(const std::string &istr) const\r
+{\r
+  size_t i;\r
+  std::string str = istr;\r
+  i = str.find_first_of("<>&'\"");\r
+  while (i != std::string::npos)\r
+    {\r
+      switch (str[i])\r
+        {\r
+         case '<': str.replace(i, 1, "&lt;"); i += 3; break;\r
+         case '>': str.replace(i, 1, "&gt;"); i += 3; break;\r
+         case '&': str.replace(i, 1, "&amp;"); i += 4; break;\r
+         case '\'': str.replace(i, 1, "&apos;"); i += 5; break;\r
+         case '"': str.replace(i, 1, "&quot;"); i += 5; break;\r
+       }\r
+      i = str.find_first_of("<>&'\"", i + 1);\r
+    }\r
+  return str;\r
+}\r
+\r
+std::string xml_escape_parser::unescape(const std::string &istr) const\r
+{\r
+  size_t i;\r
+  std::string str = istr;\r
+  i = str.find_first_of("&");\r
+  while (i != std::string::npos)\r
+    {\r
+      if (str[i] == '&')\r
+        {\r
+         if (!str.compare(i + 1, 3, "lt;"))\r
+           str.replace(i, 4, 1, '<');\r
+         else if (!str.compare(i + 1, 3, "gt;"))\r
+           str.replace(i, 4, 1, '>');\r
+         else if (!str.compare(i + 1, 4, "amp;"))\r
+           str.replace(i, 5, 1, '&');\r
+         else if (!str.compare(i + 1, 5, "apos;"))\r
+           str.replace(i, 6, 1, '\'');\r
+         else if (!str.compare(i + 1, 5, "quot;"))\r
+           str.replace(i, 6, 1, '"');\r
+       }\r
+      i = str.find_first_of("&", i + 1);\r
+    }\r
+  return str;\r
+}\r
+}; // parser\r
+}; // iobind\r
diff --git a/zypp/parser/xml/xml_escape_parser.hpp b/zypp/parser/xml/xml_escape_parser.hpp
new file mode 100644 (file)
index 0000000..205d2a7
--- /dev/null
@@ -0,0 +1,37 @@
+/*\r
+IoBind Library License:\r
+--------------------------\r
+\r
+The zlib/libpng License Copyright (c) 2003 Jonathan de Halleux\r
+\r
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.\r
+\r
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:\r
+\r
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\r
+\r
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\r
+\r
+3. This notice may not be removed or altered from any source distribution\r
+*/\r
+\r
+\r
+#ifndef IOBIND_XML_ESCAPE_PARSER_HPP\r
+#define IOBIND_XML_ESCAPE_PARSER_HPP\r
+\r
+#include<string>\r
+\r
+namespace iobind{\r
+namespace parser{\r
+\r
+       struct xml_escape_parser\r
+       {\r
+               std::string escape( std::string const&) const;\r
+               std::string unescape( std::string const&) const;\r
+       };\r
+};\r
+};//iobind\r
+\r
+#endif\r
+\r
+\r
diff --git a/zypp/parser/xml_escape_parser.hpp b/zypp/parser/xml_escape_parser.hpp
new file mode 100644 (file)
index 0000000..dc21ef1
--- /dev/null
@@ -0,0 +1,6 @@
+
+#warning DEPRECATED INCLUDE: include zypp/parser/xml/XmlEscape.h instead
+#warning ....................Consider using zypp::xml::escape instead of
+#warning ....................iobind::parser::xml_escape_parser to become
+#warning ....................implementation independent.
+#include <zypp/parser/xml/XmlEscape.h>
diff --git a/zypp/parser/yum/PatchesFileReader.cc b/zypp/parser/yum/PatchesFileReader.cc
new file mode 100644 (file)
index 0000000..b5ad479
--- /dev/null
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/yum/PatchesFileReader.cc
+ * Implementation of patches.xml file reader.
+ */
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+
+#include "zypp/Date.h"
+#include "zypp/CheckSum.h"
+#include "zypp/OnMediaLocation.h"
+
+#include "zypp/parser/xml/Reader.h"
+#include "zypp/parser/yum/PatchesFileReader.h"
+
+
+using namespace std;
+using namespace zypp::xml;
+
+namespace zypp
+{
+  namespace parser
+  {
+    namespace yum
+    {
+
+
+  enum Tag
+  {
+    tag_NONE,
+    tag_Patches,
+    tag_Patch,
+    tag_Location,
+    tag_CheckSum,
+    tag_Timestamp,
+    tag_OpenCheckSum
+  };
+
+
+  ///////////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PatchesFileReader::Impl
+  //
+  class PatchesFileReader::Impl : private base::NonCopyable
+  {
+  public:
+   /**
+    * CTOR
+    */
+    Impl(const Pathname &patches_file, const ProcessResource & callback);
+
+    /**
+    * Callback provided to the XML parser. Don't use it.
+    */
+    bool consumeNode( Reader & reader_r );
+
+  private:
+    OnMediaLocation _location;
+    Tag _tag;
+    std::string _id;
+    ProcessResource _callback;
+    CheckSum _checksum;
+    std::string _checksum_type;
+    Date _timestamp;
+  };
+  ///////////////////////////////////////////////////////////////////////
+
+
+  PatchesFileReader::Impl::Impl(const Pathname & patches_file,
+                                const ProcessResource & callback)
+    : _tag(tag_NONE), _callback(callback)
+  {
+    Reader reader( patches_file );
+    MIL << "Reading " << patches_file << endl;
+    reader.foreachNode(bind( &PatchesFileReader::Impl::consumeNode, this, _1 ));
+  }
+
+  // --------------------------------------------------------------------------
+
+  bool PatchesFileReader::Impl::consumeNode( Reader & reader_r )
+  {
+    //MIL << reader_r->name() << endl;
+    std::string data_type;
+    if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
+    {
+      if ( reader_r->name() == "patches" )
+      {
+        _tag = tag_Patches;
+        return true;
+      }
+      if ( reader_r->name() == "patch" )
+      {
+        _tag = tag_Patch;
+        _id = reader_r->getAttribute("id").asString();
+        return true;
+      }
+      if ( reader_r->name() == "location" )
+      {
+        _tag = tag_Location;
+        _location.setLocation( reader_r->getAttribute("href").asString(), 1 );
+        return true;
+      }
+      if ( reader_r->name() == "checksum" )
+      {
+        _tag = tag_CheckSum;
+        string checksum_type = reader_r->getAttribute("type").asString() ;
+        string checksum_vaue = reader_r.nodeText().asString();
+        _location.setChecksum( CheckSum( checksum_type, checksum_vaue ) );
+        return true;
+      }
+      if ( reader_r->name() == "timestamp" )
+      {
+        // ignore it
+        return true;
+      }
+    }
+    else if ( reader_r->nodeType() == XML_READER_TYPE_END_ELEMENT )
+    {
+      //MIL << "end element" << endl;
+      if ( reader_r->name() == "patch" )
+        _callback( _location, _id );
+      return true;
+    }
+    return true;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : PatchesFileReader
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  PatchesFileReader::PatchesFileReader(const Pathname & patches_file,
+                                       const ProcessResource & callback)
+    : _pimpl(new Impl(patches_file, callback))
+  {}
+
+  PatchesFileReader::~PatchesFileReader()
+  {}
+
+
+    } // ns yum
+  } // ns parser
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/yum/PatchesFileReader.h b/zypp/parser/yum/PatchesFileReader.h
new file mode 100644 (file)
index 0000000..53b344a
--- /dev/null
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/yum/PatchesFileReader.h
+ * Interface of patches.xml file reader.
+ */
+#ifndef zypp_source_yum_PatchesFileReader_H
+#define zypp_source_yum_PatchesFileReader_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Function.h"
+
+
+namespace zypp
+{
+  namespace parser
+  {
+    namespace yum
+    {
+
+
+  /**
+   * Iterates through a patches.xml file giving on each iteration
+   * a \ref OnMediaLocation object with the resource and its
+   * patch id.
+   * The iteration is done via a callback provided on
+   * construction.
+   *
+   * \code
+   * PatchesFileReader reader(patches_file, 
+   *                  bind( &SomeClass::callbackfunc, &object, _1, _2 ) );
+   * \endcode
+   */
+  class PatchesFileReader : private base::NonCopyable
+  {
+  public:
+
+    /**
+     * Callback definition
+     * first parameter is a \ref OnMediaLocation object with the resource
+     * second parameter is the patch id.
+     */
+    typedef
+      function<bool( const OnMediaLocation &, const std::string & )>
+      ProcessResource;
+
+
+    /**
+    * CTOR. Creates also \ref xml::Reader and starts reading.
+    * 
+     * \param patches_file is the patches.xml file you want to read
+     * \param callback is a function.
+     * 
+     * \see PatchesFileReader::ProcessResource
+     */
+    PatchesFileReader(const Pathname &patches_file,
+                      const ProcessResource & callback);
+
+    /**
+     * DTOR
+     */
+    ~PatchesFileReader();
+
+  private:
+    class Impl;
+    RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+  };
+
+
+    } // ns yum
+  } // ns parser
+} // ns zypp
+
+#endif /*zypp_source_yum_PatchesFileReader_H*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/yum/RepomdFileReader.cc b/zypp/parser/yum/RepomdFileReader.cc
new file mode 100644 (file)
index 0000000..6163a78
--- /dev/null
@@ -0,0 +1,207 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/yum/RepomdFileReader.cc
+ * Implementation of repomd.xml file reader.
+ */
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/Date.h"
+#include "zypp/CheckSum.h"
+#include "zypp/parser/xml/Reader.h"
+
+#include "zypp/parser/yum/RepomdFileReader.h"
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "parser::yum"
+
+using namespace std;
+using namespace zypp::xml;
+using zypp::repo::yum::ResourceType;
+
+namespace zypp
+{
+  namespace parser
+  {
+    namespace yum
+    {
+
+
+  ///////////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : RepomdFileReader::Impl
+  //
+  class RepomdFileReader::Impl : private base::NonCopyable
+  {
+  public:
+
+    /**
+     * Enumeration of repomd.xml tags.
+     * \see _tag
+     */
+    enum Tag
+    {
+      tag_NONE,
+      tag_Repomd,
+      tag_Data,
+      tag_Location,
+      tag_CheckSum,
+      tag_Timestamp,
+      tag_OpenCheckSum
+    };
+
+  public:
+    /**
+     * CTOR
+     *
+     * \see RepomdFileReader::RepomdFileReader(Pathname,ProcessResource)
+     */
+    Impl(const Pathname &repomd_file, const ProcessResource & callback);
+
+    /**
+     * Callback provided to the XML parser.
+     */
+    bool consumeNode( Reader & reader_r );
+
+
+  private:
+    /** Location of metadata file. */
+    OnMediaLocation _location;
+
+    /** Used to remember currently processed tag */
+    Tag _tag;
+
+    /** Type of metadata file. */
+    repo::yum::ResourceType _type;
+
+    /** Function for processing collected data. Passed-in through constructor. */
+    ProcessResource _callback;
+
+    /** Checksum of metadata file */
+    CheckSum _checksum;
+
+    /** Type of checksum of metadata file */
+    std::string _checksum_type;
+
+    /** Metadata file time-stamp. */
+    Date _timestamp;
+  };
+  ///////////////////////////////////////////////////////////////////////
+
+  RepomdFileReader::Impl::Impl(
+      const Pathname &repomd_file, const ProcessResource & callback)
+    :
+      _tag(tag_NONE), _type(ResourceType::NONE_e), _callback(callback)
+  {
+    Reader reader( repomd_file );
+    MIL << "Reading " << repomd_file << endl;
+    reader.foreachNode( bind( &RepomdFileReader::Impl::consumeNode, this, _1 ) );
+  }
+
+  // --------------------------------------------------------------------------
+
+  /*
+   * xpath and multiplicity of processed nodes are included in the code
+   * for convenience:
+   *
+   * // xpath: <xpath> (?|*|+)
+   *
+   * if multiplicity is ommited, then the node has multiplicity 'one'.
+   */
+
+  // --------------------------------------------------------------------------
+
+  bool RepomdFileReader::Impl::consumeNode( Reader & reader_r )
+  {
+    if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
+    {
+      // xpath: /repomd
+      if ( reader_r->name() == "repomd" )
+      {
+        _tag = tag_Repomd;
+        return true;
+      }
+
+      // xpath: /repomd/data (+)
+      if ( reader_r->name() == "data" )
+      {
+        _tag = tag_Data;
+        _type = ResourceType(reader_r->getAttribute("type").asString());
+        return true;
+      }
+
+      // xpath: /repomd/location
+      if ( reader_r->name() == "location" )
+      {
+        _tag = tag_Location;
+        _location.setLocation( reader_r->getAttribute("href").asString(), 1 );
+        // ignoring attribute xml:base
+        return true;
+      }
+
+      // xpath: /repomd/checksum
+      if ( reader_r->name() == "checksum" )
+      {
+        _tag = tag_CheckSum;
+        string checksum_type = reader_r->getAttribute("type").asString() ;
+        string checksum_vaue = reader_r.nodeText().asString();
+        _location.setChecksum( CheckSum( checksum_type, checksum_vaue ) );
+        return true;
+      }
+
+      // xpath: /repomd/timestamp
+      if ( reader_r->name() == "timestamp" )
+      {
+        // ignore it
+        return true;
+      }
+
+      //! \todo xpath: /repomd/open-checksum (?)
+    }
+
+    else if ( reader_r->nodeType() == XML_READER_TYPE_END_ELEMENT )
+    {
+      // xpath: /repomd/data
+      if ( reader_r->name() == "data" )
+      {
+        if (_callback)
+          _callback( _location, _type );
+
+        return true;
+      }
+    }
+
+    return true;
+  }
+
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //  CLASS NAME : RepomdFileReader
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  RepomdFileReader::RepomdFileReader(
+      const Pathname & repomd_file, const ProcessResource & callback)
+    :
+      _pimpl(new Impl(repomd_file, callback))
+  {}
+
+  RepomdFileReader::~RepomdFileReader()
+  {}
+
+
+    } // ns yum
+  } // ns parser
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/yum/RepomdFileReader.h b/zypp/parser/yum/RepomdFileReader.h
new file mode 100644 (file)
index 0000000..dd193ea
--- /dev/null
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/parser/yum/RepomdFileReader.h
+ * Interface of repomd.xml file reader.
+ */
+#ifndef zypp_source_yum_RepomdFileReader_H
+#define zypp_source_yum_RepomdFileReader_H
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/OnMediaLocation.h"
+#include "zypp/repo/yum/ResourceType.h"
+
+namespace zypp
+{
+  namespace parser
+  {
+    namespace yum
+    {
+
+
+  /**
+   * Reads through a repomd.xml file and collects type, location, checksum and
+   * other data about metadata files to be processed.
+   *
+   * After each package is read, a \ref OnMediaLocation
+   * and \ref repo::yum::ResourceType is prepared and \ref _callback
+   * is called with these two objects passed in.
+   *
+   * The \ref _callback is provided on construction.
+   *
+   *
+   * \code
+   * RepomdFileReader reader(repomd_file, 
+   *                  bind( &SomeClass::callbackfunc, &SomeClassInstance, _1, _2 ) );
+   * \endcode
+   */
+  class RepomdFileReader : private base::NonCopyable
+  {
+  public:
+   /**
+    * Callback definition.
+    * First parameter is a \ref OnMediaLocation object with the resource
+    * second parameter is the resource type.
+    */
+    typedef function< bool(
+        const OnMediaLocation &,
+        const repo::yum::ResourceType &)>
+      ProcessResource;
+
+   /**
+    * CTOR. Creates also \ref xml::Reader and starts reading.
+    * 
+    * \param repomd_file is the repomd.xml file you want to read
+    * \param callback is a function.
+    *
+    * \see RepomdFileReader::ProcessResource
+    */
+    RepomdFileReader(
+      const Pathname & repomd_file, const ProcessResource & callback);
+
+    /**
+     * DTOR
+     */
+    ~RepomdFileReader();
+
+  private:
+    class Impl;
+    RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+  };
+
+
+    } // ns yum
+  } // ns parser
+} // ns zypp
+
+#endif /*zypp_source_yum_RepomdFileReader_H*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/parser/yum/schema/common-inc.rnc b/zypp/parser/yum/schema/common-inc.rnc
new file mode 100644 (file)
index 0000000..f12ac3b
--- /dev/null
@@ -0,0 +1,49 @@
+# private is not an XML namespace, just a holder for reusable 
+#   attribute/element sets
+
+private.positive = xsd:positiveInteger { pattern="[1-9][0-9]*" }
+
+private.nonnegative = "0" | private.positive
+
+# Unix timestamp (seconds since epoch)
+# For our purposes, this is always positive
+private.unixts = private.positive
+
+private.size = private.nonnegative 
+
+private.checksum = 
+  (attribute type { "md5" }, xsd:string { length="32" pattern="[0-9a-f]*" }) |
+  (attribute type { "sha" }, xsd:string { length="40" pattern="[0-9a-f]*" }) |
+  (attribute type { "sha256" }, xsd:string { length="64" pattern="[0-9a-f]*" }) |
+  (attribute type { "sha" }, xsd:string { length="32" pattern="[0-9a-f]*" })
+# The last option is totally broken, but is in the wild!!! :(
+
+private.evr = 
+  attribute epoch { private.nonnegative }?,
+  attribute ver { text },
+  attribute rel { text }?
+
+private.localizedtext = attribute lang { "en" | "de" }, text
+
+private.archenum = "noarch"
+            | "armv4l"
+            | "armv5el"
+            | "armv5tel"
+            | "armv7hl"
+            | "armv7nhl"
+            | "armv7el"
+            | "armv7l"
+            | "athlon"
+            | "i386"
+            | "i486"
+            | "i586"
+            | "i686"
+            | "ia64"
+            | "ppc"
+            | "ppc64"
+            | "s390"
+            | "s390x"
+            | "sh4"
+            | "x86_64"
+            | "src"
+
diff --git a/zypp/parser/yum/schema/common-inc.rng b/zypp/parser/yum/schema/common-inc.rng
new file mode 100644 (file)
index 0000000..60e5742
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <!--
+    private is not an XML namespace, just a holder for reusable 
+      attribute/element sets
+  -->
+  <define name="private.positive">
+    <data type="positiveInteger">
+      <param name="pattern">[1-9][0-9]*</param>
+    </data>
+  </define>
+  <define name="private.nonnegative">
+    <choice>
+      <value>0</value>
+      <ref name="private.positive"/>
+    </choice>
+  </define>
+  <!--
+    Unix timestamp (seconds since epoch)
+    For our purposes, this is always positive
+  -->
+  <define name="private.unixts">
+    <ref name="private.positive"/>
+  </define>
+  <define name="private.size">
+    <ref name="private.nonnegative"/>
+  </define>
+  <define name="private.checksum">
+    <choice>
+      <group>
+        <attribute name="type">
+          <value>md5</value>
+        </attribute>
+        <data type="string">
+          <param name="length">32</param>
+          <param name="pattern">[0-9a-f]*</param>
+        </data>
+      </group>
+      <group>
+        <attribute name="type">
+          <value>sha</value>
+        </attribute>
+        <data type="string">
+          <param name="length">40</param>
+          <param name="pattern">[0-9a-f]*</param>
+        </data>
+      </group>
+      <group>
+        <attribute name="type">
+          <value>sha256</value>
+        </attribute>
+        <data type="string">
+          <param name="length">64</param>
+          <param name="pattern">[0-9a-f]*</param>
+        </data>
+      </group>
+      <group>
+        <attribute name="type">
+          <value>sha</value>
+        </attribute>
+        <data type="string">
+          <param name="length">32</param>
+          <param name="pattern">[0-9a-f]*</param>
+        </data>
+      </group>
+    </choice>
+  </define>
+  <!-- The last option is totally broken, but is in the wild!!! :( -->
+  <define name="private.evr">
+    <optional>
+      <attribute name="epoch">
+        <ref name="private.nonnegative"/>
+      </attribute>
+    </optional>
+    <attribute name="ver"/>
+    <optional>
+      <attribute name="rel"/>
+    </optional>
+  </define>
+  <define name="private.localizedtext">
+    <attribute name="lang">
+      <choice>
+        <value>en</value>
+        <value>de</value>
+      </choice>
+    </attribute>
+    <text/>
+  </define>
+  <define name="private.archenum">
+    <choice>
+      <value>noarch</value>
+      <value>armv4l</value>
+      <value>armv5el</value>
+      <value>armv5tel</value>
+      <value>armv7hl</value>
+      <value>armv7nhl</value>
+      <value>armv7el</value>
+      <value>armv7l</value>
+      <value>athlon</value>
+      <value>i386</value>
+      <value>i486</value>
+      <value>i586</value>
+      <value>i686</value>
+      <value>ia64</value>
+      <value>ppc</value>
+      <value>ppc64</value>
+      <value>s390</value>
+      <value>s390x</value>
+      <value>sh4</value>
+      <value>x86_64</value>
+      <value>src</value>
+    </choice>
+  </define>
+</grammar>
diff --git a/zypp/parser/yum/schema/deltainfo.rnc b/zypp/parser/yum/schema/deltainfo.rnc
new file mode 100644 (file)
index 0000000..71c2c40
--- /dev/null
@@ -0,0 +1,21 @@
+include "common-inc.rnc"
+
+start  = element deltainfo {
+  element newpackage {
+    attribute name { text },
+    attribute arch { private.archenum },
+    attribute version { text },
+    attribute release { text },
+    element delta {
+      attribute oldepoch { private.nonnegative },
+      # two different formats, with different attribute names with same data
+      ((attribute oldversion { text }, attribute oldrelease { text }) |
+       (attribute ver        { text }, attribute rel        { text })),
+      element filename { text },
+      element sequence { text },
+      element size { private.size },
+      element checksum { private.checksum }
+    }+
+  }*
+}
+
diff --git a/zypp/parser/yum/schema/deltainfo.rng b/zypp/parser/yum/schema/deltainfo.rng
new file mode 100644 (file)
index 0000000..5fb5946
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="deltainfo">
+      <zeroOrMore>
+        <element name="newpackage">
+          <attribute name="name"/>
+          <attribute name="arch">
+            <ref name="private.archenum"/>
+          </attribute>
+          <attribute name="version"/>
+          <attribute name="release"/>
+          <oneOrMore>
+            <element name="delta">
+              <attribute name="oldepoch">
+                <ref name="private.nonnegative"/>
+              </attribute>
+              <!-- two different formats, with different attribute names with same data -->
+              <choice>
+                <group>
+                  <attribute name="oldversion"/>
+                  <attribute name="oldrelease"/>
+                </group>
+                <group>
+                  <attribute name="ver"/>
+                  <attribute name="rel"/>
+                </group>
+              </choice>
+              <element name="filename">
+                <text/>
+              </element>
+              <element name="sequence">
+                <text/>
+              </element>
+              <element name="size">
+                <ref name="private.size"/>
+              </element>
+              <element name="checksum">
+                <ref name="private.checksum"/>
+              </element>
+            </element>
+          </oneOrMore>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/filelists.rnc b/zypp/parser/yum/schema/filelists.rnc
new file mode 100644 (file)
index 0000000..da1802b
--- /dev/null
@@ -0,0 +1,17 @@
+default namespace = "http://linux.duke.edu/metadata/filelists"
+
+include "common-inc.rnc"
+
+start = element filelists {
+  attribute packages { xsd:nonNegativeInteger },
+  element package {
+    attribute pkgid { text },
+    attribute name { text },
+    attribute arch { private.archenum },
+    element version { private.evr },
+    element file { 
+      attribute type { "dir" | "ghost" }?,
+      text 
+    }*
+  }*
+}
diff --git a/zypp/parser/yum/schema/filelists.rng b/zypp/parser/yum/schema/filelists.rng
new file mode 100644 (file)
index 0000000..48afa75
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://linux.duke.edu/metadata/filelists" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="filelists">
+      <attribute name="packages">
+        <data type="nonNegativeInteger"/>
+      </attribute>
+      <zeroOrMore>
+        <element name="package">
+          <attribute name="pkgid"/>
+          <attribute name="name"/>
+          <attribute name="arch">
+            <ref name="private.archenum"/>
+          </attribute>
+          <element name="version">
+            <ref name="private.evr"/>
+          </element>
+          <zeroOrMore>
+            <element name="file">
+              <optional>
+                <attribute name="type">
+                  <choice>
+                    <value>dir</value>
+                    <value>ghost</value>
+                  </choice>
+                </attribute>
+              </optional>
+              <text/>
+            </element>
+          </zeroOrMore>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/other.rnc b/zypp/parser/yum/schema/other.rnc
new file mode 100644 (file)
index 0000000..3aafe3a
--- /dev/null
@@ -0,0 +1,18 @@
+default namespace = "http://linux.duke.edu/metadata/other"
+
+include "common-inc.rnc"
+
+start = element otherdata {
+  attribute packages { private.nonnegative },
+  element package {
+    attribute pkgid { text },
+    attribute name { text },
+    attribute arch { private.archenum },
+    element version { private.evr },
+    element changelog { 
+      attribute author { text },
+      attribute date { private.unixts },
+      text 
+    }*
+  }*
+}
diff --git a/zypp/parser/yum/schema/other.rng b/zypp/parser/yum/schema/other.rng
new file mode 100644 (file)
index 0000000..45302f0
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://linux.duke.edu/metadata/other" xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="otherdata">
+      <attribute name="packages">
+        <ref name="private.nonnegative"/>
+      </attribute>
+      <zeroOrMore>
+        <element name="package">
+          <attribute name="pkgid"/>
+          <attribute name="name"/>
+          <attribute name="arch">
+            <ref name="private.archenum"/>
+          </attribute>
+          <element name="version">
+            <ref name="private.evr"/>
+          </element>
+          <zeroOrMore>
+            <element name="changelog">
+              <attribute name="author"/>
+              <attribute name="date">
+                <ref name="private.unixts"/>
+              </attribute>
+              <text/>
+            </element>
+          </zeroOrMore>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/patch.rnc b/zypp/parser/yum/schema/patch.rnc
new file mode 100644 (file)
index 0000000..46cc255
--- /dev/null
@@ -0,0 +1,146 @@
+default namespace = "http://novell.com/package/metadata/suse/patch"
+namespace yum = "http://linux.duke.edu/metadata/common"
+namespace suse = "http://novell.com/package/metadata/suse/common"
+
+include "rpm-inc.rnc"
+
+yum.name = element yum:name { text }
+
+yum.arch = element yum:arch { private.archenum }
+
+yum.version =  element yum:version {
+  private.evr
+}
+
+suse.freshens = element suse:freshens {
+  element suse:entry {
+    attribute kind { "package" },
+    attribute name { text }
+  }+
+}
+
+group.deps = rpm.requires?, rpm.obsoletes?, rpm.provides?, rpm.recommends?, rpm.supplements?, rpm.conflicts?, suse.freshens?
+
+patch.location = element location {
+  attribute href { xsd:anyURI }
+}
+
+patch.checksum = element checksum {
+  private.checksum
+}
+patch.time = element time {
+  attribute file { private.unixts },
+  attribute build { private.unixts }
+}
+
+patch.size = element size {
+  attribute package { private.size },
+  attribute archive { private.size }
+}
+
+start = element patch {
+  attribute patchid { text },
+  attribute timestamp { private.unixts },
+  attribute engine { "1.0" },
+  yum.name,
+  element summary {
+    private.localizedtext
+  }+,
+  element description {
+    private.localizedtext
+  }+,
+  element license-to-confirm { 
+    private.localizedtext
+  }?,
+  yum.version,
+  group.deps,
+  element reboot-needed { empty }?,
+  element package-manager { empty }?,
+  element category { "security" | "recommended" | "optional" },
+  # This is in two places (with slightly different definiton)
+  # because that is what is seen in the wild
+  element license-to-confirm { 
+    text
+  }?,
+  element atoms {
+    element message {
+      yum.name,
+      yum.version,
+      element text { 
+        private.localizedtext
+      },
+      group.deps
+    }?,
+    element yum:package {
+      attribute type { "rpm" },
+      yum.name,
+      yum.arch,
+      yum.version,
+      element yum:checksum {
+        attribute pkgid  { "YES" },
+        private.checksum
+      },
+      element yum:time {
+        attribute file { private.unixts },
+        attribute build { private.unixts }
+      },
+      element yum:size {
+        attribute package { private.size },
+        attribute installed { private.size },
+        attribute archive { xsd:nonNegativeInteger }
+      },
+      element yum:location {
+        attribute href { xsd:anyURI }
+      },
+      element yum:format {
+        group.deps
+      },
+      element pkgfiles {
+        element patchrpm {
+          patch.location,
+          patch.checksum,
+          patch.time,
+          patch.size,
+          element base-version {
+            private.evr
+          }+
+        }?,
+        element deltarpm {
+          patch.location,
+          patch.checksum,
+          patch.time,
+          patch.size,
+          element base-version {
+            private.evr,
+            attribute md5sum { xsd:string { length="32" pattern="[0-9a-f]*" } },
+            attribute buildtime { private.unixts },
+            attribute sequence_info { text }
+          }
+        }*
+      }
+    }*,
+    element message {
+      yum.name,
+      yum.version,
+      element text { 
+        private.localizedtext
+      },
+      group.deps
+    }?,
+    element script {
+      yum.name,
+      yum.version,
+      # Script can either be remote (do-location and do-checksum) 
+      # or inline (do), but not both
+      ((element do-location {
+        attribute href { xsd:anyURI }
+      },
+      element do-checksum {
+        private.checksum
+      }) | element do { text }),
+      group.deps
+    }?
+  }?
+}
+
diff --git a/zypp/parser/yum/schema/patch.rng b/zypp/parser/yum/schema/patch.rng
new file mode 100644 (file)
index 0000000..8c54b01
--- /dev/null
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://novell.com/package/metadata/suse/patch" xmlns:yum="http://linux.duke.edu/metadata/common" xmlns:suse="http://novell.com/package/metadata/suse/common" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="rpm-inc.rng"/>
+  <define name="yum.name">
+    <element name="yum:name">
+      <text/>
+    </element>
+  </define>
+  <define name="yum.arch">
+    <element name="yum:arch">
+      <ref name="private.archenum"/>
+    </element>
+  </define>
+  <define name="yum.version">
+    <element name="yum:version">
+      <ref name="private.evr"/>
+    </element>
+  </define>
+  <define name="suse.freshens">
+    <element name="suse:freshens">
+      <oneOrMore>
+        <element name="suse:entry">
+          <attribute name="kind">
+            <value>package</value>
+          </attribute>
+          <attribute name="name"/>
+        </element>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="group.deps">
+    <optional>
+      <ref name="rpm.requires"/>
+    </optional>
+    <optional>
+      <ref name="rpm.obsoletes"/>
+    </optional>
+    <optional>
+      <ref name="rpm.provides"/>
+    </optional>
+    <optional>
+      <ref name="rpm.recommends"/>
+    </optional>
+    <optional>
+      <ref name="rpm.supplements"/>
+    </optional>
+    <optional>
+      <ref name="rpm.conflicts"/>
+    </optional>
+    <optional>
+      <ref name="suse.freshens"/>
+    </optional>
+  </define>
+  <define name="patch.location">
+    <element name="location">
+      <attribute name="href">
+        <data type="anyURI"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="patch.checksum">
+    <element name="checksum">
+      <ref name="private.checksum"/>
+    </element>
+  </define>
+  <define name="patch.time">
+    <element name="time">
+      <attribute name="file">
+        <ref name="private.unixts"/>
+      </attribute>
+      <attribute name="build">
+        <ref name="private.unixts"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="patch.size">
+    <element name="size">
+      <attribute name="package">
+        <ref name="private.size"/>
+      </attribute>
+      <attribute name="archive">
+        <ref name="private.size"/>
+      </attribute>
+    </element>
+  </define>
+  <start>
+    <element name="patch">
+      <attribute name="patchid"/>
+      <attribute name="timestamp">
+        <ref name="private.unixts"/>
+      </attribute>
+      <attribute name="engine">
+        <value>1.0</value>
+      </attribute>
+      <ref name="yum.name"/>
+      <oneOrMore>
+        <element name="summary">
+          <ref name="private.localizedtext"/>
+        </element>
+      </oneOrMore>
+      <oneOrMore>
+        <element name="description">
+          <ref name="private.localizedtext"/>
+        </element>
+      </oneOrMore>
+      <optional>
+        <element name="license-to-confirm">
+          <ref name="private.localizedtext"/>
+        </element>
+      </optional>
+      <ref name="yum.version"/>
+      <ref name="group.deps"/>
+      <optional>
+        <element name="reboot-needed">
+          <empty/>
+        </element>
+      </optional>
+      <optional>
+        <element name="package-manager">
+          <empty/>
+        </element>
+      </optional>
+      <element name="category">
+        <choice>
+          <value>security</value>
+          <value>recommended</value>
+          <value>optional</value>
+        </choice>
+      </element>
+      <optional>
+        <!--
+          This is in two places (with slightly different definiton)
+          because that is what is seen in the wild
+        -->
+        <element name="license-to-confirm">
+          <text/>
+        </element>
+      </optional>
+      <optional>
+        <element name="atoms">
+          <optional>
+            <element name="message">
+              <ref name="yum.name"/>
+              <ref name="yum.version"/>
+              <element name="text">
+                <ref name="private.localizedtext"/>
+              </element>
+              <ref name="group.deps"/>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <element name="yum:package">
+              <attribute name="type">
+                <value>rpm</value>
+              </attribute>
+              <ref name="yum.name"/>
+              <ref name="yum.arch"/>
+              <ref name="yum.version"/>
+              <element name="yum:checksum">
+                <attribute name="pkgid">
+                  <value>YES</value>
+                </attribute>
+                <ref name="private.checksum"/>
+              </element>
+              <element name="yum:time">
+                <attribute name="file">
+                  <ref name="private.unixts"/>
+                </attribute>
+                <attribute name="build">
+                  <ref name="private.unixts"/>
+                </attribute>
+              </element>
+              <element name="yum:size">
+                <attribute name="package">
+                  <ref name="private.size"/>
+                </attribute>
+                <attribute name="installed">
+                  <ref name="private.size"/>
+                </attribute>
+                <attribute name="archive">
+                  <data type="nonNegativeInteger"/>
+                </attribute>
+              </element>
+              <element name="yum:location">
+                <attribute name="href">
+                  <data type="anyURI"/>
+                </attribute>
+              </element>
+              <element name="yum:format">
+                <ref name="group.deps"/>
+              </element>
+              <element name="pkgfiles">
+                <optional>
+                  <element name="patchrpm">
+                    <ref name="patch.location"/>
+                    <ref name="patch.checksum"/>
+                    <ref name="patch.time"/>
+                    <ref name="patch.size"/>
+                    <oneOrMore>
+                      <element name="base-version">
+                        <ref name="private.evr"/>
+                      </element>
+                    </oneOrMore>
+                  </element>
+                </optional>
+                <zeroOrMore>
+                  <element name="deltarpm">
+                    <ref name="patch.location"/>
+                    <ref name="patch.checksum"/>
+                    <ref name="patch.time"/>
+                    <ref name="patch.size"/>
+                    <element name="base-version">
+                      <ref name="private.evr"/>
+                      <attribute name="md5sum">
+                        <data type="string">
+                          <param name="length">32</param>
+                          <param name="pattern">[0-9a-f]*</param>
+                        </data>
+                      </attribute>
+                      <attribute name="buildtime">
+                        <ref name="private.unixts"/>
+                      </attribute>
+                      <attribute name="sequence_info"/>
+                    </element>
+                  </element>
+                </zeroOrMore>
+              </element>
+            </element>
+          </zeroOrMore>
+          <optional>
+            <element name="message">
+              <ref name="yum.name"/>
+              <ref name="yum.version"/>
+              <element name="text">
+                <ref name="private.localizedtext"/>
+              </element>
+              <ref name="group.deps"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="script">
+              <ref name="yum.name"/>
+              <ref name="yum.version"/>
+              <!--
+                Script can either be remote (do-location and do-checksum) 
+                or inline (do), but not both
+              -->
+              <choice>
+                <group>
+                  <element name="do-location">
+                    <attribute name="href">
+                      <data type="anyURI"/>
+                    </attribute>
+                  </element>
+                  <element name="do-checksum">
+                    <ref name="private.checksum"/>
+                  </element>
+                </group>
+                <element name="do">
+                  <text/>
+                </element>
+              </choice>
+              <ref name="group.deps"/>
+            </element>
+          </optional>
+        </element>
+      </optional>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/patches.rnc b/zypp/parser/yum/schema/patches.rnc
new file mode 100644 (file)
index 0000000..3819bcd
--- /dev/null
@@ -0,0 +1,16 @@
+default namespace = "http://novell.com/package/metadata/suse/patches"
+
+include "common-inc.rnc"
+
+start  = element patches {
+  element patch {
+    attribute id { text },
+    element checksum {
+      private.checksum
+    },
+    element location {
+      attribute href { xsd:anyURI }
+    },
+    element category { "recommended" | "security" | "optional" }?
+  }*
+}
diff --git a/zypp/parser/yum/schema/patches.rng b/zypp/parser/yum/schema/patches.rng
new file mode 100644 (file)
index 0000000..b386da3
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://novell.com/package/metadata/suse/patches" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="patches">
+      <zeroOrMore>
+        <element name="patch">
+          <attribute name="id"/>
+          <element name="checksum">
+            <ref name="private.checksum"/>
+          </element>
+          <element name="location">
+            <attribute name="href">
+              <data type="anyURI"/>
+            </attribute>
+          </element>
+          <optional>
+            <element name="category">
+              <choice>
+                <value>recommended</value>
+                <value>security</value>
+                <value>optional</value>
+              </choice>
+            </element>
+          </optional>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/patterns.rnc b/zypp/parser/yum/schema/patterns.rnc
new file mode 100644 (file)
index 0000000..6efa68c
--- /dev/null
@@ -0,0 +1,30 @@
+namespace suse = "http://novell.com/package/metadata/suse/pattern"
+namespace rpm = "http://linux.duke.edu/metadata/rpm"
+
+include "rpm-inc.rnc"
+
+suse.uservisible = element suse:uservisible { empty }
+
+suse.category = element suse:category { private.localizedtext } 
+
+suse.plaincat = element suse:category { text }
+
+start = element patterns {
+  # seen missing in the wild
+  attribute count { xsd:nonNegativeInteger }?,
+  element suse:pattern {
+    element suse:name { text },
+    element suse:arch { private.archenum }?,
+    element suse:version { private.evr }?,
+    element suse:summary { text },
+    element suse:description { text }?,
+    ((suse.uservisible, suse.category?) | (suse.plaincat, suse.uservisible?)),
+    rpm.provides?,
+    element rpm:freshens {
+      rpm.entry+
+    }?,
+    rpm.suggests?,
+    rpm.requires?,
+    rpm.recommends?
+  }+
+}
diff --git a/zypp/parser/yum/schema/patterns.rng b/zypp/parser/yum/schema/patterns.rng
new file mode 100644 (file)
index 0000000..3d0c6bb
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:rpm="http://linux.duke.edu/metadata/rpm" xmlns:suse="http://novell.com/package/metadata/suse/pattern" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="rpm-inc.rng"/>
+  <define name="suse.uservisible">
+    <element name="suse:uservisible">
+      <empty/>
+    </element>
+  </define>
+  <define name="suse.category">
+    <element name="suse:category">
+      <ref name="private.localizedtext"/>
+    </element>
+  </define>
+  <define name="suse.plaincat">
+    <element name="suse:category">
+      <text/>
+    </element>
+  </define>
+  <start>
+    <element name="patterns">
+      <optional>
+        <!-- seen missing in the wild -->
+        <attribute name="count">
+          <data type="nonNegativeInteger"/>
+        </attribute>
+      </optional>
+      <oneOrMore>
+        <element name="suse:pattern">
+          <element name="suse:name">
+            <text/>
+          </element>
+          <optional>
+            <element name="suse:arch">
+              <ref name="private.archenum"/>
+            </element>
+          </optional>
+          <optional>
+            <element name="suse:version">
+              <ref name="private.evr"/>
+            </element>
+          </optional>
+          <element name="suse:summary">
+            <text/>
+          </element>
+          <optional>
+            <element name="suse:description">
+              <text/>
+            </element>
+          </optional>
+          <choice>
+            <group>
+              <ref name="suse.uservisible"/>
+              <optional>
+                <ref name="suse.category"/>
+              </optional>
+            </group>
+            <group>
+              <ref name="suse.plaincat"/>
+              <optional>
+                <ref name="suse.uservisible"/>
+              </optional>
+            </group>
+          </choice>
+          <optional>
+            <ref name="rpm.provides"/>
+          </optional>
+          <optional>
+            <element name="rpm:freshens">
+              <oneOrMore>
+                <ref name="rpm.entry"/>
+              </oneOrMore>
+            </element>
+          </optional>
+          <optional>
+            <ref name="rpm.suggests"/>
+          </optional>
+          <optional>
+            <ref name="rpm.requires"/>
+          </optional>
+          <optional>
+            <ref name="rpm.recommends"/>
+          </optional>
+        </element>
+      </oneOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/primary.rnc b/zypp/parser/yum/schema/primary.rnc
new file mode 100644 (file)
index 0000000..a8de88b
--- /dev/null
@@ -0,0 +1,88 @@
+default namespace = "http://linux.duke.edu/metadata/common"
+namespace rpm = "http://linux.duke.edu/metadata/rpm"
+namespace suse = "http://novell.com/package/metadata/suse/common"
+
+include "rpm-inc.rnc"
+
+yum.name = element name { text }
+
+yum.arch = element arch { private.archenum }
+
+yum.version =  element version {
+  private.evr
+}
+
+group.deps = rpm.provides?, rpm.requires?, rpm.conflicts?, 
+             rpm.obsoletes?, rpm.suggests?, rpm.recommends?, 
+             rpm.supplements?, rpm.enhances?
+
+group.deps1 = rpm.provides?, rpm.requires?, rpm.conflicts?, 
+              rpm.obsoletes?, rpm.suggests?, rpm.enhances?, rpm.recommends?, 
+              rpm.supplements?
+
+
+group.deps2 = rpm.provides?, rpm.conflicts?, rpm.obsoletes?, rpm.enhances?, 
+              rpm.supplements?, rpm.suggests?, rpm.recommends?,
+              rpm.requires?
+
+yum.location = element location {
+  attribute href { xsd:anyURI }
+}
+
+yum.checksum = element checksum {
+  attribute pkgid { "YES" },
+  private.checksum
+}
+yum.time = element time {
+  attribute file { private.unixts },
+  attribute build { private.unixts }
+}
+
+yum.size = element size {
+  attribute package { private.size },
+  # Found this blank in the wild
+  attribute archive { private.size | "" },
+  attribute installed { private.size }
+}
+
+start = element metadata {
+  attribute packages { private.nonnegative },
+  element package {
+    attribute type { "rpm" },
+    yum.name,
+    yum.arch,
+    yum.version,
+    yum.checksum,
+    # Found unlocalized in the wild
+    element summary { private.localizedtext | text },
+    element description { private.localizedtext | text },
+    element packager { text },
+    # 10-SP1 stuff has a broken URL in the wild
+    element url { xsd:anyURI | text },
+    yum.time,
+    yum.size,
+    yum.location,
+    element format {
+      element rpm:license { text },
+      element rpm:vendor { text },
+      element rpm:group { text },
+      element rpm:buildhost { text },
+      # Two different options seen in the wild
+      # one omits the element, the other leaves the value empty
+      element rpm:sourcerpm { text | empty }?,
+      element rpm:header-range {
+        attribute start { private.positive },
+        attribute end { private.positive }
+      },
+      # any of these orders is valid, all are in use
+      (group.deps | group.deps1 | group.deps2),
+      element file { 
+        attribute type { "dir" | "ghost" }?,
+        text 
+      }*
+    },
+    element suse:license-to-confirm { text }?
+  }*
+}
+
diff --git a/zypp/parser/yum/schema/primary.rng b/zypp/parser/yum/schema/primary.rng
new file mode 100644 (file)
index 0000000..3f4fc45
--- /dev/null
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" xmlns:suse="http://novell.com/package/metadata/suse/common" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="rpm-inc.rng"/>
+  <define name="yum.name">
+    <element name="name">
+      <text/>
+    </element>
+  </define>
+  <define name="yum.arch">
+    <element name="arch">
+      <ref name="private.archenum"/>
+    </element>
+  </define>
+  <define name="yum.version">
+    <element name="version">
+      <ref name="private.evr"/>
+    </element>
+  </define>
+  <define name="group.deps">
+    <optional>
+      <ref name="rpm.provides"/>
+    </optional>
+    <optional>
+      <ref name="rpm.requires"/>
+    </optional>
+    <optional>
+      <ref name="rpm.conflicts"/>
+    </optional>
+    <optional>
+      <ref name="rpm.obsoletes"/>
+    </optional>
+    <optional>
+      <ref name="rpm.suggests"/>
+    </optional>
+    <optional>
+      <ref name="rpm.recommends"/>
+    </optional>
+    <optional>
+      <ref name="rpm.supplements"/>
+    </optional>
+    <optional>
+      <ref name="rpm.enhances"/>
+    </optional>
+  </define>
+  <define name="group.deps1">
+    <optional>
+      <ref name="rpm.provides"/>
+    </optional>
+    <optional>
+      <ref name="rpm.requires"/>
+    </optional>
+    <optional>
+      <ref name="rpm.conflicts"/>
+    </optional>
+    <optional>
+      <ref name="rpm.obsoletes"/>
+    </optional>
+    <optional>
+      <ref name="rpm.suggests"/>
+    </optional>
+    <optional>
+      <ref name="rpm.enhances"/>
+    </optional>
+    <optional>
+      <ref name="rpm.recommends"/>
+    </optional>
+    <optional>
+      <ref name="rpm.supplements"/>
+    </optional>
+  </define>
+  <define name="group.deps2">
+    <optional>
+      <ref name="rpm.provides"/>
+    </optional>
+    <optional>
+      <ref name="rpm.conflicts"/>
+    </optional>
+    <optional>
+      <ref name="rpm.obsoletes"/>
+    </optional>
+    <optional>
+      <ref name="rpm.enhances"/>
+    </optional>
+    <optional>
+      <ref name="rpm.supplements"/>
+    </optional>
+    <optional>
+      <ref name="rpm.suggests"/>
+    </optional>
+    <optional>
+      <ref name="rpm.recommends"/>
+    </optional>
+    <optional>
+      <ref name="rpm.requires"/>
+    </optional>
+  </define>
+  <define name="yum.location">
+    <element name="location">
+      <attribute name="href">
+        <data type="anyURI"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="yum.checksum">
+    <element name="checksum">
+      <attribute name="pkgid">
+        <value>YES</value>
+      </attribute>
+      <ref name="private.checksum"/>
+    </element>
+  </define>
+  <define name="yum.time">
+    <element name="time">
+      <attribute name="file">
+        <ref name="private.unixts"/>
+      </attribute>
+      <attribute name="build">
+        <ref name="private.unixts"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="yum.size">
+    <element name="size">
+      <attribute name="package">
+        <ref name="private.size"/>
+      </attribute>
+      <!-- Found this blank in the wild -->
+      <attribute name="archive">
+        <choice>
+          <ref name="private.size"/>
+          <value/>
+        </choice>
+      </attribute>
+      <attribute name="installed">
+        <ref name="private.size"/>
+      </attribute>
+    </element>
+  </define>
+  <start>
+    <element name="metadata">
+      <attribute name="packages">
+        <ref name="private.nonnegative"/>
+      </attribute>
+      <zeroOrMore>
+        <element name="package">
+          <attribute name="type">
+            <value>rpm</value>
+          </attribute>
+          <ref name="yum.name"/>
+          <ref name="yum.arch"/>
+          <ref name="yum.version"/>
+          <ref name="yum.checksum"/>
+          <!-- Found unlocalized in the wild -->
+          <element name="summary">
+            <choice>
+              <ref name="private.localizedtext"/>
+              <text/>
+            </choice>
+          </element>
+          <element name="description">
+            <choice>
+              <ref name="private.localizedtext"/>
+              <text/>
+            </choice>
+          </element>
+          <element name="packager">
+            <text/>
+          </element>
+          <!-- 10-SP1 stuff has a broken URL in the wild -->
+          <element name="url">
+            <choice>
+              <data type="anyURI"/>
+              <text/>
+            </choice>
+          </element>
+          <ref name="yum.time"/>
+          <ref name="yum.size"/>
+          <ref name="yum.location"/>
+          <element name="format">
+            <element name="rpm:license">
+              <text/>
+            </element>
+            <element name="rpm:vendor">
+              <text/>
+            </element>
+            <element name="rpm:group">
+              <text/>
+            </element>
+            <element name="rpm:buildhost">
+              <text/>
+            </element>
+            <optional>
+              <!--
+                Two different options seen in the wild
+                one omits the element, the other leaves the value empty
+              -->
+              <element name="rpm:sourcerpm">
+                <choice>
+                  <text/>
+                  <empty/>
+                </choice>
+              </element>
+            </optional>
+            <element name="rpm:header-range">
+              <attribute name="start">
+                <ref name="private.positive"/>
+              </attribute>
+              <attribute name="end">
+                <ref name="private.positive"/>
+              </attribute>
+            </element>
+            <!-- any of these orders is valid, all are in use -->
+            <choice>
+              <ref name="group.deps"/>
+              <ref name="group.deps1"/>
+              <ref name="group.deps2"/>
+            </choice>
+            <zeroOrMore>
+              <element name="file">
+                <optional>
+                  <attribute name="type">
+                    <choice>
+                      <value>dir</value>
+                      <value>ghost</value>
+                    </choice>
+                  </attribute>
+                </optional>
+                <text/>
+              </element>
+            </zeroOrMore>
+          </element>
+          <optional>
+            <element name="suse:license-to-confirm">
+              <text/>
+            </element>
+          </optional>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/product.rnc b/zypp/parser/yum/schema/product.rnc
new file mode 100644 (file)
index 0000000..c8e60d1
--- /dev/null
@@ -0,0 +1,22 @@
+default namespace = "http://novell.com/package/metadata/suse/product"
+
+include "rpm-inc.rnc"
+
+start = element product {
+  attribute type { "add-on" | "base" }, 
+  element vendor { text },
+  element name { text },
+  element arch { private.archenum },
+  element version { private.evr },
+  element displayname { private.localizedtext },
+  element shortname { text },
+  element distribution-name { text },
+  element distribution-edition { text },
+  element description { private.localizedtext },
+  element release-notes-url { xsd:anyURI }?,
+  rpm.provides,
+  rpm.obsoletes?,
+  rpm.requires?,
+  rpm.conflicts?
+}
+
diff --git a/zypp/parser/yum/schema/product.rng b/zypp/parser/yum/schema/product.rng
new file mode 100644 (file)
index 0000000..1b97f32
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://novell.com/package/metadata/suse/product" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="rpm-inc.rng"/>
+  <start>
+    <element name="product">
+      <attribute name="type">
+        <choice>
+          <value>add-on</value>
+          <value>base</value>
+        </choice>
+      </attribute>
+      <element name="vendor">
+        <text/>
+      </element>
+      <element name="name">
+        <text/>
+      </element>
+      <element name="arch">
+        <ref name="private.archenum"/>
+      </element>
+      <element name="version">
+        <ref name="private.evr"/>
+      </element>
+      <element name="displayname">
+        <ref name="private.localizedtext"/>
+      </element>
+      <element name="shortname">
+        <text/>
+      </element>
+      <element name="distribution-name">
+        <text/>
+      </element>
+      <element name="distribution-edition">
+        <text/>
+      </element>
+      <element name="description">
+        <ref name="private.localizedtext"/>
+      </element>
+      <optional>
+        <element name="release-notes-url">
+          <data type="anyURI"/>
+        </element>
+      </optional>
+      <ref name="rpm.provides"/>
+      <optional>
+        <ref name="rpm.obsoletes"/>
+      </optional>
+      <optional>
+        <ref name="rpm.requires"/>
+      </optional>
+      <optional>
+        <ref name="rpm.conflicts"/>
+      </optional>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/products.rnc b/zypp/parser/yum/schema/products.rnc
new file mode 100644 (file)
index 0000000..8e69fd3
--- /dev/null
@@ -0,0 +1,15 @@
+include "common-inc.rnc"
+
+start = element products {
+  element product {
+    attribute id { text }?,
+    attribute schemeversion { "0" }?,
+    element name { text },
+    element version { private.evr },
+    element arch { private.archenum },
+    element vendor { text },
+    element summary { text },
+    element description { text }
+  }+
+}
+
diff --git a/zypp/parser/yum/schema/products.rng b/zypp/parser/yum/schema/products.rng
new file mode 100644 (file)
index 0000000..a4a0e2a
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="products">
+      <oneOrMore>
+        <element name="product">
+          <optional>
+            <attribute name="id"/>
+          </optional>
+          <optional>
+            <attribute name="schemeversion">
+              <value>0</value>
+            </attribute>
+          </optional>
+          <element name="name">
+            <text/>
+          </element>
+          <element name="version">
+            <ref name="private.evr"/>
+          </element>
+          <element name="arch">
+            <ref name="private.archenum"/>
+          </element>
+          <element name="vendor">
+            <text/>
+          </element>
+          <element name="summary">
+            <text/>
+          </element>
+          <element name="description">
+            <text/>
+          </element>
+        </element>
+      </oneOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/repomd.rnc b/zypp/parser/yum/schema/repomd.rnc
new file mode 100644 (file)
index 0000000..8ae2538
--- /dev/null
@@ -0,0 +1,29 @@
+default namespace = "http://linux.duke.edu/metadata/repo"
+
+include "common-inc.rnc"
+
+repomd.location = element location {
+  attribute href { xsd:anyURI }
+}
+repomd.meta = element checksum {
+  private.checksum 
+},
+# Oddity in the wild: a timestamp wiht a decimal
+element timestamp { private.unixts | xsd:decimal },
+element size { private.positive }?,
+element open-size { private.positive }?,
+element open-checksum {
+  private.checksum
+}
+
+
+start = element repomd {
+  element revision { private.unixts }?,
+  element tags {
+    element repo { xsd:anyURI }
+  }?,
+  element data {
+    attribute type { "deltainfo" | "filelists" | "other" | "primary" | "susedata" | "suseinfo" | "updateinfo" | "patches" | "products" | "product" | "patterns" | "pattern" },
+    ((repomd.meta, repomd.location) | (repomd.location, repomd.meta))
+  }*
+}
diff --git a/zypp/parser/yum/schema/repomd.rng b/zypp/parser/yum/schema/repomd.rng
new file mode 100644 (file)
index 0000000..0233022
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://linux.duke.edu/metadata/repo" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common-inc.rng"/>
+  <define name="repomd.location">
+    <element name="location">
+      <attribute name="href">
+        <data type="anyURI"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="repomd.meta">
+    <element name="checksum">
+      <ref name="private.checksum"/>
+    </element>
+    <!-- Oddity in the wild: a timestamp wiht a decimal -->
+    <element name="timestamp">
+      <choice>
+        <ref name="private.unixts"/>
+        <data type="decimal"/>
+      </choice>
+    </element>
+    <optional>
+      <element name="size">
+        <ref name="private.positive"/>
+      </element>
+    </optional>
+    <optional>
+      <element name="open-size">
+        <ref name="private.positive"/>
+      </element>
+    </optional>
+    <element name="open-checksum">
+      <ref name="private.checksum"/>
+    </element>
+  </define>
+  <start>
+    <element name="repomd">
+      <optional>
+        <element name="revision">
+          <ref name="private.unixts"/>
+        </element>
+      </optional>
+      <optional>
+        <element name="tags">
+          <element name="repo">
+            <data type="anyURI"/>
+          </element>
+        </element>
+      </optional>
+      <zeroOrMore>
+        <element name="data">
+          <attribute name="type">
+            <choice>
+              <value>deltainfo</value>
+              <value>filelists</value>
+              <value>other</value>
+              <value>primary</value>
+              <value>susedata</value>
+              <value>suseinfo</value>
+              <value>updateinfo</value>
+              <value>patches</value>
+              <value>products</value>
+              <value>product</value>
+              <value>patterns</value>
+              <value>pattern</value>
+            </choice>
+          </attribute>
+          <choice>
+            <group>
+              <ref name="repomd.meta"/>
+              <ref name="repomd.location"/>
+            </group>
+            <group>
+              <ref name="repomd.location"/>
+              <ref name="repomd.meta"/>
+            </group>
+          </choice>
+        </element>
+      </zeroOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/rnc2rng b/zypp/parser/yum/schema/rnc2rng
new file mode 100755 (executable)
index 0000000..cd65ef6
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+for i in *.rnc; do
+  g=${i%.rnc}.rng
+  test $i -ot $g && continue
+  echo $i...
+  trang -I rnc -O rng $i $g
+done
diff --git a/zypp/parser/yum/schema/rpm-inc.rnc b/zypp/parser/yum/schema/rpm-inc.rnc
new file mode 100644 (file)
index 0000000..e512c7a
--- /dev/null
@@ -0,0 +1,45 @@
+namespace rpm = "http://linux.duke.edu/metadata/rpm"
+
+include "common-inc.rnc"
+
+rpm.entry =  element rpm:entry {
+  attribute pre { "1" }?,
+  attribute kind { "atom" | "message" | "package" | "script" | "product" | "patch" | "pattern" }?,
+  attribute name { text },
+  (private.evr,
+  attribute flags { "EQ" | "GE" | "LE" | "LT" | "GT" })?
+}
+
+rpm.requires = element rpm:requires {
+  # pre should not be here, but it is in the wild
+  attribute pre { "1" }?,
+  rpm.entry*
+}
+
+rpm.obsoletes = element rpm:obsoletes {
+  rpm.entry+
+}
+
+rpm.provides = element rpm:provides {
+  rpm.entry*
+}
+
+rpm.recommends = element rpm:recommends {
+  rpm.entry*
+}
+
+rpm.supplements = element rpm:supplements {
+  rpm.entry*
+}
+
+rpm.conflicts = element rpm:conflicts {
+  rpm.entry+
+}
+
+rpm.enhances = element rpm:enhances {
+  rpm.entry+
+}
+
+rpm.suggests = element rpm:suggests {
+  rpm.entry*
+}
diff --git a/zypp/parser/yum/schema/rpm-inc.rng b/zypp/parser/yum/schema/rpm-inc.rng
new file mode 100644 (file)
index 0000000..1ae4097
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:rpm="http://linux.duke.edu/metadata/rpm" xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <define name="rpm.entry">
+    <element name="rpm:entry">
+      <optional>
+        <attribute name="pre">
+          <value>1</value>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="kind">
+          <choice>
+            <value>atom</value>
+            <value>message</value>
+            <value>package</value>
+            <value>script</value>
+            <value>product</value>
+            <value>patch</value>
+            <value>pattern</value>
+          </choice>
+        </attribute>
+      </optional>
+      <attribute name="name"/>
+      <optional>
+        <ref name="private.evr"/>
+        <attribute name="flags">
+          <choice>
+            <value>EQ</value>
+            <value>GE</value>
+            <value>LE</value>
+            <value>LT</value>
+            <value>GT</value>
+          </choice>
+        </attribute>
+      </optional>
+    </element>
+  </define>
+  <define name="rpm.requires">
+    <element name="rpm:requires">
+      <optional>
+        <!-- pre should not be here, but it is in the wild -->
+        <attribute name="pre">
+          <value>1</value>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <ref name="rpm.entry"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="rpm.obsoletes">
+    <element name="rpm:obsoletes">
+      <oneOrMore>
+        <ref name="rpm.entry"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="rpm.provides">
+    <element name="rpm:provides">
+      <zeroOrMore>
+        <ref name="rpm.entry"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="rpm.recommends">
+    <element name="rpm:recommends">
+      <zeroOrMore>
+        <ref name="rpm.entry"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="rpm.supplements">
+    <element name="rpm:supplements">
+      <zeroOrMore>
+        <ref name="rpm.entry"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="rpm.conflicts">
+    <element name="rpm:conflicts">
+      <oneOrMore>
+        <ref name="rpm.entry"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="rpm.enhances">
+    <element name="rpm:enhances">
+      <oneOrMore>
+        <ref name="rpm.entry"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="rpm.suggests">
+    <element name="rpm:suggests">
+      <zeroOrMore>
+        <ref name="rpm.entry"/>
+      </zeroOrMore>
+    </element>
+  </define>
+</grammar>
diff --git a/zypp/parser/yum/schema/susedata.rnc b/zypp/parser/yum/schema/susedata.rnc
new file mode 100644 (file)
index 0000000..aba6301
--- /dev/null
@@ -0,0 +1,31 @@
+namespace susedata = "http://linux.duke.edu/metadata/susedata"
+
+include "common-inc.rnc"
+
+ns.susedata = element susedata:susedata {
+  attribute packages { private.nonnegative },
+  element susedata:package {
+    attribute pkgid { text },
+    attribute name { text },
+    attribute arch { private.archenum },
+    element susedata:version { private.evr },
+    element susedata:eula { text }?,
+    element susedata:keyword { text }?
+  }*
+}
+
+plain.susedata = element susedata {
+  attribute packages { private.nonnegative },
+  element package {
+    attribute pkgid { text },
+    attribute name { text },
+    attribute arch { private.archenum },
+    element version { private.evr },
+    element eula { text }?,
+    element keyword { text }?
+  }*
+}
+
+# Unfortunately there are some susedata files without namespaces in the wild
+start = (ns.susedata | plain.susedata)
+
diff --git a/zypp/parser/yum/schema/susedata.rng b/zypp/parser/yum/schema/susedata.rng
new file mode 100644 (file)
index 0000000..137b783
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:susedata="http://linux.duke.edu/metadata/susedata" xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <define name="ns.susedata">
+    <element name="susedata:susedata">
+      <attribute name="packages">
+        <ref name="private.nonnegative"/>
+      </attribute>
+      <zeroOrMore>
+        <element name="susedata:package">
+          <attribute name="pkgid"/>
+          <attribute name="name"/>
+          <attribute name="arch">
+            <ref name="private.archenum"/>
+          </attribute>
+          <element name="susedata:version">
+            <ref name="private.evr"/>
+          </element>
+          <optional>
+            <element name="susedata:eula">
+              <text/>
+            </element>
+          </optional>
+          <optional>
+            <element name="susedata:keyword">
+              <text/>
+            </element>
+          </optional>
+        </element>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="plain.susedata">
+    <element name="susedata">
+      <attribute name="packages">
+        <ref name="private.nonnegative"/>
+      </attribute>
+      <zeroOrMore>
+        <element name="package">
+          <attribute name="pkgid"/>
+          <attribute name="name"/>
+          <attribute name="arch">
+            <ref name="private.archenum"/>
+          </attribute>
+          <element name="version">
+            <ref name="private.evr"/>
+          </element>
+          <optional>
+            <element name="eula">
+              <text/>
+            </element>
+          </optional>
+          <optional>
+            <element name="keyword">
+              <text/>
+            </element>
+          </optional>
+        </element>
+      </zeroOrMore>
+    </element>
+  </define>
+  <!-- Unfortunately there are some susedata files without namespaces in the wild -->
+  <start>
+    <choice>
+      <ref name="ns.susedata"/>
+      <ref name="plain.susedata"/>
+    </choice>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/suseinfo.rnc b/zypp/parser/yum/schema/suseinfo.rnc
new file mode 100644 (file)
index 0000000..d05e718
--- /dev/null
@@ -0,0 +1,19 @@
+namespace yum = "http://linux.duke.edu/metadata/repo"
+
+include "common-inc.rnc"
+
+# There is a horribly broken suseinfo variant we have to handle
+good = element yum:suseinfo {
+  element yum:expire { private.positive }
+}
+
+bad = element suseinfo {
+  element keywords {
+    element k { empty }
+  },
+  element products {
+    element id { empty }
+  }
+}
+
+start = (good | bad)
diff --git a/zypp/parser/yum/schema/suseinfo.rng b/zypp/parser/yum/schema/suseinfo.rng
new file mode 100644 (file)
index 0000000..0277bd9
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:yum="http://linux.duke.edu/metadata/repo" xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common-inc.rng"/>
+  <!-- There is a horribly broken suseinfo variant we have to handle -->
+  <define name="good">
+    <element name="yum:suseinfo">
+      <element name="yum:expire">
+        <ref name="private.positive"/>
+      </element>
+    </element>
+  </define>
+  <define name="bad">
+    <element name="suseinfo">
+      <element name="keywords">
+        <element name="k">
+          <empty/>
+        </element>
+      </element>
+      <element name="products">
+        <element name="id">
+          <empty/>
+        </element>
+      </element>
+    </element>
+  </define>
+  <start>
+    <choice>
+      <ref name="good"/>
+      <ref name="bad"/>
+    </choice>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/updateinfo.rnc b/zypp/parser/yum/schema/updateinfo.rnc
new file mode 100644 (file)
index 0000000..fc7b4dc
--- /dev/null
@@ -0,0 +1,51 @@
+default namespace = "http://novell.com/package/metadata/suse/updateinfo"
+
+include "common-inc.rnc"
+
+start = element updates {
+  element update {
+    # Seen missing in the wild
+    attribute from { "maint-coord@suse.de" }?,
+    attribute status { "stable" },
+    attribute type { "recommended" | "security" | "optional" | "feature" },
+    attribute version { xsd:string },
+    element id { text },
+    element title { text },
+    # Seen missing in the wild
+    element release { text }?,
+    # Usually with date attribute, but seen in the wild with content instead
+    element issued {
+      (attribute date { private.unixts } | private.unixts)
+    },
+    # Seen missing in the wild
+    element references {
+      element reference {
+       # Appears to always start with https://bugzilla.novellc.om/show_bug.cgi?id=
+        #  if type="bugzilla" and http://cve.mitre.org/cgi-bin/cvename.cgi?name=
+        #  if type="cve"
+        attribute href { xsd:anyURI },
+        attribute id { xsd:string },
+        attribute title { xsd:string },
+        attribute type { "bugzilla" | "cve" }
+      }+
+    }?,
+    element description { text },
+    element pkglist {
+      element collection {
+        element package {
+         attribute name { xsd:string },
+          attribute arch { private.archenum },
+          # not private.evr because "version" and "release" are spelled out
+          attribute epoch { private.nonnegative }?,
+          attribute version { text }?,
+          attribute release { text }?,
+          element filename { text },
+         element restart_suggested { "1" }?,
+         element reboot_suggested { "1" }?,
+         element relogin_suggested { "1" }?
+        }+
+      }
+    }
+  }+
+}
+
diff --git a/zypp/parser/yum/schema/updateinfo.rng b/zypp/parser/yum/schema/updateinfo.rng
new file mode 100644 (file)
index 0000000..1fcf3d0
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar ns="http://novell.com/package/metadata/suse/updateinfo" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common-inc.rng"/>
+  <start>
+    <element name="updates">
+      <oneOrMore>
+        <element name="update">
+          <optional>
+            <!-- Seen missing in the wild -->
+            <attribute name="from">
+              <value>maint-coord@suse.de</value>
+            </attribute>
+          </optional>
+          <attribute name="status">
+            <value>stable</value>
+          </attribute>
+          <attribute name="type">
+            <choice>
+              <value>recommended</value>
+              <value>security</value>
+              <value>optional</value>
+              <value>feature</value>
+            </choice>
+          </attribute>
+          <attribute name="version">
+            <data type="string"/>
+          </attribute>
+          <element name="id">
+            <text/>
+          </element>
+          <element name="title">
+            <text/>
+          </element>
+          <optional>
+            <!-- Seen missing in the wild -->
+            <element name="release">
+              <text/>
+            </element>
+          </optional>
+          <!-- Usually with date attribute, but seen in the wild with content instead -->
+          <element name="issued">
+            <choice>
+              <attribute name="date">
+                <ref name="private.unixts"/>
+              </attribute>
+              <ref name="private.unixts"/>
+            </choice>
+          </element>
+          <optional>
+            <!-- Seen missing in the wild -->
+            <element name="references">
+              <oneOrMore>
+                <element name="reference">
+                  <!--
+                    Appears to always start with https://bugzilla.novellc.om/show_bug.cgi?id=
+                     if type="bugzilla" and http://cve.mitre.org/cgi-bin/cvename.cgi?name=
+                     if type="cve"
+                  -->
+                  <attribute name="href">
+                    <data type="anyURI"/>
+                  </attribute>
+                  <attribute name="id">
+                    <data type="string"/>
+                  </attribute>
+                  <attribute name="title">
+                    <data type="string"/>
+                  </attribute>
+                  <attribute name="type">
+                    <choice>
+                      <value>bugzilla</value>
+                      <value>cve</value>
+                    </choice>
+                  </attribute>
+                </element>
+              </oneOrMore>
+            </element>
+          </optional>
+          <element name="description">
+            <text/>
+          </element>
+          <element name="pkglist">
+            <element name="collection">
+              <oneOrMore>
+                <element name="package">
+                  <attribute name="name">
+                    <data type="string"/>
+                  </attribute>
+                  <attribute name="arch">
+                    <ref name="private.archenum"/>
+                  </attribute>
+                  <optional>
+                    <!-- not private.evr because "version" and "release" are spelled out -->
+                    <attribute name="epoch">
+                      <ref name="private.nonnegative"/>
+                    </attribute>
+                  </optional>
+                  <optional>
+                    <attribute name="version"/>
+                  </optional>
+                  <optional>
+                    <attribute name="release"/>
+                  </optional>
+                  <element name="filename">
+                    <text/>
+                  </element>
+                  <optional>
+                    <element name="restart_suggested">
+                      <value>1</value>
+                    </element>
+                  </optional>
+                  <optional>
+                    <element name="reboot_suggested">
+                      <value>1</value>
+                    </element>
+                  </optional>
+                  <optional>
+                    <element name="relogin_suggested">
+                      <value>1</value>
+                    </element>
+                  </optional>
+                </element>
+              </oneOrMore>
+            </element>
+          </element>
+        </element>
+      </oneOrMore>
+    </element>
+  </start>
+</grammar>
diff --git a/zypp/parser/yum/schema/validate-all b/zypp/parser/yum/schema/validate-all
new file mode 100755 (executable)
index 0000000..7124e95
--- /dev/null
@@ -0,0 +1,149 @@
+#!/bin/bash
+DEBUG=1
+
+TARGET=/mounts/you
+#TARGET=/mounts/dist/ibs
+#TARGET=/mounts/mirror/SuSE/*
+
+SCHEMAS=$(dirname "$0")
+
+val_jing_compact() {
+    gzip -dcf "$2" | jing -c "$SCHEMAS/$1.rnc" /proc/self/fd/0
+}
+
+val_jing() {
+    gzip -dcf "$2" | jing "$SCHEMAS/$1.rng" /proc/self/fd/0
+}
+
+val_xmllint() {
+    gzip -dcf "$2" | xmllint --noout --relaxng "$SCHEMAS/$1.rng" - 2>/dev/null
+}
+
+validate() {
+    [ $DEBUG -gt 4 ] && echo "$2" >&2
+    if [ ! -f "$2" ]; then
+        echo "File missing: $2"
+        return 5
+    fi
+    $VAL "$1" "$2"
+    if [ $? -ne 0 ]; then
+        echo "Validation failed: $2 (using $1)"
+        return 2
+    fi
+}
+
+get_xpath_href() {
+    xpath "$1" "$2" 2>/dev/null | sed -r -e 's/ href="/\n/g;s/$/\n/' \
+    | sed -e 's/"$//' | tail -n +2
+}
+
+get_data_type() {
+    get_xpath_href "$1" '/repomd/data[@type="'"$2"'"]/location/@href'
+}
+
+get_patches() {
+    get_xpath_href "$1" '/patches/patch/location/@href'
+}
+
+if [ $(type -p xmllint) ]; then
+    VAL=val_xmllint
+elif [ $(type -p jing) ]; then
+    VAL=val_jing_compact
+else
+    echo "No validators found!!"
+    exit 100
+fi
+
+if [ -z $(type -p xpath) ]; then
+    echo "xpath command not fond"
+fi
+
+find $TARGET -noleaf -name 'repomd.xml' 2>/dev/null | while read FN; do
+    [ $DEBUG -gt 0 ] && echo "$FN" >&2 
+    validate repomd "$FN"   
+#    [ $? -eq 0 ] || break 
+    D=$(dirname "$FN")
+    BASE=$(dirname "$D")
+    B=$(basename "$D")
+    if [ "$B" != "repodata" ]; then
+        echo "Found $FN not in repodata directory"
+        exit 3
+    fi
+
+    get_data_type "$FN" patterns | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate patterns "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" pattern | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate patterns "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" primary | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate primary "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" other | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate other "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+    
+    get_data_type "$FN" filelists | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate filelists "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" suseinfo | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate suseinfo "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" susedata | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate susedata "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" product | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate product "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" products | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate products "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" updateinfo | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate updateinfo "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" deltainfo | while read UIFN; do 
+        [ $DEBUG -gt 1 ] && echo "$UIFN" >&2 
+        validate deltainfo "$BASE/$UIFN"
+        [ $? -eq 0 ] || exit 11
+    done
+
+    get_data_type "$FN" patches | while read PFN; do
+        [ $DEBUG -gt 1 ] && echo "$PFN" >&2 
+        validate patches "$BASE/$PFN"
+        [ $? -eq 0 ] || exit 12
+        get_patches "$BASE/$PFN" | while read PATCH; do
+            [ $DEBUG -gt 2 ] && echo "$PATCH" >&2 
+            validate patch "$BASE/$PATCH"
+            [ $? -eq 0 ] || exit 13
+        done        
+    done
+done
diff --git a/zypp/pool/ByIdent.h b/zypp/pool/ByIdent.h
new file mode 100644 (file)
index 0000000..3f8b192
--- /dev/null
@@ -0,0 +1,103 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/ByIdent.h
+ *
+*/
+#ifndef ZYPP_POOL_BYIDENT_H
+#define ZYPP_POOL_BYIDENT_H
+
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    /** Main filter selecting PoolItems by \c name and \c kind.
+     */
+    class ByIdent
+    {
+      public:
+        ByIdent()
+        : _id( 0 )
+        {}
+
+        explicit ByIdent( sat::Solvable slv_r )
+        : _id( makeIdent( slv_r ) )
+        {}
+
+        explicit ByIdent( IdString ident_r )
+        : _id( ident_r.id() )
+        {}
+
+        ByIdent( ResKind kind_r, IdString name_r )
+        : _id( makeIdent( kind_r, name_r ) )
+        {}
+
+        ByIdent( ResKind kind_r, const C_Str & name_r )
+        : _id( makeIdent( kind_r, name_r ) )
+        {}
+
+      public:
+        bool operator()( sat::Solvable slv_r ) const
+        {
+          return _id >= 0 ? ( slv_r.ident().id() == _id && ! slv_r.isKind( ResKind::srcpackage ) )
+            : ( slv_r.ident().id() == -_id && slv_r.isKind( ResKind::srcpackage ) );
+        }
+
+        bool operator()( const PoolItem & pi_r ) const
+        { return operator()( pi_r.satSolvable() ); }
+
+        bool operator()( ResObject::constPtr p_r ) const
+        { return p_r ? operator()( p_r->satSolvable() ) : !_id; }
+
+      private:
+        sat::detail::IdType makeIdent( sat::Solvable slv_r )
+        {
+          return slv_r.isKind( ResKind::srcpackage ) ? -slv_r.ident().id()
+            : slv_r.ident().id();
+        }
+
+        sat::detail::IdType makeIdent( ResKind kind_r, IdString name_r )
+        {
+          if ( kind_r == ResKind::package )
+            return name_r.id();
+          else if ( kind_r == ResKind::srcpackage )
+            return -name_r.id();
+          return IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) ).id();
+        }
+
+        sat::detail::IdType makeIdent( ResKind kind_r, const C_Str & name_r )
+        {
+          if ( kind_r == ResKind::package )
+            return IdString( name_r ).id();
+          else if ( kind_r == ResKind::srcpackage )
+            return -(IdString( name_r ).id());
+          return IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) ).id();
+        }
+
+      public:
+        sat::detail::IdType get() const { return _id; }
+
+      private:
+        /** negative \c _id for \c srcpackage, as they use the same \c ident
+         * as \c package.
+         */
+        sat::detail::IdType _id;
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOL_BYIDENT_H
diff --git a/zypp/pool/GetResolvablesToInsDel.cc b/zypp/pool/GetResolvablesToInsDel.cc
new file mode 100644 (file)
index 0000000..a6d799c
--- /dev/null
@@ -0,0 +1,366 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/GetResolvablesToInsDel.cc
+ *
+*/
+#include <iostream>
+#include <set>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/pool/GetResolvablesToInsDel.h"
+#include "zypp/pool/PoolStats.h"
+#include "zypp/solver/detail/InstallOrder.h"
+
+#include "zypp/Resolver.h"
+#include "zypp/sat/Transaction.h"
+
+using std::endl;
+using zypp::solver::detail::InstallOrder;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::GetResolvablesToInsDel"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : strip_obsoleted_to_delete
+     **        FUNCTION TYPE : void
+     **
+     ** strip packages to_delete which get obsoleted by
+     ** to_install (i.e. delay deletion in case the
+     ** obsoleting package likes to save whatever...
+    */
+
+    static void
+    strip_obsoleted_to_delete( GetResolvablesToInsDel::PoolItemList & deleteList_r,
+                               const GetResolvablesToInsDel::PoolItemList & instlist_r )
+    {
+      if ( deleteList_r.size() == 0 || instlist_r.size() == 0 )
+        return; // ---> nothing to do
+
+      // These are all installed packages obsoleted by any package to be installed.
+      // Actually we expect all of them to appear in the deleteList_r, and we want
+      // to kick them out. Rpm will delete them when the obsoleter gets installed.
+      sat::WhatObsoletes obsoleted( instlist_r.begin(), instlist_r.end() );
+      for_( it, obsoleted.poolItemBegin(), obsoleted.poolItemEnd() )
+      {
+        DBG << "Ignore appl_delete (should be obsoleted): " << *it << endl;
+        deleteList_r.remove( *it );
+      }
+
+      MIL << "Undelayed deletes: " << deleteList_r << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : GetResolvablesToInsDel::GetResolvablesToInsDel
+    // METHOD TYPE : Ctor
+    //
+    GetResolvablesToInsDel::GetResolvablesToInsDel( ResPool pool_r, Order order_r )
+    {
+      zypp::base::LogControl::TmpLineWriter shutUp;
+      typedef std::set<PoolItem> PoolItemSet;
+
+      PoolItemList & dellist_r( _toDelete );
+      PoolItemList & instlist_r( _toInstall );
+      PoolItemList & srclist_r( _toSrcinstall );
+
+      for ( ResPool::const_iterator it = pool_r.begin(); it != pool_r.end(); ++it )
+        {
+          if (it->status().isToBeInstalled())
+            {
+              if ((*it)->kind() == ResKind::srcpackage) {
+               srclist_r.push_back( *it );
+              }
+              else
+               instlist_r.push_back( *it );
+            }
+          else if (it->status().isToBeUninstalled())
+            {
+              if ( it->status().isToBeUninstalledDueToObsolete() )
+                {
+                  DBG << "Ignore auto_delete (should be obsoleted): " << *it << endl;
+                }
+              else if ( it->status().isToBeUninstalledDueToUpgrade() )
+                {
+                  DBG << "Ignore auto_delete (should be upgraded): " << *it << endl;
+                }
+              else {
+               dellist_r.push_back( *it );
+              }
+            }
+        }
+
+      MIL << "ResolvablesToInsDel: delete " << dellist_r.size()
+      << ", install " << instlist_r.size()
+      << ", srcinstall " << srclist_r.size() << endl;
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      // strip packages to_delete which get obsoleted by
+      // to_install (i.e. delay deletion in case the
+      // obsoleting package likes to save whatever...
+      //
+      ///////////////////////////////////////////////////////////////////
+      strip_obsoleted_to_delete( dellist_r, instlist_r );
+
+      if ( dellist_r.size() ) {
+        ///////////////////////////////////////////////////////////////////
+        //
+        // sort delete list...
+        //
+        ///////////////////////////////////////////////////////////////////
+        PoolItemSet delset( dellist_r.begin(), dellist_r.end() );  // for delete order
+        PoolItemSet dummy; // dummy, empty, should contain already installed
+
+        InstallOrder order( pool_r, delset, dummy ); // sort according top prereq
+        order.init();
+        const PoolItemList dsorted( order.getTopSorted() );
+
+        dellist_r.clear();
+        for ( PoolItemList::const_reverse_iterator cit = dsorted.rbegin();
+              cit != dsorted.rend(); ++cit )
+          {
+            dellist_r.push_back( *cit );
+          }
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      // sort installed list...
+      //
+      ///////////////////////////////////////////////////////////////////
+      if ( instlist_r.empty() )
+        return;
+
+      ///////////////////////////////////////////////////////////////////
+      // Compute install order according to packages prereq.
+      // Try to group packages with respect to the desired install order
+      ///////////////////////////////////////////////////////////////////
+      // backup list for debug purpose.
+      // You can as well build the set, clear the list and rebuild it in install order.
+      PoolItemList instbackup_r;
+      instbackup_r.swap( instlist_r );
+
+      PoolItemSet insset( instbackup_r.begin(), instbackup_r.end() ); // for install order
+      PoolItemSet installed; // dummy, empty, should contain already installed
+
+      InstallOrder order( pool_r, insset, installed );
+      // start recursive depth-first-search
+      order.init();
+      MIL << "order.init() done" << endl;
+      order.printAdj( XXX, false );
+      ///////////////////////////////////////////////////////////////////
+      // build install list in install order
+      ///////////////////////////////////////////////////////////////////
+      PoolItemList best_list;
+      sat::detail::RepoIdType best_prio     = 0;
+      unsigned best_medianum = 0;
+
+      PoolItemList last_list;
+      sat::detail::RepoIdType last_prio     = 0;
+      unsigned last_medianum = 0;
+
+      PoolItemList other_list;
+
+      for ( PoolItemList items = order.computeNextSet(); ! items.empty(); items = order.computeNextSet() )
+        {
+          XXX << "order.computeNextSet: " << items.size() << " resolvables" << endl;
+          ///////////////////////////////////////////////////////////////////
+          // items contains all objects we could install now. Pick all objects
+          // from current media, or best media if none for current. Alwayys pick
+          // objects that do not require media access.
+          ///////////////////////////////////////////////////////////////////
+
+          best_list.clear();
+          last_list.clear();
+          other_list.clear();
+
+          for ( PoolItemList::iterator cit = items.begin(); cit != items.end(); ++cit )
+            {
+              ResObject::constPtr cobj( cit->resolvable() );
+              if (!cobj)
+                continue;
+
+              if ( ! cobj->mediaNr() ) {
+                XXX << "No media access required for " << *cit << endl;
+                order.setInstalled( *cit );
+                other_list.push_back( *cit );
+                continue;
+              }
+
+              if ( cobj->satSolvable().repository().id() == last_prio &&
+                   cobj->mediaNr() == last_medianum ) {
+                // prefer packages on current media.
+                XXX << "Stay with current media " << *cit << endl;
+                last_list.push_back( *cit );
+                continue;
+              }
+
+              if ( last_list.empty() ) {
+                // check for best media as long as there are no packages for current media.
+
+                if ( ! best_list.empty() ) {
+
+                  if ( order_r == ORDER_BY_MEDIANR )
+                    {
+                      if ( cobj->mediaNr() < best_medianum ) {
+                        best_list.clear(); // new best
+                      } else if ( cobj->mediaNr() == best_medianum ) {
+                        if ( cobj->satSolvable().repository().id() < best_prio ) {
+                          best_list.clear(); // new best
+                        } else if ( cobj->satSolvable().repository().id() == best_prio ) {
+                          XXX << "Add to best list " << *cit << endl;
+                          best_list.push_back( *cit ); // same as best -> add
+                          continue;
+                        } else {
+                          continue; // worse
+                        }
+                      } else {
+                        continue; // worse
+                      }
+                    }
+                  else // default: ORDER_BY_SOURCE
+                    {
+                      if ( cobj->satSolvable().repository().id() < best_prio ) {
+                        best_list.clear(); // new best
+                      } else if ( cobj->satSolvable().repository().id() == best_prio ) {
+                        if ( cobj->mediaNr() < best_medianum ) {
+                          best_list.clear(); // new best
+                        } else if ( cobj->mediaNr() == best_medianum ) {
+                          XXX << "Add to best list " << *cit << endl;
+                          best_list.push_back( *cit ); // same as best -> add
+                          continue;
+                        } else {
+                          continue; // worse
+                        }
+                      } else {
+                        continue; // worse
+                      }
+                    }
+                }
+
+                if ( best_list.empty() )
+                  {
+                    XXX << "NEW BEST LIST [S" << cobj->satSolvable().repository().id() << ":" << cobj->mediaNr()
+                        << "] (last [S" << best_prio << ":" << best_medianum << "])" << endl;
+                    best_prio     = cobj->satSolvable().repository().id();
+                    best_medianum = cobj->mediaNr();
+                    // first package or new best
+                    XXX << "Add to best list " << *cit << endl;
+                    best_list.push_back( *cit );
+                    continue;
+                  }
+              }
+
+            } // for all objects in current set
+
+          ///////////////////////////////////////////////////////////////////
+          // remove objects picked from install order and append them to
+          // install list.
+          ///////////////////////////////////////////////////////////////////
+          PoolItemList & take_list( last_list.empty() ? best_list : last_list );
+          if ( last_list.empty() )
+            {
+              MIL << "SET NEW media [S" << best_prio << ":" << best_medianum << "]" << endl;
+              last_prio     = best_prio;
+              last_medianum = best_medianum;
+            }
+          else
+            {
+              XXX << "SET CONTINUE [S" << best_prio << ":" << best_medianum << "]" << endl;
+            }
+
+          for ( PoolItemList::iterator it = take_list.begin(); it != take_list.end(); ++it )
+            {
+              order.setInstalled( *it );
+              XXX << "SET collect " << (*it) << endl;
+            }
+          // move everthing from take_list to the end of instlist_r, clean take_list
+          instlist_r.splice( instlist_r.end(), take_list );
+          // same for other_list
+          instlist_r.splice( instlist_r.end(), other_list );
+
+        } // for all sets computed
+
+
+      MIL << "order done" << endl;
+      if ( instbackup_r.size() != instlist_r.size() )
+        {
+          ERR << "***************** Lost packages in InstallOrder sort." << endl;
+        }
+
+    }
+
+    void GetResolvablesToInsDel::debugDiffTransaction() const
+    {
+      SEC << "START debugDiffTransaction" << endl;
+      sat::Transaction trans( ResPool::instance().resolver().getTransaction() );
+
+      {
+       const PoolItemList & clist( _toDelete );
+       for_( it, clist.begin(), clist.end() )
+       {
+         sat::Transaction::const_iterator ci( trans.find( *it ) );
+         if ( ci == trans.end() )
+           ERR << "Missing to del in NEW trans: " << *it << endl;
+       }
+      }
+      {
+       const PoolItemList & clist( _toInstall );
+       for_( it, clist.begin(), clist.end() )
+       {
+         sat::Transaction::const_iterator ci( trans.find( *it ) );
+         if ( ci == trans.end() )
+           ERR << "Missing to ins in NEW trans: " << *it << endl;
+       }
+      }
+      {
+       const PoolItemList & clist( _toSrcinstall );
+       for_( it, clist.begin(), clist.end() )
+       {
+         sat::Transaction::const_iterator ci( trans.find( *it ) );
+         if ( ci == trans.end() )
+           ERR << "Missing srcins in NEW trans: " << *it << endl;
+       }
+      }
+
+      SEC << "END debugDiffTransaction" << endl;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const GetResolvablesToInsDel & obj )
+    {
+      dumpPoolStats( str << "toInstall: " << endl,
+                     obj._toInstall.begin(), obj._toInstall.end() ) << endl;
+      dumpPoolStats( str << "toDelete: " << endl,
+                     obj._toDelete.begin(), obj._toDelete.end() ) << endl;
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/pool/GetResolvablesToInsDel.h b/zypp/pool/GetResolvablesToInsDel.h
new file mode 100644 (file)
index 0000000..98ff5c3
--- /dev/null
@@ -0,0 +1,74 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/GetResolvablesToInsDel.h
+ *
+*/
+#ifndef ZYPP_POOL_GETRESOLVABLESTOINSDEL_H
+#define ZYPP_POOL_GETRESOLVABLESTOINSDEL_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/ResPool.h"
+#include "zypp/base/Deprecated.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : GetResolvablesToInsDel
+    //
+    /** Collect transacting items and sort according to prereqs and
+     *  media access.
+     *
+     * \deprecated Use class \ref sat::Transaction which does a better job
+     *             esp. when packages are to be deleted.
+     */
+    struct ZYPP_DEPRECATED GetResolvablesToInsDel
+    {
+      typedef std::list<PoolItem> PoolItemList;
+
+      /** Influences the sequence of sources and media proscessed.
+       * If true prefer a better source, otherwise a better media.
+       * \code
+       * ORDER_BY_SOURCE:  [S1:1], [S1:2], ... , [S2:1], [S2:2], ...
+       * ORDER_BY_MEDIANR: [S1:1], [S2:1], ... , [S1:2], [S2:2], ...
+       * \endcode
+       */
+      enum Order { ORDER_BY_SOURCE, ORDER_BY_MEDIANR };
+
+      /** */
+      GetResolvablesToInsDel( ResPool pool_r,
+                              Order order_r = ORDER_BY_SOURCE );
+
+      /** Diff with new style \ref Transacrion and write result to log. */
+      void debugDiffTransaction() const;
+
+      PoolItemList _toDelete;
+      PoolItemList _toInstall;
+      PoolItemList _toSrcinstall;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates GetResolvablesToInsDel Stream output */
+    std::ostream & operator<<( std::ostream & str, const GetResolvablesToInsDel & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOL_GETRESOLVABLESTOINSDEL_H
+
diff --git a/zypp/pool/PoolImpl.cc b/zypp/pool/PoolImpl.cc
new file mode 100644 (file)
index 0000000..23d803a
--- /dev/null
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/PoolImpl.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/pool/PoolImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Class PoolImpl::PoolImpl
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PoolImpl::PoolImpl
+    // METHOD TYPE : Ctor
+    //
+    PoolImpl::PoolImpl()
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : PoolImpl::~PoolImpl
+    // METHOD TYPE : Dtor
+    //
+    PoolImpl::~PoolImpl()
+    {}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/pool/PoolImpl.h b/zypp/pool/PoolImpl.h
new file mode 100644 (file)
index 0000000..30e5059
--- /dev/null
@@ -0,0 +1,496 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/PoolImpl.h
+ *
+*/
+#ifndef ZYPP_POOL_POOLIMPL_H
+#define ZYPP_POOL_POOLIMPL_H
+
+#include <iosfwd>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/SerialNumber.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/pool/PoolTraits.h"
+#include "zypp/ResPoolProxy.h"
+#include "zypp/PoolQueryResult.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/Product.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  namespace resstatus
+  {
+    /** Manipulator for \ref ResStatus::UserLockQueryField.
+     * Field is not public available. It is intended to remember the
+     * initial lock status usually derived from /etc/zypp/locks. So
+     * we are able to detect changes we have to write back on commit.
+    */
+    struct UserLockQueryManip
+    {
+      /** Set lock and UserLockQuery bit according to \c yesno_r. */
+      static void setLock( ResStatus & status_r, bool yesno_r )
+      {
+        status_r.setLock( yesno_r, ResStatus::USER );
+        status_r.setUserLockQueryMatch( yesno_r );
+      }
+
+      /** Update lock and UserLockQuery bit IFF the item gained the bit. */
+      static void reapplyLock( ResStatus & status_r, bool yesno_r )
+      {
+        if ( yesno_r && ! status_r.isUserLockQueryMatch() )
+        {
+          status_r.setLock( yesno_r, ResStatus::USER );
+          status_r.setUserLockQueryMatch( yesno_r );
+        }
+      }
+
+      /** Test whether the lock status differs from the remembered UserLockQuery bit. */
+      static int diffLock( const ResStatus & status_r )
+      {
+        bool userLock( status_r.isUserLocked() );
+        if ( userLock == status_r.isUserLockQueryMatch() )
+          return 0;
+        return userLock ? 1 : -1;
+      }
+
+    };
+  }
+
+  namespace
+  {
+    inline PoolQuery makeTrivialQuery( IdString ident_r )
+    {
+      sat::Solvable::SplitIdent ident( ident_r );
+
+      PoolQuery q;
+      q.addAttribute( sat::SolvAttr::name, ident.name().asString() );
+      q.addKind( ident.kind() );
+      q.setMatchExact();
+      q.setCaseSensitive(true);
+      return q;
+   }
+
+    inline bool hardLockQueriesRemove( pool::PoolTraits::HardLockQueries & activeLocks_r, IdString ident_r )
+    {
+      unsigned s( activeLocks_r.size() );
+      activeLocks_r.remove( makeTrivialQuery( ident_r ) );
+      return( activeLocks_r.size() != s );
+    }
+
+    inline bool hardLockQueriesAdd( pool::PoolTraits::HardLockQueries & activeLocks_r, IdString ident_r )
+    {
+      PoolQuery q( makeTrivialQuery( ident_r ) );
+      for_( it, activeLocks_r.begin(), activeLocks_r.end() )
+      {
+        if ( *it == q )
+          return false;
+      }
+      activeLocks_r.push_back( q );
+      return true;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PoolImpl
+    //
+    /** */
+    class PoolImpl
+    {
+      friend std::ostream & operator<<( std::ostream & str, const PoolImpl & obj );
+
+      public:
+        /** */
+        typedef PoolTraits::ItemContainerT             ContainerT;
+        typedef PoolTraits::size_type                  size_type;
+        typedef PoolTraits::const_iterator             const_iterator;
+       typedef PoolTraits::Id2ItemT                    Id2ItemT;
+
+        typedef PoolTraits::repository_iterator                repository_iterator;
+
+        typedef sat::detail::SolvableIdType            SolvableIdType;
+
+      public:
+        /** Default ctor */
+        PoolImpl();
+        /** Dtor */
+        ~PoolImpl();
+
+      public:
+        /** convenience. */
+        const sat::Pool satpool() const
+        { return sat::Pool::instance(); }
+
+        /** Housekeeping data serial number. */
+        const SerialNumber & serial() const
+        { return satpool().serial(); }
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        ///////////////////////////////////////////////////////////////////
+      public:
+        /**  */
+        bool empty() const
+        { return satpool().solvablesEmpty(); }
+
+        /**  */
+        size_type size() const
+        { return satpool().solvablesSize(); }
+
+        const_iterator begin() const
+        { return make_filter_begin( pool::ByPoolItem(), store() ); }
+
+        const_iterator end() const
+        { return make_filter_end( pool::ByPoolItem(), store() ); }
+
+      public:
+        /** Return the corresponding \ref PoolItem.
+         * Pool and sat pool should be in sync. Returns an empty
+         * \ref PoolItem if there is no corresponding \ref PoolItem.
+         * \see \ref PoolItem::satSolvable.
+         */
+        PoolItem find( const sat::Solvable & slv_r ) const
+        {
+          const ContainerT & mystore( store() );
+          return( slv_r.id() < mystore.size() ? mystore[slv_r.id()] : PoolItem() );
+        }
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        ///////////////////////////////////////////////////////////////////
+      public:
+        /** \name Save and restore state. */
+        //@{
+        void SaveState( const ResObject::Kind & kind_r );
+
+        void RestoreState( const ResObject::Kind & kind_r );
+        //@}
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        ///////////////////////////////////////////////////////////////////
+      public:
+        ResPoolProxy proxy( ResPool self ) const
+        {
+          checkSerial();
+          if ( !_poolProxy )
+          {
+            _poolProxy.reset( new ResPoolProxy( self, *this ) );
+          }
+          return *_poolProxy;
+        }
+
+      public:
+        /** Forward list of Repositories that contribute ResObjects from \ref sat::Pool */
+        size_type knownRepositoriesSize() const
+        { checkSerial(); return satpool().reposSize(); }
+
+        repository_iterator knownRepositoriesBegin() const
+        { checkSerial(); return satpool().reposBegin(); }
+
+        repository_iterator knownRepositoriesEnd() const
+        { checkSerial(); return satpool().reposEnd(); }
+
+        Repository reposFind( const std::string & alias_r ) const
+        { checkSerial(); return satpool().reposFind( alias_r ); }
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        ///////////////////////////////////////////////////////////////////
+      public:
+        typedef PoolTraits::HardLockQueries           HardLockQueries;
+        typedef PoolTraits::hardLockQueries_iterator  hardLockQueries_iterator;
+
+        const HardLockQueries & hardLockQueries() const
+        { return _hardLockQueries; }
+
+        void reapplyHardLocks() const
+        {
+          // It is assumed that reapplyHardLocks is called after new
+          // items were added to the pool, but the _hardLockQueries
+          // did not change since. Action is to be performed only on
+          // those items that gained the bit in the UserLockQueryField.
+          MIL << "Re-apply " << _hardLockQueries.size() << " HardLockQueries" << endl;
+          PoolQueryResult locked;
+          for_( it, _hardLockQueries.begin(), _hardLockQueries.end() )
+          {
+            locked += *it;
+          }
+          MIL << "HardLockQueries match " << locked.size() << " Solvables." << endl;
+          for_( it, begin(), end() )
+          {
+            resstatus::UserLockQueryManip::reapplyLock( it->status(), locked.contains( *it ) );
+          }
+        }
+
+        void setHardLockQueries( const HardLockQueries & newLocks_r )
+        {
+          MIL << "Apply " << newLocks_r.size() << " HardLockQueries" << endl;
+          _hardLockQueries = newLocks_r;
+          // now adjust the pool status
+          PoolQueryResult locked;
+          for_( it, _hardLockQueries.begin(), _hardLockQueries.end() )
+          {
+            locked += *it;
+          }
+          MIL << "HardLockQueries match " << locked.size() << " Solvables." << endl;
+          for_( it, begin(), end() )
+          {
+            resstatus::UserLockQueryManip::setLock( it->status(), locked.contains( *it ) );
+          }
+        }
+
+        bool getHardLockQueries( HardLockQueries & activeLocks_r )
+        {
+          activeLocks_r = _hardLockQueries; // current queries
+          // Now diff to the pool collecting names only.
+          // Thus added and removed locks are not necessarily
+          // disjoint. Added locks win.
+          typedef std::tr1::unordered_set<IdString> IdentSet;
+          IdentSet addedLocks;
+          IdentSet removedLocks;
+          for_( it, begin(), end() )
+          {
+            switch ( resstatus::UserLockQueryManip::diffLock( it->status() ) )
+            {
+              case 0:  // unchanged
+                break;
+              case 1:
+                addedLocks.insert( it->satSolvable().ident() );
+                break;
+              case -1:
+                removedLocks.insert( it->satSolvable().ident() );
+               break;
+            }
+          }
+          // now the bad part - adjust the queries
+          bool setChanged = false;
+          for_( it, removedLocks.begin(), removedLocks.end() )
+          {
+            if ( addedLocks.find( *it ) != addedLocks.end() )
+              continue; // Added locks win
+            if ( hardLockQueriesRemove( activeLocks_r, *it ) && ! setChanged )
+              setChanged = true;
+          }
+          for_( it, addedLocks.begin(), addedLocks.end() )
+          {
+            if ( hardLockQueriesAdd( activeLocks_r, *it ) && ! setChanged )
+              setChanged = true;
+          }
+          return setChanged;
+       }
+
+      public:
+        typedef PoolTraits::AutoSoftLocks          AutoSoftLocks;
+        typedef PoolTraits::autoSoftLocks_iterator autoSoftLocks_iterator;
+
+        const AutoSoftLocks & autoSoftLocks() const
+        { return _autoSoftLocks; }
+
+        bool autoSoftLockAppliesTo( sat::Solvable solv_r ) const
+        { return( _autoSoftLocks.find( solv_r.ident() ) != _autoSoftLocks.end() ); }
+
+        void setAutoSoftLocks( const AutoSoftLocks & newLocks_r )
+        {
+          MIL << "Apply " << newLocks_r.size() << " AutoSoftLocks: " << newLocks_r << endl;
+          _autoSoftLocks = newLocks_r;
+          // now adjust the pool status
+          for_( it, begin(), end() )
+          {
+            if ( ! it->status().isKept() )
+              continue;
+
+            if ( autoSoftLockAppliesTo( it->satSolvable() ) )
+              it->status().setSoftLock( ResStatus::USER );
+            else
+              it->status().resetTransact( ResStatus::USER );
+          }
+        }
+
+        void getActiveSoftLocks( AutoSoftLocks & activeLocks_r )
+        {
+          activeLocks_r = _autoSoftLocks; // current soft-locks
+          AutoSoftLocks todel;            // + names to be deleted
+          AutoSoftLocks toins;            // - names to be installed
+
+          for_( it, begin(), end() )
+          {
+            ResStatus & status( it->status() );
+            if ( ! ( status.isByUser() || status.isByApplLow() ) )
+              continue; // ignore non-user requests; ApplLow means selected
+                        // by solver, but on behalf of a user request.
+
+            switch ( status.getTransactValue() )
+            {
+              case ResStatus::KEEP_STATE:
+                // Filter only items included in the last recommended set.
+                if ( status.isRecommended() )
+                  activeLocks_r.insert( it->satSolvable().ident() );
+                break;
+              case ResStatus::LOCKED:
+                //  NOOP
+                break;
+              case ResStatus::TRANSACT:
+                (status.isInstalled() ? todel : toins).insert( it->satSolvable().ident() );
+                break;
+            }
+          }
+          for_( it, todel.begin(), todel.end() )
+          {
+            activeLocks_r.insert( *it );
+          }
+          for_( it, toins.begin(), toins.end() )
+          {
+            activeLocks_r.erase( *it );
+          }
+        }
+
+      public:
+        const ContainerT & store() const
+        {
+          checkSerial();
+          if ( _storeDirty )
+          {
+            sat::Pool pool( satpool() );
+            bool addedItems = false;
+            std::list<PoolItem> addedProducts;
+
+            if ( pool.capacity() != _store.capacity() )
+            {
+              _store.resize( pool.capacity() );
+            }
+
+            if ( pool.capacity() )
+            {
+              for ( sat::detail::SolvableIdType i = pool.capacity()-1; i != 0; --i )
+              {
+                sat::Solvable s( i );
+                PoolItem & pi( _store[i] );
+                if ( ! s &&  pi )
+                {
+                  // the PoolItem got invalidated (e.g unloaded repo)
+                  pi = PoolItem();
+                }
+                else if ( s && ! pi )
+                {
+                  // new PoolItem to add
+                  pi = PoolItem::makePoolItem( s ); // the only way to create a new one!
+                  // remember products for buddy processing (requires clean store)
+                  if ( s.isKind( ResKind::product ) )
+                    addedProducts.push_back( pi );
+                  // and on the fly check for weak locks...
+                  if ( autoSoftLockAppliesTo( s ) )
+                  {
+                    pi.status().setSoftLock( ResStatus::USER );
+                  }
+                  if ( !addedItems )
+                    addedItems = true;
+                }
+              }
+            }
+            _storeDirty = false;
+
+            // Now, as the pool is adjusted, ....
+
+            // .... we check for product buddies.
+            if ( ! addedProducts.empty() )
+            {
+              for_( it, addedProducts.begin(), addedProducts.end() )
+              {
+                it->setBuddy( asKind<Product>(*it)->referencePackage() );
+              }
+            }
+
+            // .... we must reapply those query based hard locks.
+            if ( addedItems )
+            {
+              reapplyHardLocks();
+            }
+          }
+          return _store;
+        }
+
+       const Id2ItemT & id2item () const
+       {
+         checkSerial();
+         if ( _id2itemDirty )
+         {
+           store();
+           _id2item = Id2ItemT( size() );
+            for_( it, begin(), end() )
+            {
+              const sat::Solvable &s = (*it)->satSolvable();
+              sat::detail::IdType id = s.ident().id();
+              if ( s.isKind( ResKind::srcpackage ) )
+                id = -id;
+              _id2item.insert( std::make_pair( id, *it ) );
+            }
+            //INT << _id2item << endl;
+           _id2itemDirty = false;
+          }
+         return _id2item;
+       }
+
+        ///////////////////////////////////////////////////////////////////
+        //
+        ///////////////////////////////////////////////////////////////////
+      private:
+        void checkSerial() const
+        {
+          if ( _watcher.remember( serial() ) )
+            invalidate();
+          satpool().prepare(); // always ajust dependencies.
+        }
+
+        void invalidate() const
+        {
+          _storeDirty = true;
+         _id2itemDirty = true;
+         _id2item.clear();
+          _poolProxy.reset();
+        }
+
+      private:
+        /** Watch sat pools serial number. */
+        SerialNumberWatcher                   _watcher;
+        mutable ContainerT                    _store;
+        mutable DefaultIntegral<bool,true>    _storeDirty;
+       mutable Id2ItemT                      _id2item;
+        mutable DefaultIntegral<bool,true>    _id2itemDirty;
+
+      private:
+        mutable shared_ptr<ResPoolProxy>      _poolProxy;
+
+      private:
+        /** Set of solvable idents that should be soft locked per default. */
+        AutoSoftLocks                         _autoSoftLocks;
+        /** Set of queries that define hardlocks. */
+        HardLockQueries                       _hardLockQueries;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOL_POOLIMPL_H
diff --git a/zypp/pool/PoolStats.cc b/zypp/pool/PoolStats.cc
new file mode 100644 (file)
index 0000000..9b19531
--- /dev/null
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/PoolStats.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/pool/PoolStats.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const PoolStats & obj )
+    {
+      str << "ResObjects: " << obj._total;
+      for( PoolStats::KindMap::const_iterator it = obj._perKind.begin(); it != obj._perKind.end(); ++it )
+        {
+          str << endl << "  " << it->first << ":\t" << it->second;
+        }
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/pool/PoolStats.h b/zypp/pool/PoolStats.h
new file mode 100644 (file)
index 0000000..a01abed
--- /dev/null
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/PoolStats.h
+ *
+*/
+#ifndef ZYPP_POOL_POOLSTATS_H
+#define ZYPP_POOL_POOLSTATS_H
+
+#include <iosfwd>
+
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Functional.h"
+#include "zypp/base/Counter.h"
+#include "zypp/ResObject.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PoolStats
+    //
+    /** Functor counting ResObjects per Kind.
+     * \see dumpPoolStats
+     * \code
+     * Total: 2830
+     *   language:     81
+     *   package:      2710
+     *   product:      2
+     *   selection:    36
+     *   system:       1
+     * \endcode
+    */
+    struct PoolStats : public std::unary_function<ResObject::constPtr, void>
+    {
+      void operator()( ResObject::constPtr ptr )
+      {
+        ++_total;
+        ++_perKind[ptr->kind()];
+      }
+    public:
+      typedef std::map<ResKind,Counter<unsigned> > KindMap;
+      Counter<unsigned> _total;
+      KindMap           _perKind;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates PoolStats Stream output */
+    std::ostream & operator<<( std::ostream & str, const PoolStats & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates pool::PoolStats Convenience to count and print out the
+   *  number of ResObjects per Kind in a container.
+   * Fits container of ResObject::Ptr or PoolItem.
+  */
+  template <class _Iterator>
+    std::ostream & dumpPoolStats( std::ostream & str,
+                                  _Iterator begin_r, _Iterator end_r )
+    {
+      pool::PoolStats stats;
+      std::for_each( begin_r, end_r,
+                     functor::functorRef<void,ResObject::constPtr>(stats) );
+      return str << stats;
+    }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOL_POOLSTATS_H
diff --git a/zypp/pool/PoolTraits.h b/zypp/pool/PoolTraits.h
new file mode 100644 (file)
index 0000000..0b180d4
--- /dev/null
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/pool/PoolTraits.h
+ *
+*/
+#ifndef ZYPP_POOL_POOLTRAITS_H
+#define ZYPP_POOL_POOLTRAITS_H
+
+#include <set>
+#include <map>
+#include <list>
+#include <vector>
+
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/pool/ByIdent.h"
+#include "zypp/sat/Pool.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class PoolQuery;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace pool
+  { /////////////////////////////////////////////////////////////////
+
+    class PoolImpl;
+
+    /** Pool internal filter skiping invalid/unwanted PoolItems. */
+    struct ByPoolItem
+    {
+      bool operator()( const PoolItem & pi ) const
+      { return pi; }
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PoolTraits
+    //
+    /** */
+    struct PoolTraits
+    {
+    public:
+      typedef sat::detail::SolvableIdType              SolvableIdType;
+
+      /** pure items  */
+      typedef std::vector<PoolItem>                    ItemContainerT;
+      typedef ItemContainerT::const_iterator            item_iterator;
+      typedef filter_iterator<ByPoolItem,ItemContainerT::const_iterator>
+                                                       const_iterator;
+      typedef ItemContainerT::size_type                        size_type;
+
+      /** ident index */
+      typedef std::tr1::unordered_multimap<sat::detail::IdType, PoolItem>
+                                                        Id2ItemT;
+      typedef std::_Select2nd<Id2ItemT::value_type>     Id2ItemValueSelector;
+      typedef transform_iterator<Id2ItemValueSelector, Id2ItemT::const_iterator>
+                                                        byIdent_iterator;
+
+      /** list of known Repositories */
+      typedef sat::Pool::RepositoryIterator            repository_iterator;
+
+      /** soft locks */
+      typedef std::tr1::unordered_set<IdString>                AutoSoftLocks;
+      typedef AutoSoftLocks::const_iterator             autoSoftLocks_iterator;
+
+      /** hard locks from etc/zypp/locks */
+      typedef std::list<PoolQuery>                     HardLockQueries;
+      typedef HardLockQueries::const_iterator          hardLockQueries_iterator;
+
+      typedef PoolImpl                   Impl;
+      typedef shared_ptr<PoolImpl>       Impl_Ptr;
+      typedef shared_ptr<const PoolImpl> Impl_constPtr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace pool
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_POOL_POOLTRAITS_H
diff --git a/zypp/repo/Applydeltarpm.cc b/zypp/repo/Applydeltarpm.cc
new file mode 100644 (file)
index 0000000..7b382be
--- /dev/null
@@ -0,0 +1,193 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/Applydeltarpm.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/repo/Applydeltarpm.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/AutoDispose.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TriBool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace applydeltarpm
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      const Pathname   applydeltarpm_prog( "/usr/bin/applydeltarpm" );
+      const str::regex applydeltarpm_tick ( "([0-9]+) percent finished" );
+
+      /******************************************************************
+       **
+       **      FUNCTION NAME : applydeltarpm
+       **      FUNCTION TYPE : bool
+      */
+      bool applydeltarpm( const char *const argv_r[],
+                          const Progress & report_r  = Progress() )
+      {
+        ExternalProgram prog( argv_r, ExternalProgram::Stderr_To_Stdout );
+        str::smatch what;
+        for ( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+          {
+            if ( report_r && str::regex_match( line, what, applydeltarpm_tick ) )
+              {
+                report_r( str::strtonum<unsigned>( what[1] ) );
+              }
+            else
+              DBG << "Applydeltarpm : " << line;
+        }
+        return( prog.close() == 0 );
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : haveApplydeltarpm
+     **        FUNCTION TYPE : bool
+    */
+    bool haveApplydeltarpm()
+    {
+      // To track changes in availability of applydeltarpm.
+      static TriBool _last = indeterminate;
+      PathInfo prog( applydeltarpm_prog );
+      bool have = prog.isX();
+      if ( _last == have )
+        ; // TriBool! 'else' is not '_last != have'
+      else
+        {
+          // _last is 'indeterminate' or '!have'
+          if ( (_last = have) )
+            MIL << "Found executable " << prog << endl;
+          else
+            WAR << "No executable " << prog << endl;
+        }
+      return _last;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : check
+     **        FUNCTION TYPE : bool
+    */
+    bool check( const std::string & sequenceinfo_r, bool quick_r )
+    {
+      if ( ! haveApplydeltarpm() )
+        return false;
+
+      const char *const argv[] = {
+        "/usr/bin/applydeltarpm",
+        ( quick_r ? "-C" : "-c" ),
+        "-s", sequenceinfo_r.c_str(),
+        NULL
+      };
+
+      return( applydeltarpm( argv ) );
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : check
+     **        FUNCTION TYPE : bool
+    */
+    bool check( const Pathname & delta_r, bool quick_r )
+    {
+      if ( ! haveApplydeltarpm() )
+        return false;
+
+      const char *const argv[] = {
+        "/usr/bin/applydeltarpm",
+        ( quick_r ? "-C" : "-c" ),
+        delta_r.asString().c_str(),
+        NULL
+      };
+
+      return( applydeltarpm( argv ) );
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : provide
+     **        FUNCTION TYPE : bool
+    */
+    bool provide( const Pathname & delta_r, const Pathname & new_r,
+                  const Progress & report_r )
+    {
+      // cleanup on error
+      AutoDispose<const Pathname> guard( new_r, filesystem::unlink );
+
+      if ( ! haveApplydeltarpm() )
+        return false;
+
+      const char *const argv[] = {
+        "/usr/bin/applydeltarpm",
+        "-p", "-p", // twice to get percent output one per line
+        delta_r.asString().c_str(),
+        new_r.asString().c_str(),
+        NULL
+      };
+
+      if ( ! applydeltarpm( argv, report_r ) )
+        return false;
+
+      guard.resetDispose(); // no cleanup on success
+      return true;
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : provide
+     **        FUNCTION TYPE : bool
+    */
+    bool provide( const Pathname & old_r, const Pathname & delta_r,
+                  const Pathname & new_r,
+                  const Progress & report_r )
+    {
+      // cleanup on error
+      AutoDispose<const Pathname> guard( new_r, filesystem::unlink );
+
+      if ( ! haveApplydeltarpm() )
+        return false;
+
+      const char *const argv[] = {
+        "/usr/bin/applydeltarpm",
+        "-p", "-p", // twice to get percent output one per line
+        "-r", old_r.asString().c_str(),
+        delta_r.asString().c_str(),
+        new_r.asString().c_str(),
+        NULL
+      };
+
+      if ( ! applydeltarpm( argv, report_r ) )
+        return false;
+
+      guard.resetDispose(); // no cleanup on success
+      return true;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace applydeltarpm
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/Applydeltarpm.h b/zypp/repo/Applydeltarpm.h
new file mode 100644 (file)
index 0000000..d4ccf40
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/Applydeltarpm.h
+ *
+*/
+#ifndef ZYPP_SOURCE_APPLYDELTARPM_H
+#define ZYPP_SOURCE_APPLYDELTARPM_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Function.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  /** Namespace wrapping invocations of /usr/bin/applydeltarpm. */
+  ///////////////////////////////////////////////////////////////////
+  namespace applydeltarpm
+  { /////////////////////////////////////////////////////////////////
+
+    /** Test whether an execuatble applydeltarpm program is available. */
+    bool haveApplydeltarpm();
+
+    /** \name Check if reconstruction of rpm is possible.
+     * \see <tt>man applydeltarpm</tt>
+    */
+    //@{
+    /** Check via sequence info.
+     * \see <tt>applydeltarpm [-c|-C] -s sequence</tt>
+    */
+    bool check( const std::string & sequenceinfo_r, bool quick_r = false );
+
+    /** Check via deltarpm.
+     * \see <tt>applydeltarpm [-c|-C] deltarpm</tt>
+    */
+    bool check( const Pathname & delta_r, bool quick_r = false );
+
+    /** Quick via check sequence info.*/
+    inline bool quickcheck( const std::string & sequenceinfo_r )
+    { return check( sequenceinfo_r, true ); }
+
+    /** Quick check via deltarpm.*/
+    inline bool quickcheck( const Pathname & delta_r )
+    { return check( delta_r, true ); }
+    //@}
+
+    /** \name Re-create a new rpm from binary delta.
+     * \see <tt>man applydeltarpm</tt>
+    */
+    //@{
+    /** progress reporting */
+    typedef function<void( unsigned )> Progress;
+
+    /** Apply a binary delta to on-disk data to re-create a new rpm.
+     * \see <tt>applydeltarpm deltarpm newrpm</tt>
+    */
+    bool provide( const Pathname & delta_r, const Pathname & new_r,
+                  const Progress & report_r = Progress() );
+
+    /** Apply a binary delta to an old rpm to re-create a new rpm.
+     * \see <tt>applydeltarpm -r oldrpm deltarpm newrpm</tt>
+    */
+    bool provide( const Pathname & old_r, const Pathname & delta_r,
+                  const Pathname & new_r,
+                  const Progress & report_r = Progress() );
+    //@}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace applydeltarpm
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SOURCE_APPLYDELTARPM_H
diff --git a/zypp/repo/DeltaCandidates.cc b/zypp/repo/DeltaCandidates.cc
new file mode 100644 (file)
index 0000000..1cf4950
--- /dev/null
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+extern "C"
+{
+#include <satsolver/knownid.h>
+}
+
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/Repository.h"
+#include "zypp/repo/DeltaCandidates.h"
+#include "zypp/sat/Pool.h"
+
+
+using std::endl;
+using namespace zypp::packagedelta;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    /** DeltaCandidates implementation. */
+    struct DeltaCandidates::Impl
+    {
+      public:
+        Impl()
+        {}
+
+        Impl( const std::list<Repository> & repos, const std::string & pkgname = "" )
+        : repos(repos), pkgname(pkgname)
+        {}
+
+        std::list<Repository> repos;
+        std::string pkgname;
+
+      private:
+        friend Impl * rwcowClone<Impl>( const Impl * rhs );
+        /** clone for RWCOW_pointer */
+        Impl * clone() const
+        { return new Impl( *this ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates DeltaCandidates::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const DeltaCandidates::Impl & obj )
+    {
+      return str << "DeltaCandidates::Impl";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // class DeltaCandidates
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    DeltaCandidates::DeltaCandidates()
+    : _pimpl( new Impl )
+    {}
+
+
+    DeltaCandidates::DeltaCandidates(const std::list<Repository> & repos,
+                                     const std::string & pkgname)
+    : _pimpl( new Impl(repos, pkgname) )
+    {}
+
+    DeltaCandidates::~DeltaCandidates()
+    {}
+
+    std::list<DeltaRpm> DeltaCandidates::deltaRpms(const Package::constPtr & package) const
+    {
+      std::list<DeltaRpm> candidates;
+
+      DBG << "package: " << package << endl;
+      for_( rit, _pimpl->repos.begin(), _pimpl->repos.end() )
+      {
+        sat::LookupRepoAttr q( sat::SolvAttr::repositoryDeltaInfo, *rit );
+        for_( it, q.begin(), q.end() )
+        {
+          if ( _pimpl->pkgname.empty()
+               || it.subFind( sat::SolvAttr(DELTA_PACKAGE_NAME) ).asString() == _pimpl->pkgname )
+          {
+            DeltaRpm delta( it );
+            //DBG << "checking delta: " << delta << endl;
+            if ( ! package
+                   || (    package->name()    == delta.name()
+                        && package->edition() == delta.edition()
+                        && package->arch()    == delta.arch() ) )
+            {
+              DBG << "got delta candidate: " << delta << endl;
+              candidates.push_back( delta );
+            }
+          }
+        }
+      }
+      return candidates;
+    }
+
+    std::ostream & operator<<( std::ostream & str, const DeltaCandidates & obj )
+    {
+      return str << *obj._pimpl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/DeltaCandidates.h b/zypp/repo/DeltaCandidates.h
new file mode 100644 (file)
index 0000000..e59cd12
--- /dev/null
@@ -0,0 +1,88 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_DELTACANDIDATES_H
+#define ZYPP_REPO_DELTACANDIDATES_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/repo/PackageDelta.h"
+#include "zypp/Repository.h"
+#include "zypp/Package.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    /**
+     * \short Candidate delta and patches for a package
+     *
+     * Basically a container that given N repositories,
+     * gets all patches and deltas from them for a given
+     * package.
+     */
+    class DeltaCandidates
+    {
+      friend std::ostream & operator<<( std::ostream & str, const DeltaCandidates & obj );
+
+    public:
+      /** Implementation  */
+      class Impl;
+
+    public:
+      DeltaCandidates();
+      /**
+       * \short Creates a candidate calculator
+       * \param repos Set of repositories providing patch and delta packages
+       */
+      DeltaCandidates( const std::list<Repository> & repos, const std::string & pkgname = "" );
+      /** Dtor */
+      ~DeltaCandidates();
+
+      std::list<packagedelta::DeltaRpm> deltaRpms(const Package::constPtr & package) const;
+
+    private:
+      /** Pointer to implementation */
+      RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates DeltaCandidates Stream output */
+    std::ostream & operator<<( std::ostream & str, const DeltaCandidates & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates DeltaCandidates Convenient construction.
+     * \todo templated ctor
+    */
+    template<class RepoIter>
+    inline DeltaCandidates makeDeltaCandidates( RepoIter begin_r, RepoIter end_r )
+    { return DeltaCandidates( std::list<Repository>( begin_r, end_r ) ); }
+
+    /** \relates DeltaCandidates Convenient construction.
+     * \todo templated ctor
+     */
+    template<class RepoContainer>
+    inline DeltaCandidates makeDeltaCandidates( const RepoContainer & cont_r )
+    { return makeDeltaCandidates( cont_r.begin(), cont_r.end() ); }
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_DELTACANDIDATES_H
diff --git a/zypp/repo/Downloader.cc b/zypp/repo/Downloader.cc
new file mode 100644 (file)
index 0000000..319e86d
--- /dev/null
@@ -0,0 +1,55 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <fstream>
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/Date.h"
+
+#include "Downloader.h"
+#include "zypp/repo/MediaInfoDownloader.h"
+#include "zypp/base/UserRequestException.h"
+
+using namespace std;
+
+namespace zypp
+{
+namespace repo
+{
+
+Downloader::Downloader()
+{
+}
+Downloader::Downloader(const RepoInfo & repoinfo) : _repoinfo(repoinfo)
+{  
+}
+Downloader::~Downloader()
+{
+}
+
+RepoStatus Downloader::status( MediaSetAccess &media )
+{
+  WAR << "Non implemented" << endl;
+  return RepoStatus();
+}
+
+void Downloader::download( MediaSetAccess &media,
+                           const Pathname &dest_dir,
+                           const ProgressData::ReceiverFnc & progress )
+{
+  WAR << "Non implemented" << endl;
+}
+
+}// ns repo
+} // ns zypp
+
+
+
diff --git a/zypp/repo/Downloader.h b/zypp/repo/Downloader.h
new file mode 100644 (file)
index 0000000..ff4cf07
--- /dev/null
@@ -0,0 +1,66 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_DOWNLOADER
+#define ZYPP_REPO_DOWNLOADER
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/ProgressData.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/Fetcher.h"
+#include "zypp/RepoInfo.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    /**
+      * \short Downloader base class
+      *
+      * a Downloader encapsulates all the knowledge of 
+      * which files have to be downloaded to the local disk.
+      *
+      */
+    class Downloader : public Fetcher
+    {
+      public:
+      /**
+        * \short Constructor
+        */
+      Downloader();
+      /** C-tor associating the downloader with a RepoInfo */
+      Downloader(const RepoInfo & info);
+      virtual ~Downloader();
+
+      /**
+        * \short Download metadata to a local directory
+        *
+        * \param media Media access to the repository url
+        * \param dest_dir Local destination directory
+        * \param progress progress receiver
+        */
+      virtual void download( MediaSetAccess &media,
+                              const Pathname &dest_dir,
+                              const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+      /**
+        * \short Status of the remote repository
+        */
+      virtual RepoStatus status( MediaSetAccess &media );
+
+      const RepoInfo & repoInfo() const { return _repoinfo; }
+
+      private:
+        RepoInfo _repoinfo;
+    };
+  } // ns repo
+} // ns zypp
+
+#endif
diff --git a/zypp/repo/MediaInfoDownloader.cc b/zypp/repo/MediaInfoDownloader.cc
new file mode 100644 (file)
index 0000000..9c35864
--- /dev/null
@@ -0,0 +1,40 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <fstream>
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Function.h"
+
+#include "MediaInfoDownloader.h"
+#include "zypp/base/UserRequestException.h"
+
+using namespace std;
+
+namespace zypp
+{
+namespace repo
+{
+
+void downloadMediaInfo( const Pathname &dest_dir,
+                        MediaSetAccess &media,
+                        const ProgressData::ReceiverFnc & progressrcv )
+{
+  Fetcher fetcher;
+  fetcher.enqueue( OnMediaLocation("/media.1/media") );
+  fetcher.start( dest_dir, media, progressrcv );
+  // ready, go!
+  fetcher.reset();
+}
+
+}// ns repo 
+} // ns zypp
+
+
+
diff --git a/zypp/repo/MediaInfoDownloader.h b/zypp/repo/MediaInfoDownloader.h
new file mode 100644 (file)
index 0000000..01b403e
--- /dev/null
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_MEDIAINFO_DOWNLOADER
+#define ZYPP_REPO_MEDIAINFO_DOWNLOADER
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/Fetcher.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/ProgressData.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+   
+    /**
+     * \short Downloads the media info (/media.1) to a local directory
+     * \param dest_dir Destination directory
+     * \param media \ref MediaSetAccess object to some media
+     * \param progress Progress callback function
+     *
+     * \throws Exception on error
+     */
+    void downloadMediaInfo( const Pathname &dest_dir,
+                            MediaSetAccess &media,
+                            const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+  } // ns repo
+} // ns zypp
+
+#endif
diff --git a/zypp/repo/PackageDelta.cc b/zypp/repo/PackageDelta.cc
new file mode 100644 (file)
index 0000000..ec3f8d0
--- /dev/null
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/PackageDelta.cc
+ *
+*/
+#include <iostream>
+extern "C"
+{
+#include <satsolver/knownid.h>
+}
+
+#include "zypp/base/LogTools.h"
+
+#include "zypp/repo/PackageDelta.h"
+#include "zypp/sat/Pool.h"
+
+
+using std::endl;
+using std::string;
+
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace packagedelta
+  { /////////////////////////////////////////////////////////////////
+
+    DeltaRpm::DeltaRpm( sat::LookupAttr::iterator deltaInfo_r )
+    {
+      if ( deltaInfo_r.inSolvAttr() != sat::SolvAttr::repositoryDeltaInfo )
+      {
+        INT << "Illegal non-repositoryDeltaInfo iterator: " << deltaInfo_r << endl;
+        return;
+      }
+      _repo = deltaInfo_r.inRepo();
+
+      IdString locdir;
+      IdString locname;
+      IdString locevr;
+      IdString locsuffix;
+
+      IdString    seqname;
+      IdString    seqevr;
+      std::string seqnum;
+
+      for_( it, deltaInfo_r.subBegin(), deltaInfo_r.subEnd() )
+      {
+        switch ( it.inSolvAttr().id() )
+        {
+          case DELTA_PACKAGE_NAME:
+            _name = it.asString();
+            break;
+
+          case DELTA_PACKAGE_EVR:
+            _edition = Edition( it.idStr() );
+            break;
+
+          case DELTA_PACKAGE_ARCH:
+            _arch = Arch( it.idStr() );
+            break;
+
+          case DELTA_LOCATION_DIR:
+            locdir = it.idStr();
+            break;
+
+          case DELTA_LOCATION_NAME:
+            locname = it.idStr();
+            break;
+
+          case DELTA_LOCATION_EVR:
+            locevr = it.idStr();
+            break;
+
+          case DELTA_LOCATION_SUFFIX:
+            locsuffix = it.idStr();
+            break;
+
+          case DELTA_DOWNLOADSIZE:
+            _location.setDownloadSize( ByteCount( it.asUnsigned(), ByteCount::K ) );
+            break;
+
+          case DELTA_CHECKSUM:
+            _location.setChecksum( it.asCheckSum() );
+            break;
+
+          case DELTA_BASE_EVR:
+            _baseversion.setEdition( Edition( it.idStr() ) );
+            break;
+
+          case DELTA_SEQ_NAME:
+            seqname = it.idStr();
+            break;
+
+          case DELTA_SEQ_EVR:
+            seqevr = it.idStr();
+            break;
+
+          case DELTA_SEQ_NUM:
+            seqnum = it.asString();
+            break;
+
+          default:
+            WAR << "Igore unknown attribute: " << it << endl;
+        }
+      }
+
+      _location.setLocation( str::form( "%s/%s-%s.%s",
+                                        locdir.c_str(),
+                                        locname.c_str(),
+                                        locevr.c_str(),
+                                        locsuffix.c_str() ) );
+
+      _baseversion.setSequenceinfo( str::form( "%s-%s-%s",
+                                               seqname.c_str(),
+                                               seqevr.c_str(),
+                                               seqnum.c_str() ) );
+    }
+
+    std::ostream & operator<<( std::ostream & str, const DeltaRpm & obj )
+    {
+      return str
+      << "DeltaRpm[" << obj.name() << "-" << obj.edition() << "." << obj.arch()
+      << "](" << obj.location()
+      << '|' << obj.baseversion().edition()
+      << ',' << obj.baseversion().sequenceinfo()
+      << ')';
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace packagedelta
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/PackageDelta.h b/zypp/repo/PackageDelta.h
new file mode 100644 (file)
index 0000000..3c4a810
--- /dev/null
@@ -0,0 +1,99 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/PackageDelta.h
+ *
+*/
+#ifndef ZYPP_SOURCE_PACKAGEDELTA_H
+#define ZYPP_SOURCE_PACKAGEDELTA_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/OnMediaLocation.h"
+#include "zypp/Edition.h"
+#include "zypp/Arch.h"
+#include "zypp/Date.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/Repository.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace packagedelta
+  { /////////////////////////////////////////////////////////////////
+
+
+    /** \todo cheap copy! (switch to RWCOW) */
+    class DeltaRpm
+    {
+      public:
+        class BaseVersion
+        {
+          public:
+            BaseVersion()
+            {}
+
+          public:
+            const Edition &     edition()      const { return _edition; }
+            const std::string & sequenceinfo() const { return _sequenceinfo; }
+
+          public:
+            BaseVersion & setEdition( const Edition & val_r )          { _edition = val_r; return *this; }
+            BaseVersion & setSequenceinfo( const std::string & val_r ) { _sequenceinfo = val_r; return *this; }
+
+          private:
+            Edition     _edition;
+            std::string _sequenceinfo;
+        };
+
+      public:
+        DeltaRpm() {}
+        DeltaRpm( sat::LookupAttr::iterator deltaInfo_r );
+
+      public:
+        /** \name Target package ident. */
+        //@{
+        const std::string &     name()         const { return _name; }
+        const Edition &         edition()      const { return _edition; }
+        const Arch &            arch()         const { return _arch; }
+        //@}
+        const OnMediaLocation & location()     const { return _location; }
+        const BaseVersion &     baseversion()  const { return _baseversion; }
+        const Repository &      repository()   const { return _repo; }
+
+      public:
+        DeltaRpm & setName( const std::string & val_r )         { _name = val_r; return *this; }
+        DeltaRpm & setEdition( const Edition & val_r )          { _edition = val_r; return *this; }
+        DeltaRpm & setArch( const Arch & val_r )                { _arch = val_r; return *this; }
+        DeltaRpm & setLocation( const OnMediaLocation & val_r ) { _location = val_r; return *this; }
+        DeltaRpm & setBaseversion( const BaseVersion & val_r )  { _baseversion = val_r; return *this; }
+
+      private:
+        std::string     _name;
+        Edition         _edition;
+        Arch            _arch;
+        OnMediaLocation _location;
+        BaseVersion     _baseversion;
+        Repository      _repo;
+    };
+
+    /** \relates DeltaRpm Stream output. */
+    std::ostream & operator<<( std::ostream & str, const DeltaRpm & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace packagedelta
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SOURCE_PACKAGEDELTA_H
diff --git a/zypp/repo/PackageProvider.cc b/zypp/repo/PackageProvider.cc
new file mode 100644 (file)
index 0000000..bac24d1
--- /dev/null
@@ -0,0 +1,319 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/PackageProvider.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+#include "zypp/repo/PackageDelta.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/repo/PackageProvider.h"
+#include "zypp/repo/RepoProvideFile.h"
+#include "zypp/repo/Applydeltarpm.h"
+#include "zypp/repo/PackageDelta.h"
+
+#include "zypp/TmpPath.h"
+#include "zypp/ZConfig.h"
+#include "zypp/RepoInfo.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PackageProviderPolicy
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
+                                                const Edition &     ed_r,
+                                                const Arch &        arch_r ) const
+    {
+      if ( _queryInstalledCB )
+        return _queryInstalledCB( name_r, ed_r, arch_r );
+      return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PackageProvider
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      inline std::string defRpmFileName( const Package::constPtr & package )
+      {
+        std::ostringstream ret;
+        ret << package->name() << '-' << package->edition() << '.' << package->arch() << ".rpm";
+        return ret.str();
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace source
+    ///////////////////////////////////////////////////////////////////
+    PackageProvider::PackageProvider(  RepoMediaAccess &access,
+                                      const Package::constPtr & package,
+                                      const DeltaCandidates & deltas,
+                                      const PackageProviderPolicy & policy_r )
+    : _policy( policy_r )
+    , _package( package )
+    , _retry(false)
+    , _deltas(deltas)
+    , _access(access)
+    {}
+
+    PackageProvider::~PackageProvider()
+    {}
+
+    ManagedFile PackageProvider::providePackage() const
+    {
+      Url url;
+      RepoInfo info = _package->repoInfo();
+      // FIXME we only support the first url for now.
+      if ( info.baseUrlsEmpty() )
+        ZYPP_THROW(Exception("No url in repository."));
+      else
+        url = * info.baseUrlsBegin();
+
+      { // check for cache hit:
+        OnMediaLocation loc( _package->location() );
+        PathInfo cachepath( info.packagesPath() / loc.filename() );
+
+        if ( cachepath.isFile() && ! loc.checksum().empty() ) // accept cache hit with matching checksum only!
+             // Tempting to do a quick check for matching .rpm-filesize before computing checksum,
+             // but real life shows that loc.downloadSize() and the .rpm-filesize frequently do not
+             // match, even if loc.checksum() and the .rpm-files checksum do. Blame the metadata generator(s).
+        {
+          CheckSum cachechecksum( loc.checksum().type(), filesystem::checksum( cachepath.path(), loc.checksum().type() ) );
+          if ( cachechecksum == loc.checksum() )
+          {
+            ManagedFile ret( cachepath.path() );
+            if ( ! info.keepPackages() )
+            {
+              ret.setDispose( filesystem::unlink );
+            }
+            MIL << "provided Package from cache " << _package << " at " << ret << endl;
+            return ret; // <-- cache hit
+          }
+        }
+      }
+
+      // HERE: cache misss, do download:
+      MIL << "provide Package " << _package << endl;
+      ScopedGuard guardReport( newReport() );
+      ManagedFile ret;
+      do {
+        _retry = false;
+        report()->start( _package, url );
+        try  // ELIMINATE try/catch by providing a log-guard
+          {
+            ret = doProvidePackage();
+          }
+        catch ( const UserRequestException & excpt )
+          {
+            // UserRequestException e.g. from failOnChecksumError was already reported.
+            ERR << "Failed to provide Package " << _package << endl;
+            if ( ! _retry )
+              {
+                ZYPP_RETHROW( excpt );
+              }
+          }
+        catch ( const Exception & excpt )
+          {
+            ERR << "Failed to provide Package " << _package << endl;
+            if ( ! _retry )
+              {
+                // Aything else gets reported
+                std::string package_str = _package->name() + "-" + _package->edition().asString();
+
+                // TranslatorExplanation %s = name of the package being processed.
+                std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
+                detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
+
+                switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
+                {
+                      case repo::DownloadResolvableReport::RETRY:
+                        _retry = true;
+                        break;
+                      case repo::DownloadResolvableReport::IGNORE:
+                        ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
+                        break;
+                      case repo::DownloadResolvableReport::ABORT:
+                        ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
+                        break;
+                      default:
+                        ZYPP_RETHROW( excpt );
+                        break;
+                }
+              }
+          }
+      } while ( _retry );
+
+      report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
+      MIL << "provided Package " << _package << " at " << ret << endl;
+      return ret;
+    }
+
+    ManagedFile PackageProvider::doProvidePackage() const
+    {
+      Url url;
+      RepoInfo info = _package->repoInfo();
+      // FIXME we only support the first url for now.
+      if ( info.baseUrlsEmpty() )
+        ZYPP_THROW(Exception("No url in repository."));
+      else
+        url = * info.baseUrlsBegin();
+
+      // check whether to process patch/delta rpms
+      if ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() )
+        {
+          std::list<DeltaRpm> deltaRpms;
+          if ( ZConfig::instance().download_use_deltarpm() )
+          {
+            _deltas.deltaRpms( _package ).swap( deltaRpms );
+          }
+
+          if ( ! ( deltaRpms.empty() )
+               && queryInstalled() )
+            {
+              if ( ! deltaRpms.empty() && applydeltarpm::haveApplydeltarpm() )
+                {
+                  for( std::list<DeltaRpm>::const_iterator it = deltaRpms.begin();
+                       it != deltaRpms.end(); ++it )
+                    {
+                      DBG << "tryDelta " << *it << endl;
+                      ManagedFile ret( tryDelta( *it ) );
+                      if ( ! ret->empty() )
+                        return ret;
+                    }
+                }
+            }
+        }
+
+      // no patch/delta -> provide full package
+      ManagedFile ret;
+      OnMediaLocation loc = _package->location();
+
+      ProvideFilePolicy policy;
+      policy.progressCB( bind( &PackageProvider::progressPackageDownload, this, _1 ) );
+      policy.failOnChecksumErrorCB( bind( &PackageProvider::failOnChecksumError, this ) );
+      return _access.provideFile( _package->repoInfo(), loc, policy );
+    }
+
+    ManagedFile PackageProvider::tryDelta( const DeltaRpm & delta_r ) const
+    {
+      if ( delta_r.baseversion().edition() != Edition::noedition
+           && ! queryInstalled( delta_r.baseversion().edition() ) )
+        return ManagedFile();
+
+      if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
+        return ManagedFile();
+
+      report()->startDeltaDownload( delta_r.location().filename(),
+                                    delta_r.location().downloadSize() );
+      ManagedFile delta;
+      try
+        {
+          ProvideFilePolicy policy;
+          policy.progressCB( bind( &PackageProvider::progressDeltaDownload, this, _1 ) );
+          delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
+        }
+      catch ( const Exception & excpt )
+        {
+          report()->problemDeltaDownload( excpt.asUserHistory() );
+          return ManagedFile();
+        }
+      report()->finishDeltaDownload();
+
+      report()->startDeltaApply( delta );
+      if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
+        {
+          report()->problemDeltaApply( _("applydeltarpm check failed.") );
+          return ManagedFile();
+        }
+
+      // build the package and put it into the cache
+      Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
+
+      if ( ! applydeltarpm::provide( delta, destination,
+                                     bind( &PackageProvider::progressDeltaApply, this, _1 ) ) )
+        {
+          report()->problemDeltaApply( _("applydeltarpm failed.") );
+          return ManagedFile();
+        }
+      report()->finishDeltaApply();
+
+      return ManagedFile( destination, filesystem::unlink );
+    }
+
+    PackageProvider::ScopedGuard PackageProvider::newReport() const
+    {
+      _report.reset( new Report );
+      return shared_ptr<void>( static_cast<void*>(0),
+                               // custom deleter calling _report.reset()
+                               // (cast required as reset is overloaded)
+                               bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
+                                     ref(_report) ) );
+    }
+
+    PackageProvider::Report & PackageProvider::report() const
+    { return *_report; }
+
+    bool PackageProvider::progressDeltaDownload( int value ) const
+    { return report()->progressDeltaDownload( value ); }
+
+    void PackageProvider::progressDeltaApply( int value ) const
+    { return report()->progressDeltaApply( value ); }
+
+    bool PackageProvider::progressPackageDownload( int value ) const
+    { return report()->progress( value, _package ); }
+
+    bool PackageProvider::failOnChecksumError() const
+    {
+      std::string package_str = _package->name() + "-" + _package->edition().asString();
+
+      // TranslatorExplanation %s = package being checked for integrity
+      switch ( report()->problem( _package, repo::DownloadResolvableReport::INVALID, str::form(_("Package %s seems to be corrupted during transfer. Do you want to retry retrieval?"), package_str.c_str() ) ) )
+        {
+        case repo::DownloadResolvableReport::RETRY:
+          _retry = true;
+          break;
+          case repo::DownloadResolvableReport::IGNORE:
+          ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
+          break;
+          case repo::DownloadResolvableReport::ABORT:
+          ZYPP_THROW(AbortRequestException("User requested to abort"));
+          break;
+        default:
+          break;
+        }
+      return true; // anyway a failure
+    }
+
+    bool PackageProvider::queryInstalled( const Edition & ed_r ) const
+    { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/PackageProvider.h b/zypp/repo/PackageProvider.h
new file mode 100644 (file)
index 0000000..2931e2e
--- /dev/null
@@ -0,0 +1,114 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/PackageProvider.h
+ *
+*/
+#ifndef ZYPP_REPO_PACKAGEPROVIDER_H
+#define ZYPP_REPO_PACKAGEPROVIDER_H
+
+#include <iosfwd>
+
+#include "zypp/base/NonCopyable.h"
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/Package.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/repo/RepoProvideFile.h"
+#include "zypp/repo/DeltaCandidates.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PackageProviderPolicy
+    //
+    /** */
+    class PackageProviderPolicy
+    {
+    public:
+      /** Get installed Editions callback signature. */
+      typedef function<bool ( const std::string &, const Edition &, const Arch & )> QueryInstalledCB;
+
+      /** Set callback. */
+      PackageProviderPolicy & queryInstalledCB( QueryInstalledCB queryInstalledCB_r )
+      { _queryInstalledCB = queryInstalledCB_r; return *this; }
+
+      /** Evaluate callback. */
+      bool queryInstalled( const std::string & name_r,
+                           const Edition &     ed_r,
+                           const Arch &        arch_r ) const;
+
+    private:
+      QueryInstalledCB _queryInstalledCB;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : PackageProvider
+    //
+    /** Provide a package from a Source.
+     * Use available deltarpm if apropriate.
+    */
+    class PackageProvider : private base::NonCopyable
+    {
+      typedef shared_ptr<void>                                       ScopedGuard;
+      typedef callback::SendReport<repo::DownloadResolvableReport> Report;
+
+      typedef packagedelta::DeltaRpm                         DeltaRpm;
+
+    public:
+      /** Ctor taking the Package to provide. */
+      PackageProvider( RepoMediaAccess &access,
+                       const Package::constPtr & package,
+                       const DeltaCandidates & deltas,
+                       const PackageProviderPolicy & policy_r = PackageProviderPolicy() );
+      ~PackageProvider();
+
+    public:
+      /** Provide the package.
+       * \throws Exception.
+      */
+      ManagedFile providePackage() const;
+
+    private:
+      ManagedFile doProvidePackage() const;
+      ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
+
+    private:
+      ScopedGuard newReport() const;
+      Report & report() const;
+      bool progressDeltaDownload( int value ) const;
+      void progressDeltaApply( int value ) const;
+      bool progressPackageDownload( int value ) const;
+      bool failOnChecksumError() const;
+      bool queryInstalled( const Edition & ed_r = Edition() ) const;
+
+    private:
+      PackageProviderPolicy      _policy;
+      Package::constPtr          _package;
+      mutable bool               _retry;
+      mutable shared_ptr<Report> _report;
+      DeltaCandidates            _deltas;
+      RepoMediaAccess &          _access;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SOURCE_PACKAGEPROVIDER_H
diff --git a/zypp/repo/PluginServices.cc b/zypp/repo/PluginServices.cc
new file mode 100644 (file)
index 0000000..7382a66
--- /dev/null
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+#include <iostream>
+#include <sstream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/repo/PluginServices.h"
+#include "zypp/ServiceInfo.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/PathInfo.h"
+
+using std::endl;
+using std::stringstream;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    class PluginServices::Impl
+    {
+    public:
+      static void loadServices( const Pathname &path,
+          const PluginServices::ProcessService &callback );
+    };
+
+    void PluginServices::Impl::loadServices( const Pathname &path,
+                                  const PluginServices::ProcessService & callback/*,
+                                  const ProgressData::ReceiverFnc &progress*/ )
+    {
+      std::list<Pathname> entries;
+      if (PathInfo(path).isExist())
+      {
+        if ( filesystem::readdir( entries, path, false ) != 0 )
+        {
+          // TranslatorExplanation '%s' is a pathname
+            ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), path.c_str())));
+        }
+
+        //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
+        for_(it, entries.begin(), entries.end() )
+        {
+          ServiceInfo service_info;
+          service_info.setAlias((*it).basename());
+          Url url;
+          url.setPathName((*it).asString());
+          url.setScheme("file");
+          service_info.setUrl(url);
+          service_info.setType(ServiceType::PLUGIN);
+          service_info.setAutorefresh( true );
+         DBG << "Plugin Service: " << service_info << endl;
+          callback(service_info);
+        }
+
+      }
+    }
+
+    PluginServices::PluginServices( const Pathname &path,
+                                  const ProcessService & callback/*,
+                                  const ProgressData::ReceiverFnc &progress */)
+    {
+      Impl::loadServices(path, callback/*, progress*/);
+    }
+
+    PluginServices::~PluginServices()
+    {}
+
+    std::ostream & operator<<( std::ostream & str, const PluginServices & obj )
+    {
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/PluginServices.h b/zypp/repo/PluginServices.h
new file mode 100644 (file)
index 0000000..6c69c52
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_PLUGINSERVICES_H
+#define ZYPP_REPO_PLUGINSERVICES_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ProgressData.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class ServiceInfo;
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    class PluginServices
+    {
+      friend std::ostream & operator<<( std::ostream & str, const PluginServices& obj );
+    public:
+      
+     /**
+      * Callback definition.
+      * First parameter is a \ref ServiceInfo object with the resource.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+      typedef function< bool( const ServiceInfo & )> ProcessService;
+      
+      /** Implementation  */
+      class Impl;
+
+    public:
+      PluginServices(const Pathname &path,
+                    const ProcessService & callback);
+     
+      /**
+       * Dtor
+       */
+      ~PluginServices();
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ServiceFileReader Stream output */
+    std::ostream & operator<<( std::ostream & str, const PluginServices & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_LOCALSERVICES_H
diff --git a/zypp/repo/RepoException.cc b/zypp/repo/RepoException.cc
new file mode 100644 (file)
index 0000000..cb25498
--- /dev/null
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/RepoException.cc
+ *
+*/
+#include <iostream>
+#include "zypp/repo/RepoException.h"
+#include "zypp/base/String.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Repository related exceptions
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    RepoException::RepoException()
+    : Exception( "Repo exception" )
+    {}
+
+    RepoException::RepoException( const std::string & msg_r )
+    : Exception( msg_r )
+    {}
+
+    RepoException::RepoException( const RepoInfo & info )
+    : Exception( "Repo exception" ), _info( info )
+    {}
+
+    RepoException::RepoException( const RepoInfo & info, const std::string& msg_r )
+    : Exception( msg_r ), _info( info )
+    {}
+
+    RepoException::~RepoException() throw()
+    {}
+
+    std::ostream & RepoException::dumpOn( std::ostream & str ) const
+    {
+      str << "[" << _info.alias() << "|" << _info.url() << "] ";
+      return Exception::dumpOn( str );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+#define DEF_CTORS( CLASS, MSG ) \
+    CLASS::CLASS()                                                        : RepoException( MSG ) {} \
+    CLASS::CLASS( const std::string & msg_r )                             : RepoException( msg_r ) {} \
+    CLASS::CLASS( const RepoInfo & service_r )                            : RepoException( service_r, MSG ) {} \
+    CLASS::CLASS( const RepoInfo & service_r, const std::string & msg_r ) : RepoException( service_r, msg_r ) {}
+
+    DEF_CTORS( RepoNotCachedException,      "Repository is not cached" );
+    DEF_CTORS( RepoNoUrlException,          "Repository has no or invalid url defined." );
+    DEF_CTORS( RepoNoAliasException,        "Repository has no alias defined." );
+    DEF_CTORS( RepoInvalidAliasException,   "Repository has an invalid alias." );
+    DEF_CTORS( RepoNotFoundException,       "Repository not found." );
+    DEF_CTORS( RepoAlreadyExistsException,  "Repository already exists." );
+    DEF_CTORS( RepoUnknownTypeException,    "Repository type can't be determined." );
+    DEF_CTORS( RepoMetadataException,       "Repository metadata not usable." );
+
+#undef DEF_CTORS
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Service related exceptions
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ServiceException::ServiceException()
+    : Exception( "Service exception" )
+    {}
+
+    ServiceException::ServiceException( const std::string & msg_r )
+    : Exception( msg_r )
+    {}
+
+    ServiceException::ServiceException( const ServiceInfo & service_r )
+    : Exception( "Service exception" ), _service( service_r )
+    {}
+
+    ServiceException::ServiceException( const ServiceInfo & service_r, const std::string & msg_r )
+    : Exception( msg_r ), _service( service_r )
+    {}
+
+    ServiceException::~ServiceException() throw()
+    {}
+
+    std::ostream & ServiceException::dumpOn( std::ostream & str ) const
+    {
+      str << "[" << _service.alias() << "|" << _service.url() << "] ";
+      return Exception::dumpOn( str );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+#define DEF_CTORS( CLASS, MSG ) \
+    CLASS::CLASS()                                                           : ServiceException( MSG ) {} \
+    CLASS::CLASS( const std::string & msg_r )                                : ServiceException( msg_r ) {} \
+    CLASS::CLASS( const ServiceInfo & service_r )                            : ServiceException( service_r, MSG ) {} \
+    CLASS::CLASS( const ServiceInfo & service_r, const std::string & msg_r ) : ServiceException( service_r, msg_r ) {}
+
+    DEF_CTORS( ServiceNoAliasException,       "Service has no alias defined." );
+    DEF_CTORS( ServiceInvalidAliasException,  "Service has an invalid alias." );
+    DEF_CTORS( ServiceAlreadyExistsException, "Service already exists." );
+    DEF_CTORS( ServiceNoUrlException,         "Service has no or invalid url defined." );
+    DEF_CTORS( ServicePluginInformalException,"Service plugin has trouble providing the metadata but this should not be treated as error." );
+
+#undef DEF_CTORS
+
+   /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/RepoException.h b/zypp/repo/RepoException.h
new file mode 100644 (file)
index 0000000..00d5a94
--- /dev/null
@@ -0,0 +1,262 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/parser/tagfile/RepoException.h
+ *
+*/
+#ifndef ZYPP_REPO_REPOEXCEPTION_H
+#define ZYPP_REPO_REPOEXCEPTION_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/ServiceInfo.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    /** \name Repository related exceptions.
+    */
+    //@{
+
+    /**
+     * \short Exception for repository handling.
+     */
+    class RepoException : public Exception
+    {
+      public:
+        RepoException();
+        RepoException( const std::string & msg_r );
+        RepoException( const RepoInfo & info );
+        RepoException( const RepoInfo & info, const std::string & msg_r );
+        virtual ~RepoException() throw();
+
+        RepoInfo info()
+        { return _info; }
+
+        std::string alias()
+        { return info().alias(); }
+
+      protected:
+        virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      private:
+        RepoInfo _info;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * The repository cache is not built yet
+     * so you can't create the repostories from
+     * the cache.
+     */
+    class RepoNotCachedException : public RepoException
+    {
+      public:
+        RepoNotCachedException();
+        RepoNotCachedException( const std::string & msg_r );
+        RepoNotCachedException( const RepoInfo & info );
+        RepoNotCachedException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * thrown when it was impossible to
+     * determine one url for this repo.
+     */
+    class RepoNoUrlException : public RepoException
+    {
+      public:
+        RepoNoUrlException();
+        RepoNoUrlException( const std::string & msg_r );
+        RepoNoUrlException( const RepoInfo & info );
+        RepoNoUrlException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * thrown when it was impossible to
+     * determine an alias for this repo.
+     */
+    class RepoNoAliasException : public RepoException
+    {
+      public:
+        RepoNoAliasException();
+        RepoNoAliasException( const std::string & msg_r );
+        RepoNoAliasException( const RepoInfo & info );
+        RepoNoAliasException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * Thrown when the repo alias is found to be invalid.
+     */
+    class RepoInvalidAliasException : public RepoException
+    {
+    public:
+      RepoInvalidAliasException();
+      RepoInvalidAliasException( const std::string & msg_r );
+      RepoInvalidAliasException( const RepoInfo & info );
+      RepoInvalidAliasException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * thrown when it was impossible to
+     * match a repository
+     */
+    class RepoNotFoundException : public RepoException
+    {
+      public:
+        RepoNotFoundException();
+        RepoNotFoundException( const std::string & msg_r );
+        RepoNotFoundException( const RepoInfo & info );
+        RepoNotFoundException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * Repository already exists and some unique
+     * attribute can't be duplicated.
+     */
+    class RepoAlreadyExistsException : public RepoException
+    {
+      public:
+        RepoAlreadyExistsException();
+        RepoAlreadyExistsException( const std::string & msg_r );
+        RepoAlreadyExistsException( const RepoInfo & info );
+        RepoAlreadyExistsException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * thrown when it was impossible to
+     * determine this repo type.
+     */
+    class RepoUnknownTypeException : public RepoException
+    {
+      public:
+        RepoUnknownTypeException();
+        RepoUnknownTypeException( const std::string & msg_r );
+        RepoUnknownTypeException( const RepoInfo & info );
+        RepoUnknownTypeException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    /**
+     * thrown when it was impossible to
+     * use the raw metadata for this repo.
+     */
+    class RepoMetadataException : public RepoException
+    {
+      public:
+        RepoMetadataException();
+        RepoMetadataException( const std::string & msg_r );
+        RepoMetadataException( const RepoInfo & info );
+        RepoMetadataException( const RepoInfo & info, const std::string & msg_r );
+    };
+
+    //@}
+    ///////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name Service related exceptions.
+    */
+    //@{
+
+    /** Base Exception for service handling.
+     */
+    class ServiceException : public Exception
+    {
+      public:
+        ServiceException();
+        ServiceException( const std::string & msg_r );
+        ServiceException( const ServiceInfo & service_r );
+        ServiceException( const ServiceInfo & service_r, const std::string & msg_r );
+        virtual ~ServiceException() throw();
+
+        ServiceInfo service()
+        { return _service; }
+
+        std::string alias()
+        { return service().alias(); }
+
+     protected:
+        virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      private:
+        ServiceInfo _service;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** Service without alias was used in an operation.
+     */
+    class ServiceNoAliasException : public ServiceException
+    {
+      public:
+        ServiceNoAliasException();
+        ServiceNoAliasException( const std::string & msg_r );
+        ServiceNoAliasException( const ServiceInfo & service_r );
+        ServiceNoAliasException( const ServiceInfo & service_r, const std::string & msg_r );
+    };
+
+    /**
+     * Thrown when the repo alias is found to be invalid.
+     */
+    class ServiceInvalidAliasException : public ServiceException
+    {
+    public:
+      ServiceInvalidAliasException();
+      ServiceInvalidAliasException( const std::string & msg_r );
+      ServiceInvalidAliasException( const ServiceInfo & info );
+      ServiceInvalidAliasException( const ServiceInfo & info, const std::string & msg_r );
+    };
+
+    /** Service already exists and some unique attribute can't be duplicated.
+     */
+    class ServiceAlreadyExistsException : public ServiceException
+    {
+      public:
+        ServiceAlreadyExistsException();
+        ServiceAlreadyExistsException( const std::string & msg_r );
+        ServiceAlreadyExistsException( const ServiceInfo & service_r );
+        ServiceAlreadyExistsException( const ServiceInfo & service_r, const std::string & msg_r );
+    };
+
+    /** Service has no or invalid url defined.
+     */
+    class ServiceNoUrlException : public ServiceException
+    {
+      public:
+        ServiceNoUrlException();
+        ServiceNoUrlException( const std::string & msg_r );
+        ServiceNoUrlException( const ServiceInfo & service_r );
+        ServiceNoUrlException( const ServiceInfo & service_r, const std::string & msg_r );
+    };
+
+    /** Service plugin has trouble providing the metadata but this should not be treated as error.
+     */
+    class ServicePluginInformalException : public ServiceException
+    {
+      public:
+        ServicePluginInformalException();
+        ServicePluginInformalException( const std::string & msg_r );
+        ServicePluginInformalException( const ServiceInfo & service_r );
+        ServicePluginInformalException( const ServiceInfo & service_r, const std::string & msg_r );
+    };
+
+    //@}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_PARSER_TAGFILE_PARSEEXCEPTION_H
diff --git a/zypp/repo/RepoInfoBase.cc b/zypp/repo/RepoInfoBase.cc
new file mode 100644 (file)
index 0000000..bac195b
--- /dev/null
@@ -0,0 +1,191 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/repo/RepoInfoBase.cc
+ *
+ */
+#include <iostream>
+
+#include "zypp/ZConfig.h"
+#include "zypp/repo/RepoVariables.h"
+
+#include "zypp/repo/RepoInfoBase.h"
+#include "zypp/repo/RepoInfoBaseImpl.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : RepoInfoBase::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoInfo::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const RepoInfoBase::Impl & obj )
+  {
+    return str << "RepoInfo::Impl";
+  }
+
+  void RepoInfoBase::Impl::setAlias(const string & alias_)
+  {
+    this->alias = alias_;
+    // replace slashes with underscores
+    std::string fnd="/";
+    std::string rep="_";
+    std::string escaped_alias = alias_;
+    size_t pos = escaped_alias.find(fnd);
+    while (pos != string::npos)
+    {
+      escaped_alias.replace(pos, fnd.length(), rep);
+      pos = escaped_alias.find(fnd, pos+rep.length());
+    }
+    this->escaped_alias = escaped_alias;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : RepoInfoBase
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    METHOD NAME : RepoInfoBase::RepoInfoBase
+  //    METHOD TYPE : Ctor
+  //
+  RepoInfoBase::RepoInfoBase()
+    : _pimpl( new Impl() )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    METHOD NAME : RepoInfoBase::RepoInfoBase
+  //    METHOD TYPE : Ctor
+  //
+  RepoInfoBase::RepoInfoBase(const string & alias)
+    : _pimpl( new Impl(alias) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    METHOD NAME : RepoInfoBase::~RepoInfoBase
+  //    METHOD TYPE : Dtor
+  //
+  RepoInfoBase::~RepoInfoBase()
+  {}
+
+  void RepoInfoBase::setEnabled( bool enabled )
+  {
+    _pimpl->enabled = enabled;
+  }
+
+  void RepoInfoBase::setAutorefresh( bool autorefresh )
+  {
+    _pimpl->autorefresh = autorefresh;
+  }
+
+  void RepoInfoBase::setAlias( const std::string &alias )
+  {
+    _pimpl->setAlias(alias);
+  }
+
+  void RepoInfoBase::setName( const std::string &name )
+  {
+    _pimpl->name = name;
+  }
+
+  void RepoInfoBase::setFilepath( const Pathname &filepath )
+  {
+    _pimpl->filepath = filepath;
+  }
+
+  // true by default (if not set by setEnabled())
+  bool RepoInfoBase::enabled() const
+  { return indeterminate(_pimpl->enabled) ? true : (bool) _pimpl->enabled; }
+
+  // false by default (if not set by setAutorefresh())
+  bool RepoInfoBase::autorefresh() const
+  { return indeterminate(_pimpl->autorefresh) ? false : (bool) _pimpl->autorefresh; }
+
+  std::string RepoInfoBase::alias() const
+  { return _pimpl->alias; }
+
+  std::string RepoInfoBase::escaped_alias() const
+  { return _pimpl->escaped_alias; }
+
+  std::string RepoInfoBase::name() const
+  {
+    if ( _pimpl->name.empty() )
+    {
+      return alias();
+    }
+
+    repo::RepoVariablesStringReplacer replacer;
+    return replacer(_pimpl->name);
+  }
+
+  std::string RepoInfoBase::label() const
+  {
+    if ( ZConfig::instance().repoLabelIsAlias() )
+      return alias();
+    return name();
+  }
+
+  Pathname RepoInfoBase::filepath() const
+  { return _pimpl->filepath; }
+
+
+  std::ostream & RepoInfoBase::dumpOn( std::ostream & str ) const
+  {
+    str << "--------------------------------------" << std::endl;
+    str << "- alias       : " << alias() << std::endl;
+    str << "- name        : " << name() << std::endl;
+    str << "- enabled     : " << enabled() << std::endl;
+    str << "- autorefresh : " << autorefresh() << std::endl;
+
+    return str;
+  }
+
+  std::ostream & RepoInfoBase::dumpAsIniOn( std::ostream & str ) const
+  {
+    // we save the original data without variable replacement
+    str << "[" << alias() << "]" << endl;
+    str << "name=" << name() << endl;
+    str << "enabled=" << (enabled() ? "1" : "0") << endl;
+    str << "autorefresh=" << (autorefresh() ? "1" : "0") << endl;
+
+    return str;
+  }
+
+  std::ostream & RepoInfoBase::dumpAsXMLOn(std::ostream & str) const
+  { return dumpAsXMLOn(str, ""); }
+
+  std::ostream & RepoInfoBase::dumpAsXMLOn( std::ostream & str, const std::string & content) const
+  {
+    return str << "<!-- there's no XML representation of RepoInfoBase -->" << endl;
+  }
+
+  std::ostream & operator<<( std::ostream & str, const RepoInfoBase & obj )
+  {
+    return obj.dumpOn(str);
+  }
+  ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/RepoInfoBase.h b/zypp/repo/RepoInfoBase.h
new file mode 100644 (file)
index 0000000..e348b87
--- /dev/null
@@ -0,0 +1,189 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/repo/RepoInfoBase.h
+ *
+ */
+#ifndef REPOINFOBASE_H_
+#define REPOINFOBASE_H_
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //    CLASS NAME : RepoInfoBase
+    //
+    /**
+     * \short Base class implementing common features of \ref RepoInfo and
+     *        \ref ServiceInfo.
+     */
+    class RepoInfoBase
+    {
+      friend std::ostream & operator<<( std::ostream & str, const RepoInfoBase & obj );
+
+    public:
+      RepoInfoBase();
+      RepoInfoBase(const std::string & alias);
+      virtual ~RepoInfoBase();
+
+      /**
+       * unique identifier for this source. If not specified
+       * It should be generated from the base url.
+       *
+       * Normally, in a .repo file the section name is used
+       * ( [somerepo] )
+       */
+      std::string alias() const;
+
+      /**
+       * Same as alias(), just escaped in a way to be a valid file name.
+       */
+      std::string escaped_alias() const;
+
+      /**
+       * \short Repository short label
+       *
+       * Short label or description of the repository.
+       * ie: "SUSE Linux 10.2 updates"
+       */
+      std::string name() const;
+
+      /**
+       * \short Label for use in messages for the user interface.
+       *
+       * Returns an alias or name, according to ZConfig::repoLabelIsAlias().
+       */
+      std::string label() const;
+
+      /**
+       * If enabled is false, then this repository must be ignored as if does
+       * not exists, except when checking for duplicate alias.
+       */
+      bool enabled() const;
+
+      /**
+       * If true, the repostory must be refreshed before creating resolvables
+       * from it
+       */
+      bool autorefresh() const;
+
+      /**
+       * \short File where this repo was read from
+       *
+       * \note could be an empty pathname for repo
+       * infos created in memory.
+       */
+       Pathname filepath() const;
+
+
+    public:
+
+      /**
+       * set the repository alias \see alias
+       * \param alias
+       */
+      void setAlias( const std::string &alias );
+
+      /**
+       * set the repository name \see name
+       * \param name
+       */
+      void setName( const std::string &name );
+
+      /**
+       * enable or disable the repository \see enabled
+       * \param enabled
+       */
+      void setEnabled( bool enabled );
+
+      /**
+       * enable or disable autorefresh \see autorefresh
+       * \param enabled
+       */
+      void setAutorefresh( bool autorefresh );
+
+      /**
+       * \short set the path to the .repo file
+       *
+       * The path to the .repo file where this repository
+       * was defined, or empty if nowhere.
+       *
+       * \param path File path
+       */
+      void setFilepath( const Pathname &filename );
+
+      /**
+       * Write a human-readable representation of this RepoInfoBase object
+       * into the \a str stream. Useful for logging.
+       */
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      /**
+       * Write this RepoInfoBase object into \a str in
+       * a <tr>.repo</tt> (ini) file format.
+       */
+      virtual std::ostream & dumpAsIniOn( std::ostream & str ) const;
+
+      /**
+       * Write an XML representation of this object. Implement in
+       * derived classes.
+       */
+      virtual std::ostream & dumpAsXMLOn(std::ostream & str) const;
+
+      /**
+       * Write an XML representation of this object with content (if available).
+       */
+      virtual std::ostream & dumpAsXMLOn(
+          std::ostream & str, const std::string & content) const;
+
+      class Impl;
+    private:
+      /** Pointer to implementation */
+      RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RepoInfoBase */
+    inline bool operator==( const RepoInfoBase & lhs, const RepoInfoBase & rhs )
+    { return lhs.alias() == rhs.alias(); }
+
+    /** \relates RepoInfoBase */
+    inline bool operator!=( const RepoInfoBase & lhs, const RepoInfoBase & rhs )
+    { return lhs.alias() != rhs.alias(); }
+
+    inline bool operator<( const RepoInfoBase & lhs, const RepoInfoBase & rhs )
+    { return lhs.alias() < rhs.alias(); }
+
+    /** \relates RepoInfoBase Stream output */
+    std::ostream & operator<<( std::ostream & str, const RepoInfoBase & obj );
+
+    /** \relates RepoInfoBase */
+    typedef shared_ptr<RepoInfoBase> RepoInfoBase_Ptr;
+    /** \relates RepoInfoBase */
+    typedef shared_ptr<const RepoInfoBase> RepoInfoBase_constPtr;
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif /*REPOINFOBASE_H_*/
diff --git a/zypp/repo/RepoInfoBaseImpl.h b/zypp/repo/RepoInfoBaseImpl.h
new file mode 100644 (file)
index 0000000..7d0592e
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/repo/RepoInfoBaseImpl.h
+ *
+ */
+#ifndef REPOINFOBASEIMPL_H_
+#define REPOINFOBASEIMPL_H_
+
+#include <string>
+
+#include "zypp/TriBool.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //    CLASS NAME : RepoInfoBase::Impl
+  //
+  struct RepoInfoBase::Impl
+  {
+    Impl()
+      : enabled (indeterminate)
+      , autorefresh (indeterminate)
+    {}
+
+    Impl(const std::string & alias_)
+      : enabled(indeterminate)
+      , autorefresh(indeterminate)
+    { setAlias(alias_); }
+
+    ~Impl()
+    {}
+
+  public:
+    TriBool enabled;
+    TriBool autorefresh;
+    std::string alias;
+    std::string escaped_alias;
+    std::string name;
+    Pathname filepath;
+  public:
+
+    void setAlias(const std::string & alias_);
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif /*REPOINFOBASEIMPL_H_*/
diff --git a/zypp/repo/RepoMirrorList.cc b/zypp/repo/RepoMirrorList.cc
new file mode 100644 (file)
index 0000000..15b1c45
--- /dev/null
@@ -0,0 +1,161 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/RepoMirrorList.cc
+ *
+*/
+
+#include <iostream>
+#include <vector>
+#include <time.h>
+#include "zypp/repo/RepoMirrorList.h"
+#include "zypp/media/MetaLinkParser.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/ZConfig.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    RepoMirrorList::RepoMirrorList( const Url &url, const Pathname &metadatapath )
+    {
+      std::vector<Url> my_urls;
+      Pathname tmpfile, cachefile;
+
+      if ( url.asString().find("/metalink") != string::npos )
+        cachefile = metadatapath / "mirrorlist.xml";
+      else
+        cachefile = metadatapath / "mirrorlist.txt";
+        //cachefile = ZConfig::instance().repoMetadataPath() / Pathname(escaped_alias) / "mirrorlist.txt";
+
+      zypp::filesystem::PathInfo cacheinfo (cachefile);
+
+      if ( !cacheinfo.isFile() || cacheinfo.mtime() < time(NULL) - (long) ZConfig::instance().repo_refresh_delay() * 60 )
+      {
+        Pathname filepath (url.getPathName());
+        Url abs_url (url);
+
+        DBG << "Getting MirrorList from URL: " << abs_url << endl;
+
+        abs_url.setPathName("");
+        abs_url.setQueryParam("mediahandler", "curl");
+
+        MediaSetAccess access (abs_url);
+        tmpfile = access.provideFile(filepath);
+
+        // Create directory, if not existing
+        zypp::filesystem::assert_dir(metadatapath);
+
+        DBG << "Copy MirrorList file to " << cachefile << endl;
+        zypp::filesystem::copy(tmpfile, cachefile);
+      }
+
+      if ( url.asString().find("/metalink") != string::npos )
+      {
+        my_urls = parseXML(cachefile);
+      }
+      else
+      {
+        my_urls = parseTXT(cachefile);
+      }
+
+      setUrls( my_urls );
+      if( urls.empty() )
+      {
+        DBG << "Removing Cachefile as it contains no URLs" << endl;
+        zypp::filesystem::unlink(cachefile);
+      }
+    }
+
+    RepoMirrorList::RepoMirrorList( const Url &url )
+    {
+      std::vector<Url> my_urls;
+      Pathname tmpfile;
+
+      Pathname filepath (url.getPathName());
+      Url abs_url (url);
+
+      DBG << "Getting MirrorList from URL: " << abs_url << endl;
+
+      abs_url.setPathName("");
+      abs_url.setQueryParam("mediahandler", "curl");
+
+      MediaSetAccess access (abs_url);
+      tmpfile = access.provideFile(filepath);
+
+      if ( url.asString().find("/metalink") != string::npos )
+      {
+        my_urls = parseXML(tmpfile);
+      }
+      else
+      {
+        my_urls = parseTXT(tmpfile);
+      }
+
+      setUrls( my_urls );
+    }
+
+    void RepoMirrorList::setUrls( std::vector<Url> my_urls )
+    {
+      int valid_urls = 0;
+      for (std::vector<Url>::iterator it = my_urls.begin() ; it != my_urls.end() and valid_urls < 4 ; ++it)
+      {
+        if ( it->getScheme() != "rsync" )
+        {
+          size_t delpos = it->getPathName().find("repodata/repomd.xml");
+          if( delpos != string::npos )
+          {
+            it->setPathName( it->getPathName().erase(delpos)  );
+          }
+          urls.push_back(*it);
+          ++valid_urls;
+        }
+      }
+    }
+
+    std::vector<Url> RepoMirrorList::parseXML( const Pathname &tmpfile ) const
+    {
+      InputStream tmpfstream (tmpfile);
+      media::MetaLinkParser metalink;
+      metalink.parse(tmpfstream);
+      return metalink.getUrls();
+    }
+
+    std::vector<Url> RepoMirrorList::parseTXT( const Pathname &tmpfile ) const
+    {
+      InputStream tmpfstream (tmpfile);
+      std::vector<Url> my_urls;
+      string tmpurl;
+      while (getline(tmpfstream.stream(), tmpurl))
+      {
+        my_urls.push_back(Url(tmpurl));
+      }
+      return my_urls;
+    }
+    
+    std::vector<Url> RepoMirrorList::getUrls() const
+    {
+      return urls;
+    }
+
+    RepoMirrorList::~RepoMirrorList()
+    {}
+
+   /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/RepoMirrorList.h b/zypp/repo/RepoMirrorList.h
new file mode 100644 (file)
index 0000000..dfc2ca6
--- /dev/null
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_MIRRORLIST_H_
+#define ZYPP_REPO_MIRRORLIST_H_
+
+#include <vector>
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    class RepoMirrorList
+    {
+      public:
+        RepoMirrorList( const Url &url );
+        RepoMirrorList( const Url &url, const Pathname &metadatapath );
+        virtual ~RepoMirrorList();
+        
+        std::vector<Url> getUrls() const;
+
+      private:
+        std::vector<Url> urls;
+        void setUrls( std::vector<Url> my_urls );
+        std::vector<Url> parseXML( const Pathname &tmpfile ) const;
+        std::vector<Url> parseTXT( const Pathname &tmpfile ) const;
+    };
+
+  } // ns repo
+} // ns zypp
+
+#endif
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/RepoProvideFile.cc b/zypp/repo/RepoProvideFile.cc
new file mode 100644 (file)
index 0000000..1b46ae8
--- /dev/null
@@ -0,0 +1,363 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/source/RepoProvideFile.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <set>
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/repo/RepoProvideFile.h"
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/ZConfig.h"
+#include "zypp/repo/SUSEMediaVerifier.h"
+#include "zypp/repo/RepoException.h"
+
+#include "zypp/repo/SUSEMediaVerifier.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/FileChecker.h"
+#include "zypp/Fetcher.h"
+
+using std::endl;
+using std::set;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // provideFile
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      /** Hack to extract progress information from media::DownloadProgressReport.
+       * We redirect the static report triggered from RepoInfo::provideFile
+       * to feed the ProvideFilePolicy callbacks in addition to any connected
+       * media::DownloadProgressReport.
+      */
+      struct DownloadFileReportHack : public callback::ReceiveReport<media::DownloadProgressReport>
+      {
+       typedef callback::ReceiveReport<ReportType> BaseType;
+       typedef function<bool(int)>                 RedirectType;
+
+       DownloadFileReportHack( RedirectType redirect_r )
+       : _oldRec( Distributor::instance().getReceiver() )
+       , _redirect( redirect_r )
+       { connect(); }
+       ~DownloadFileReportHack()
+       { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
+
+       virtual void start( const Url & file, Pathname localfile )
+       {
+         if ( _oldRec )
+           _oldRec->start( file, localfile );
+         else
+           BaseType::start( file, localfile );
+       }
+
+       virtual bool progress( int value, const Url & file, double dbps_avg = -1, double dbps_current = -1 )
+       {
+         bool ret = true;
+         if ( _oldRec )
+           ret &= _oldRec->progress( value, file, dbps_avg, dbps_current );
+          if ( _redirect )
+            ret &= _redirect( value );
+         return ret;
+       }
+
+       virtual Action problem( const Url & file, Error error, const std::string & description )
+       {
+         if ( _oldRec )
+           return _oldRec->problem( file, error, description );
+         return BaseType::problem( file, error, description );
+       }
+       virtual void finish( const Url & file, Error error, const std::string & reason )
+       {
+         if ( _oldRec )
+           _oldRec->finish( file, error, reason );
+         else
+           BaseType::finish( file, error, reason );
+       }
+
+       private:
+         Receiver * _oldRec;
+         RedirectType _redirect;
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ManagedFile provideFile( RepoInfo repo_r,
+                             const OnMediaLocation & loc_r,
+                             const ProvideFilePolicy & policy_r )
+    {
+      RepoMediaAccess access;
+      return access.provideFile(repo_r, loc_r, policy_r );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    class RepoMediaAccess::Impl
+    {
+    public:
+      Impl( const ProvideFilePolicy & defaultPolicy_r )
+        : _defaultPolicy( defaultPolicy_r )
+      {}
+
+      ~Impl()
+      {
+        std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
+        for ( it = _medias.begin();
+              it != _medias.end();
+              ++it )
+        {
+          it->second->release();
+        }
+      }
+
+      /** Provide a MediaSetAccess for \c url with label and verifyer adjusted.
+       *
+       * As the same url (e.g. \c 'dvd:///' ) might be used for multiple repos
+       * we must always adjust the repo specific data (label,verifyer).
+       *
+       * \todo This mixture of media and repos specific data is fragile.
+      */
+      shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url, RepoInfo repo )
+      {
+        std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
+        it = _medias.find(url);
+        shared_ptr<MediaSetAccess> media;
+        if ( it != _medias.end() )
+        {
+          media = it->second;
+        }
+        else
+        {
+          media.reset( new MediaSetAccess(url) );
+          _medias[url] = media;
+        }
+        setVerifierForRepo( repo, media );
+        return media;
+      }
+
+      private:
+        void setVerifierForRepo( RepoInfo repo, shared_ptr<MediaSetAccess> media )
+        {
+          // Always set the MediaSetAccess label.
+          media->setLabel( repo.name() );
+
+          // set a verifier if the repository has it
+
+          Pathname mediafile = repo.metadataPath() + "/media.1/media";
+          if ( ! repo.metadataPath().empty() )
+          {
+            if ( PathInfo(mediafile).isExist() )
+            {
+              std::map<shared_ptr<MediaSetAccess>, RepoInfo>::const_iterator it;
+              it = _verifier.find(media);
+              if ( it != _verifier.end() )
+              {
+                if ( it->second.alias() == repo.alias() )
+                {
+                  // this media is already using this repo verifier
+                  return;
+                }
+              }
+
+              std::ifstream str(mediafile.asString().c_str());
+              std::string vendor;
+              std::string mediaid;
+              std::string buffer;
+              if ( str )
+              {
+                getline(str, vendor);
+                getline(str, mediaid);
+                getline(str, buffer);
+
+                unsigned media_nr = str::strtonum<unsigned>(buffer);
+                MIL << "Repository '" << repo.alias() << "' has " << media_nr << " medias"<< endl;
+
+                for ( unsigned i=1; i <= media_nr; ++i )
+                {
+                  media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
+
+                  media->setVerifier( i, verifier);
+                }
+                _verifier[media] = repo;
+              }
+              else
+              {
+                ZYPP_THROW(RepoMetadataException(repo));
+              }
+            }
+            else
+            {
+              WAR << "No media verifier for repo '" << repo.alias() << "' media/media.1 does not exist in '" << repo.metadataPath() << "'" << endl;
+            }
+          }
+          else
+          {
+            WAR << "'" << repo.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
+          }
+        }
+
+      private:
+        std::map<shared_ptr<MediaSetAccess>, RepoInfo> _verifier;
+        std::map<Url, shared_ptr<MediaSetAccess> > _medias;
+
+      public:
+        ProvideFilePolicy _defaultPolicy;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+
+    RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
+      : _impl( new Impl( defaultPolicy_r ) )
+    {}
+
+    RepoMediaAccess::~RepoMediaAccess()
+    {}
+
+    void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
+    { _impl->_defaultPolicy = policy_r; }
+
+    const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
+    { return _impl->_defaultPolicy; }
+
+    ManagedFile RepoMediaAccess::provideFile( RepoInfo repo_r,
+                                              const OnMediaLocation & loc_r,
+                                              const ProvideFilePolicy & policy_r )
+    {
+      MIL << loc_r << endl;
+      // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
+      // and redirect download progress triggers to call the ProvideFilePolicy
+      // callback.
+      DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
+
+      RepoException repo_excpt(repo_r,
+                              str::form(_("Can't provide file '%s' from repository '%s'"),
+                               loc_r.filename().c_str(),
+                               repo_r.alias().c_str() ) );
+
+      if ( repo_r.baseUrlsEmpty() )
+      {
+        repo_excpt.remember(RepoException(_("No url in repository.")));
+        ZYPP_THROW(repo_excpt);
+      }
+
+      Fetcher fetcher;
+      fetcher.addCachePath( repo_r.packagesPath() );
+
+      MIL << "Added cache path " << repo_r.packagesPath() << endl;
+
+      for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
+            it != repo_r.baseUrlsEnd();
+            /* incremented in the loop */ )
+      {
+        Url url( *it );
+        ++it;
+        try
+        {
+          MIL << "Providing file of repo '" << repo_r.alias()
+              << "' from " << url << endl;
+          shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
+
+         fetcher.enqueue( loc_r );
+
+         // FIXME: works for packages only
+         fetcher.start( repo_r.packagesPath(), *access );
+
+         // reached if no exception has been thrown, so this is the correct file
+          ManagedFile ret( repo_r.packagesPath() + loc_r.filename() );
+
+          std::string scheme( url.getScheme() );
+          if ( !repo_r.keepPackages() )
+          {
+            ret.setDispose( filesystem::unlink );
+          }
+
+          if ( loc_r.checksum().empty() )
+          {
+            // no checksum in metadata
+            WAR << "No checksum in metadata " << loc_r << endl;
+          }
+          else
+          {
+            std::ifstream input( ret->asString().c_str() );
+            CheckSum retChecksum( loc_r.checksum().type(), input );
+            input.close();
+
+            if ( loc_r.checksum() != retChecksum )
+            {
+              // failed integity check
+              std::ostringstream err;
+              err << "File " << ret << " fails integrity check. Expected: [" << loc_r.checksum() << "] Got: [";
+              if ( retChecksum.empty() )
+                err << "Failed to compute checksum";
+              else
+                err << retChecksum;
+              err << "]";
+
+              WAR << err.str() << endl;
+
+              if ( policy_r.failOnChecksumError() )
+                ZYPP_THROW( FileCheckException( err.str() ) );
+              else
+                WAR << "NO failOnChecksumError: " << err.str() << endl;
+            }
+          }
+
+          MIL << "provideFile at " << ret << endl;
+          return ret;
+        }
+        catch ( const SkipRequestException &e )
+        {
+          ZYPP_CAUGHT( e );
+          ZYPP_RETHROW(e);
+        }
+        catch ( const AbortRequestException &e )
+        {
+          ZYPP_CAUGHT( e );
+          ZYPP_RETHROW(e);
+        }
+        catch ( const Exception &e )
+        {
+          ZYPP_CAUGHT( e );
+
+          repo_excpt.remember(e);
+
+          WAR << "Trying next url" << endl;
+          continue;
+        }
+      } // iteration over urls
+
+      ZYPP_THROW(repo_excpt);
+      return ManagedFile(); // not reached
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/RepoProvideFile.h b/zypp/repo/RepoProvideFile.h
new file mode 100644 (file)
index 0000000..63628d9
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/RepoProvideFile.h
+ *
+*/
+#ifndef ZYPP_REPO_REPOPROVIDEFILE_H
+#define ZYPP_REPO_REPOPROVIDEFILE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+#include "zypp/base/Functional.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/ProvideFilePolicy.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // provideFile
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    /** Provide a file from a Repository.
+     * Let \a source_r provide the file described by \a loc_r. In case
+     * \a loc_r contains a checksum, the file is verified. \a policy_r
+     * provides callback hooks for download progress reporting and behaviour
+     * on failed checksum verification.
+     *
+     * \throws Exception
+    */
+    ManagedFile provideFile( RepoInfo repo_r,
+                             const OnMediaLocation & loc_r,
+                             const ProvideFilePolicy & policy_r = ProvideFilePolicy() );
+
+    /**
+     * \short Provides files from different repos
+     *
+     * Class that allows to get files from repositories
+     * It handles automatically setting media verifiers if the
+     * repo is cached, and reuses media set access opened for
+     * repositories during its scope, so you can provide
+     * files from different repositories in different order
+     * without opening and closing medias all the time
+     */
+    class RepoMediaAccess
+    {
+    public:
+      /** Ctor taking the default \ref ProvideFilePolicy. */
+      RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r = ProvideFilePolicy() );
+      ~RepoMediaAccess();
+
+      /** Provide a file from a Repository.
+      * Let \a source_r provide the file described by \a loc_r. In case
+      * \a loc_r contains a checksum, the file is verified. \a policy_r
+      * provides callback hooks for download progress reporting and behaviour
+      * on failed checksum verification.
+      *
+      * \throws Exception
+      * \todo Investigate why this needs a non-const Repository as arg.
+      */
+      ManagedFile provideFile( RepoInfo repo_r,
+                               const OnMediaLocation & loc_r,
+                               const ProvideFilePolicy & policy_r );
+
+      /** \overload Using the current default \ref ProvideFilePolicy. */
+      ManagedFile provideFile( RepoInfo repo_r, const OnMediaLocation & loc_r )
+      { return provideFile( repo_r, loc_r, defaultPolicy() ); }
+
+    public:
+      /** Set a new default \ref ProvideFilePolicy. */
+      void setDefaultPolicy( const ProvideFilePolicy & policy_r );
+
+      /** Get the current default \ref ProvideFilePolicy. */
+      const ProvideFilePolicy & defaultPolicy() const;
+
+   private:
+      class Impl;
+       RW_pointer<Impl> _impl;
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_REPOPROVIDEFILE_H
diff --git a/zypp/repo/RepoType.cc b/zypp/repo/RepoType.cc
new file mode 100644 (file)
index 0000000..c10f827
--- /dev/null
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <map>
+#include "zypp/repo/RepoException.h"
+#include "RepoType.h"
+
+namespace zypp
+{
+namespace repo
+{
+
+  static std::map<std::string,RepoType::Type> _table;
+
+  const RepoType RepoType::RPMMD(RepoType::RPMMD_e);
+  const RepoType RepoType::YAST2(RepoType::YAST2_e);
+  const RepoType RepoType::RPMPLAINDIR(RepoType::RPMPLAINDIR_e);
+  const RepoType RepoType::NONE(RepoType::NONE_e);
+
+  RepoType::RepoType(const std::string & strval_r)
+    : _type(parse(strval_r))
+  {}
+
+  RepoType::Type RepoType::parse(const std::string & strval_r)
+  {
+    if (_table.empty())
+    {
+      // initialize it
+      _table["repomd"]
+      = _table["rpmmd"]
+      = _table["rpm-md"]
+      = _table["yum"]
+      = _table["YUM"]
+      = _table["up2date"]
+      = RepoType::RPMMD_e;
+
+      _table["susetags"]
+      = _table["yast"]
+      = _table["YaST"]
+      = _table["YaST2"]
+      = _table["YAST"]
+      = _table["YAST2"]
+      = _table["yast2"]
+      = RepoType::YAST2_e;
+
+      _table["plaindir"]
+      = _table["Plaindir"]
+      = RepoType::RPMPLAINDIR_e;
+
+      _table["NONE"]
+      = _table["none"]
+      = RepoType::NONE_e;
+    }
+
+    std::map<std::string,RepoType::Type>::const_iterator it
+      = _table.find(strval_r);
+    if (it == _table.end())
+    {
+      ZYPP_THROW(RepoUnknownTypeException(
+        "Unknown repository type '" + strval_r + "'"));
+    }
+    return it->second;
+  }
+
+
+  const std::string & RepoType::asString() const
+  {
+    static std::map<Type, std::string> _table;
+    if ( _table.empty() )
+    {
+      // initialize it
+      _table[RPMMD_e]          = "rpm-md";
+      _table[YAST2_e]          = "yast2";
+      _table[RPMPLAINDIR_e]    = "plaindir";
+      _table[NONE_e]           = "NONE";
+    }
+    return _table[_type];
+  }
+
+
+  } // ns repo
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/RepoType.h b/zypp/repo/RepoType.h
new file mode 100644 (file)
index 0000000..17e2b95
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_TYPE_H_
+#define ZYPP_REPO_TYPE_H_
+
+#include <iosfwd>
+#include <string>
+
+namespace zypp
+{
+  namespace repo
+  {
+
+  /**
+   * \short Repository type enumeration
+   *
+   * Repositories can be from varous types
+   * ...
+   */
+  struct RepoType
+  {
+    static const RepoType RPMMD;
+    static const RepoType YAST2;
+    static const RepoType RPMPLAINDIR;
+    static const RepoType NONE;
+
+    enum Type
+    {
+      NONE_e,
+      RPMMD_e,
+      YAST2_e,
+      RPMPLAINDIR_e,
+    };
+
+    RepoType() : _type(NONE_e) {}
+
+    RepoType(Type type) : _type(type) {}
+
+    explicit RepoType(const std::string & strval_r);
+
+    Type toEnum() const { return _type; }
+
+    RepoType::Type parse(const std::string & strval_r);
+
+    const std::string & asString() const;
+
+    Type _type;
+  };
+
+
+  inline std::ostream & operator<<( std::ostream & str, const RepoType & obj )
+  { return str << obj.asString(); }
+
+  inline bool operator==(const RepoType & obj1, const RepoType & obj2)
+  { return obj1._type == obj2._type; }
+
+  inline bool operator!=(const RepoType & obj1, const RepoType & obj2)
+  { return ! (obj1 == obj2); }
+
+  } // ns repo
+} // ns zypp
+
+#endif
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/RepoVariables.cc b/zypp/repo/RepoVariables.cc
new file mode 100644 (file)
index 0000000..83ec70b
--- /dev/null
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <map>
+#include <algorithm>
+#include "zypp/base/String.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/ZConfig.h"
+#include "zypp/ZYppFactory.h"
+#include "RepoVariables.h"
+
+using namespace std;
+
+namespace zypp
+{
+namespace repo
+{
+
+RepoVariablesStringReplacer::RepoVariablesStringReplacer()
+{
+  sysarch = Arch_empty;
+  basearch = Arch_empty;
+}
+
+RepoVariablesStringReplacer::~RepoVariablesStringReplacer()
+{}
+
+void RepoVariablesStringReplacer::resetVarCache( void )
+{
+  sysarch = Arch_empty;
+  basearch = Arch_empty;
+  releasever = "";
+}
+
+std::string RepoVariablesStringReplacer::operator()( const std::string &value ) const
+{
+  string newvalue(value);
+
+  // $arch
+  if( sysarch.empty() )
+    sysarch = ZConfig::instance().systemArchitecture();
+
+  newvalue = str::gsub( newvalue, "$arch", sysarch.asString() );
+
+  // $basearch
+  if( basearch.empty() )
+    basearch = sysarch.baseArch();
+
+  newvalue = str::gsub( newvalue, "$basearch", basearch.asString() );
+
+  // $releasever (Target::distributionVersion assumes root=/ if target not initialized)
+  if ( newvalue.find("$releasever") != string::npos ) {
+    if( releasever.empty() )
+      releasever = Target::distributionVersion(Pathname()/*guess*/);
+
+    newvalue = str::gsub( newvalue, "$releasever", releasever );
+  } 
+
+  return newvalue;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+RepoVariablesUrlReplacer::RepoVariablesUrlReplacer()
+{}
+
+RepoVariablesUrlReplacer::~RepoVariablesUrlReplacer()
+{}
+
+void RepoVariablesUrlReplacer::resetVarCache( void )
+{
+  replacer.resetVarCache();
+}
+
+/*
+ * Replaces '$arch' and '$basearch' in the path and query part of the URL
+ * with the global ZYpp values. Examples:
+ *
+ * ftp://user:secret@site.net/$arch/ -> ftp://user:secret@site.net/i686/
+ * http://site.net/?basearch=$basearch -> http://site.net/?basearch=i386
+ */
+Url RepoVariablesUrlReplacer::operator()( const Url &value ) const
+{
+  Url newurl = value;
+  newurl.setPathData(replacer(value.getPathData()));
+  newurl.setQueryString(replacer(value.getQueryString()));
+
+  return newurl;
+}
+
+} // ns repo
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/RepoVariables.h b/zypp/repo/RepoVariables.h
new file mode 100644 (file)
index 0000000..012ba73
--- /dev/null
@@ -0,0 +1,69 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_VARIABLES_H_
+#define ZYPP_REPO_VARIABLES_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/Url.h"
+#include "zypp/Arch.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+
+  /**
+   * \short Repository variables
+   *
+   * ...
+   */
+  struct RepoVariablesStringReplacer : public std::unary_function<std::string, std::string>
+  {
+    RepoVariablesStringReplacer();
+
+    std::string operator()( const std::string &value ) const;
+
+    ~RepoVariablesStringReplacer();
+
+    void resetVarCache( void );
+
+    private:
+      mutable Arch sysarch;
+      mutable Arch basearch;
+      mutable std::string releasever;
+  };
+
+  /**
+   * \short Repository variables
+   *
+   * ...
+   */
+  struct RepoVariablesUrlReplacer : public std::unary_function<Url, Url>
+  {
+    RepoVariablesUrlReplacer();
+
+    Url operator()( const Url &url ) const;
+
+    ~RepoVariablesUrlReplacer();
+
+    void resetVarCache( void );
+
+    private:
+      RepoVariablesStringReplacer replacer;
+  };
+
+  } // ns repo
+} // ns zypp
+
+#endif
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/SUSEMediaVerifier.cc b/zypp/repo/SUSEMediaVerifier.cc
new file mode 100644 (file)
index 0000000..1a91de6
--- /dev/null
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <fstream>
+#include "zypp/repo/SUSEMediaVerifier.h"
+
+using namespace std;
+
+namespace zypp
+{
+namespace repo
+{
+
+SUSEMediaVerifier::SUSEMediaVerifier(const std::string & vendor_r,
+                                     const std::string & id_r,
+                                     const media::MediaNr media_nr)
+   : _media_vendor(vendor_r)
+    , _media_id(id_r)
+    , _media_nr(media_nr)
+{}
+
+SUSEMediaVerifier::SUSEMediaVerifier( int media_nr, const Pathname &path_r )
+  : _media_nr(media_nr)
+{
+  std::ifstream str(path_r.asString().c_str());
+  std::string vendor;
+  std::string id;
+  
+  if ( str )
+  {
+    getline(str, _media_vendor);
+    getline(str, _media_id);
+  }
+  else
+  {
+    ZYPP_THROW(Exception("Can't setup media verifier using file: '"
+        + path_r.asString() + "'"));
+  }
+}
+
+bool SUSEMediaVerifier::isDesiredMedia(const media::MediaAccessRef &ref)
+{
+  if (_media_vendor.empty() || _media_id.empty())
+    return true;
+
+    Pathname media_file = "/media." + str::numstring(_media_nr) + "/media";
+    ref->provideFile (media_file);
+    media_file = ref->localPath(media_file);
+    std::ifstream str(media_file.asString().c_str());
+    std::string vendor;
+    std::string id;
+#warning check the stream status
+    getline(str, vendor);
+    getline(str, id);
+
+    return (vendor == _media_vendor && id == _media_id );
+}
+
+}
+}
+
diff --git a/zypp/repo/SUSEMediaVerifier.h b/zypp/repo/SUSEMediaVerifier.h
new file mode 100644 (file)
index 0000000..b415c33
--- /dev/null
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_SUSE_MEDIAVERIFIER_H
+#define ZYPP_SUSE_MEDIAVERIFIER_H
+
+#include "zypp/media/MediaManager.h"
+#include "zypp/media/MediaAccess.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+
+    /**
+     * \short Implementation of the traditional SUSE media verifier
+     */
+    class SUSEMediaVerifier : public zypp::media::MediaVerifierBase
+    {
+      public:
+      /**
+       * \short create a verifier from attributes
+       *
+       * Creates a verifier for the media using
+       * the attributes
+       *
+       * \param vendor_r i.e. "SUSE Linux Products GmbH"
+       * \param id_r i.e. "20070718164719"
+       * \param media_nr media number
+       */
+      SUSEMediaVerifier(const std::string & vendor_r,
+                        const std::string & id_r,
+                        const media::MediaNr media_nr = 1);
+      
+      /**
+       * \short creates a verifier from a media file
+       *
+       * \param path_r Path to media.1/media kind file
+       */
+      SUSEMediaVerifier( int media_nr, const Pathname &path_r );
+      
+      /**
+        * \short Check if it is the desider media
+        *
+        * Check if the specified attached media contains
+        * the desired media number (e.g. SLES10 CD1).
+        *
+        * Reimplementation of virtual function, will be
+        * called by the component verifying the media.
+        */
+      virtual bool isDesiredMedia(const media::MediaAccessRef &ref);
+      
+      private:
+        std::string _media_vendor;
+        std::string _media_id;
+        media::MediaNr _media_nr;
+    };
+
+  }
+}
+#endif
diff --git a/zypp/repo/ServiceRepos.cc b/zypp/repo/ServiceRepos.cc
new file mode 100644 (file)
index 0000000..041c448
--- /dev/null
@@ -0,0 +1,115 @@
+#include <iostream>
+#include <sstream>
+#include "zypp/base/Logger.h"
+#include "zypp/repo/ServiceRepos.h"
+#include "zypp/repo/RepoException.h"
+#include "zypp/media/MediaException.h"
+#include "zypp/parser/RepoFileReader.h"
+#include "zypp/media/MediaManager.h"
+#include "zypp/parser/RepoindexFileReader.h"
+#include "zypp/ExternalProgram.h"
+
+using std::stringstream;
+using std::endl;
+
+namespace zypp
+{
+namespace repo
+{
+
+class ServiceRepos::Impl
+{
+public:
+    Impl()
+    {
+    }
+
+    virtual ~Impl()
+    {
+    }
+};
+
+class RIMServiceRepos : public ServiceRepos::Impl
+{
+public:
+    ServiceRepos::ProcessRepo _callback;
+
+    RIMServiceRepos(const ServiceInfo &service,
+                    const ServiceRepos::ProcessRepo & callback,
+                    const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() )
+        : _callback(callback)
+    {
+      // repoindex.xml must be fetched always without using cookies (bnc #573897)
+      Url serviceUrl( service.url() );
+      serviceUrl.setQueryParam( "cookies", "0" );
+
+      // download the repo index file
+      media::MediaManager mediamanager;
+      media::MediaAccessId mid = mediamanager.open( serviceUrl );
+      mediamanager.attach( mid );
+      mediamanager.provideFile( mid, "repo/repoindex.xml" );
+      Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
+      parser::RepoindexFileReader reader(path, _callback);
+      mediamanager.release( mid );
+      mediamanager.close( mid );
+    }
+
+    ~RIMServiceRepos()
+    {
+
+    }
+};
+
+class PluginServiceRepos : public ServiceRepos::Impl
+{
+public:
+    ServiceRepos::ProcessRepo _callback;
+
+    PluginServiceRepos(const ServiceInfo &service,
+                      const ServiceRepos::ProcessRepo & callback,
+                      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() )
+        : _callback(callback)
+    {
+      Url serviceUrl( service.url() );
+      stringstream buffer;
+
+      ExternalProgram::Arguments args;
+      args.reserve( 3 );
+      args.push_back( "/bin/sh" );
+      args.push_back( "-c" );
+      args.push_back( serviceUrl.getPathName() );
+      ExternalProgramWithStderr prog( args );
+      prog >> buffer;
+
+      if ( prog.close() != 0 )
+      {
+       // ServicePluginInformalException:
+       // Ignore this error but we'd like to report it somehow...
+       std::string errbuffer;
+       prog.stderrGetUpTo( errbuffer, '\0' );
+       ERR << "Capture plugin error:[" << endl << errbuffer << endl << ']' << endl;
+       ZYPP_THROW( repo::ServicePluginInformalException(errbuffer));
+      }
+
+      parser::RepoFileReader parser(buffer, _callback);
+    }
+
+    ~PluginServiceRepos()
+    {}
+};
+
+
+ServiceRepos::ServiceRepos(const ServiceInfo &service,
+                           const ServiceRepos::ProcessRepo & callback,
+                           const ProgressData::ReceiverFnc &progress)
+    : _impl( (service.type() == ServiceType::PLUGIN) ? (ServiceRepos::Impl *)(new PluginServiceRepos(service, callback, progress)) : (ServiceRepos::Impl *)(new RIMServiceRepos(service, callback, progress)))
+{
+}
+
+ServiceRepos::~ServiceRepos()
+{
+}
+
+
+}
+}
diff --git a/zypp/repo/ServiceRepos.h b/zypp/repo/ServiceRepos.h
new file mode 100644 (file)
index 0000000..66bef82
--- /dev/null
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_REPO_SERVICE_REPOS
+#define ZYPP_REPO_SERVICE_REPOS
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/ProgressData.h"
+#include "zypp/ServiceInfo.h"
+#include "zypp/RepoInfo.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    /**
+     * Retrieval of repository list for
+     * a service
+     */
+    class ServiceRepos : private base::NonCopyable
+    {
+    public:
+
+     /**
+      * Callback definition.
+      * First parameter is a \ref RepoInfo object with the resource
+      * second parameter is the resource type.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+      typedef function< bool( const RepoInfo & )> ProcessRepo;
+
+      ServiceRepos(const ServiceInfo &service,
+                   const ProcessRepo & callback,
+                   const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+      ~ServiceRepos();
+
+      /** Implementation  */
+      class Impl;
+    private:
+      RW_pointer<Impl> _impl;
+    };
+  } // ns repo
+} // ns zypp
+
+#endif
diff --git a/zypp/repo/ServiceType.cc b/zypp/repo/ServiceType.cc
new file mode 100644 (file)
index 0000000..d677936
--- /dev/null
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <map>
+#include "zypp/repo/RepoException.h"
+#include "ServiceType.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+
+
+  static std::map<std::string,ServiceType::Type> _table;
+
+  const ServiceType ServiceType::RIS(ServiceType::RIS_e);
+  const ServiceType ServiceType::NONE(ServiceType::NONE_e);
+  const ServiceType ServiceType::PLUGIN(ServiceType::PLUGIN_e);
+
+  ServiceType::ServiceType(const std::string & strval_r)
+    : _type(parse(strval_r))
+  {}
+
+  ServiceType::Type ServiceType::parse(const std::string & strval_r)
+  {
+    if (_table.empty())
+    {
+      // initialize it
+      _table["ris"] = ServiceType::RIS_e;
+      _table["RIS"] = ServiceType::RIS_e;
+      _table["nu"] = ServiceType::RIS_e;
+      _table["NU"] = ServiceType::RIS_e;
+      _table["plugin"] = ServiceType::PLUGIN_e;
+      _table["PLUGIN"] = ServiceType::PLUGIN_e;
+      _table["NONE"] = _table["none"] = ServiceType::NONE_e;
+    }
+
+    std::map<std::string,ServiceType::Type>::const_iterator it
+      = _table.find(strval_r);
+    if (it == _table.end())
+    {
+      ZYPP_THROW(RepoUnknownTypeException(
+        "Unknown service type '" + strval_r + "'"));
+    }
+    return it->second;
+  }
+
+
+  const std::string & ServiceType::asString() const
+  {
+    static std::map<Type, std::string> _table;
+    if ( _table.empty() )
+    {
+      // initialize it
+      _table[RIS_e]  = "ris";
+      _table[PLUGIN_e]  = "plugin";
+      _table[NONE_e] = "NONE";
+    }
+    return _table[_type];
+  }
+
+
+  } // ns repo
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/ServiceType.h b/zypp/repo/ServiceType.h
new file mode 100644 (file)
index 0000000..51feb1f
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_SERVICE_TYPE_H_
+#define ZYPP_SERVICE_TYPE_H_
+
+#include <iosfwd>
+#include <string>
+
+namespace zypp
+{
+  namespace repo
+  {
+
+  /**
+   * \short Service type enumeration
+   *
+   * Currently we have only RIS service, but more can come later.
+   */
+  struct ServiceType
+  {
+    /**
+     * Repository Index Service (RIS)
+     * (formerly known as 'Novell Update' (NU) service)
+     */
+    static const ServiceType RIS;
+    /** No service set. */
+    static const ServiceType NONE;
+    /**
+     * Plugin services are scripts installed on
+     * your system that provide the package manager with
+     * repositories.
+     *
+     * The mechanism used to create this repository list
+     * is completely up to the script
+     */
+    static const ServiceType PLUGIN;
+
+    enum Type
+    {
+      NONE_e,
+      RIS_e,
+      PLUGIN_e,
+    };
+
+    ServiceType() : _type(NONE_e) {}
+
+    ServiceType(Type type) : _type(type) {}
+
+    explicit ServiceType(const std::string & strval_r);
+
+    Type toEnum() const { return _type; }
+
+    ServiceType::Type parse(const std::string & strval_r);
+
+    const std::string & asString() const;
+
+    Type _type;
+  };
+
+
+  inline std::ostream & operator<<( std::ostream & str, const ServiceType & obj )
+  { return str << obj.asString(); }
+
+  inline bool operator==(const ServiceType & obj1, const ServiceType & obj2)
+  { return obj1._type == obj2._type; }
+
+  inline bool operator!=(const ServiceType & obj1, const ServiceType & obj2)
+  { return ! (obj1 == obj2); }
+
+
+  } // ns repo
+} // ns zypp
+
+#endif /* ZYPP_SERVICE_TYPE_H_ */
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/SrcPackageProvider.cc b/zypp/repo/SrcPackageProvider.cc
new file mode 100644 (file)
index 0000000..db977e9
--- /dev/null
@@ -0,0 +1,106 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/SrcPackageProvider.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+
+#include "zypp/repo/SrcPackageProvider.h"
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/SrcPackage.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      typedef std::string     (SrcPackage::*inlined)() const;
+      typedef OnMediaLocation (SrcPackage::*location)() const;
+
+      /** Provide a SrcPackage in a local file. */
+      ManagedFile doProvideSrcPackage( repo::RepoMediaAccess & access_r,
+                                   const SrcPackage & script_r,
+                                   inlined inlined_r, location location_r )
+      {
+        ManagedFile ret;
+
+        // 1st try inlined
+        std::string inlined( (script_r.*inlined_r)() );
+        if ( ! inlined.empty() )
+        {
+          // Take care the TmpFile goes out of scope BEFORE the
+          // ofstream opens the file again.
+          ret = ManagedFile( filesystem::TmpFile( filesystem::TmpPath::defaultLocation(),
+                                                  "zypp-script-"+script_r.name() ),
+                             filesystem::unlink );
+          std::ofstream str( ret.value().c_str() );
+          str << inlined << endl;
+        }
+        else
+        {
+          // otherwise try download
+          OnMediaLocation location( (script_r.*location_r)() );
+          if ( ! location.filename().empty() )
+          {
+            ret = access_r.provideFile( script_r.repoInfo(), location );
+          }
+          else
+          {
+            // no script
+            return ManagedFile();
+          }
+        }
+
+        // HERE: got the script
+        filesystem::chmod( ret, 0700 );
+        return ret;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : SrcPackageProvider::SrcPackageProvider
+    // METHOD TYPE : Ctor
+    //
+    SrcPackageProvider::SrcPackageProvider( repo::RepoMediaAccess & access_r )
+      : _access( access_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : SrcPackageProvider::~SrcPackageProvider
+    // METHOD TYPE : Dtor
+    //
+    SrcPackageProvider::~SrcPackageProvider()
+    {}
+
+    ManagedFile SrcPackageProvider::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r ) const
+    {
+      return _access.provideFile( srcPackage_r->repoInfo(), srcPackage_r->location() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/repo/SrcPackageProvider.h b/zypp/repo/SrcPackageProvider.h
new file mode 100644 (file)
index 0000000..e8d98fe
--- /dev/null
@@ -0,0 +1,61 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/repo/SrcPackageProvider.h
+ *
+*/
+#ifndef ZYPP_REPO_SRCPACKAGEPROVIDER_H
+#define ZYPP_REPO_SRCPACKAGEPROVIDER_H
+
+#include <iosfwd>
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/repo/RepoProvideFile.h"
+#include "zypp/ManagedFile.h"
+#include "zypp/ResTraits.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace repo
+  { /////////////////////////////////////////////////////////////////
+
+    class RepoMediaAccess;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SrcPackageProvider
+    //
+    /** */
+    class SrcPackageProvider : private base::NonCopyable
+    {
+    public:
+      /** Ctor */
+      SrcPackageProvider( repo::RepoMediaAccess & access_r );
+      /** Dtor */
+      ~SrcPackageProvider();
+
+    public:
+      /** Provide SrcPackage in a local file. */
+      ManagedFile provideSrcPackage( const SrcPackage_constPtr & srcPackage_r ) const;
+
+    private:
+      RepoMediaAccess & _access;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace repo
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_REPO_SRCPACKAGEPROVIDER_H
diff --git a/zypp/repo/susetags/Downloader.cc b/zypp/repo/susetags/Downloader.cc
new file mode 100644 (file)
index 0000000..d1580ec
--- /dev/null
@@ -0,0 +1,231 @@
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/Fetcher.h"
+#include "zypp/Locale.h"
+#include "zypp/ZConfig.h"
+#include "zypp/repo/MediaInfoDownloader.h"
+#include "zypp/repo/susetags/Downloader.h"
+#include "zypp/parser/ParseException.h"
+#include "zypp/parser/susetags/RepoIndex.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/KeyContext.h" // for SignatureFileChecker
+
+using namespace std;
+using namespace zypp::parser;
+using namespace zypp::parser::susetags;
+
+namespace zypp
+{
+namespace repo
+{
+namespace susetags
+{
+
+Downloader::Downloader( const RepoInfo &repoinfo, const Pathname &delta_dir )
+  : repo::Downloader(repoinfo), _delta_dir(delta_dir)
+{
+}
+
+RepoStatus Downloader::status( MediaSetAccess &media )
+{
+  Pathname content = media.provideFile( repoInfo().path() + "/content");
+  // the media.1 is always in the root of the media, not like the content
+  // file which is in the path() location
+  Pathname mediafile = media.provideFile( "/media.1/media" );
+
+  return RepoStatus(content) && RepoStatus(mediafile);
+}
+
+// search old repository file file to run the delta algorithm on
+static Pathname search_deltafile( const Pathname &dir, const Pathname &file )
+{
+  Pathname deltafile(dir + file.basename());
+  if (PathInfo(deltafile).isExist())
+    return deltafile;
+  return Pathname();
+}
+
+void Downloader::download( MediaSetAccess &media,
+                           const Pathname &dest_dir,
+                           const ProgressData::ReceiverFnc & progress )
+{
+  downloadMediaInfo( dest_dir, media );
+
+  SignatureFileChecker sigchecker/*(repoInfo().name())*/;
+
+  Pathname sig = repoInfo().path() + "/content.asc";
+
+  this->enqueue( OnMediaLocation( sig, 1 ).setOptional(true) );
+  this->start( dest_dir, media );
+  // only if there is a signature in the destination directory
+  if ( PathInfo(dest_dir / sig ).isExist() )
+      sigchecker = SignatureFileChecker( dest_dir + sig/*, repoInfo().name() */);
+  this->reset();
+
+  Pathname key = repoInfo().path() + "/content.key";
+
+  this->enqueue( OnMediaLocation( key, 1 ).setOptional(true) );
+  this->start( dest_dir, media );
+
+  KeyContext context;
+  context.setRepoInfo(repoInfo());
+  // only if there is a key in the destination directory
+  if ( PathInfo(dest_dir / key).isExist() )
+    sigchecker.addPublicKey(dest_dir + key, context);
+  // set the checker context even if the key is not known (unsigned repo, key
+  // file missing; bnc #495977)
+  else
+    sigchecker.setKeyContext(context);
+
+  this->reset();
+
+  if ( ! repoInfo().gpgCheck() )
+  {
+    WAR << "Signature checking disabled in config of repository " << repoInfo().alias() << endl;
+  }
+  this->enqueue( OnMediaLocation( repoInfo().path() + "/content", 1 ),
+                 repoInfo().gpgCheck() ? FileChecker(sigchecker) : FileChecker(NullFileChecker()) );
+  this->start( dest_dir, media );
+  this->reset();
+
+  Pathname descr_dir;
+
+  // Content file first to get the repoindex
+  {
+    Pathname inputfile( dest_dir +  repoInfo().path() + "/content" );
+    ContentFileReader content;
+    content.setRepoIndexConsumer( bind( &Downloader::consumeIndex, this, _1 ) );
+    content.parse( inputfile );
+  }
+  if ( ! _repoindex )
+  {
+    ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No repository index in content file." ) );
+  }
+  MIL << "RepoIndex: " << _repoindex << endl;
+  if ( _repoindex->metaFileChecksums.empty() )
+  {
+    ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No metadata checksums in content file." ) );
+  }
+  if ( _repoindex->signingKeys.empty() )
+  {
+    WAR << "No signing keys defined." << endl;
+  }
+
+  // Prepare parsing
+  descr_dir = _repoindex->descrdir; // path below reporoot
+  //_datadir  = _repoIndex->datadir;  // path below reporoot
+
+
+  for_( it, _repoindex->metaFileChecksums.begin(), _repoindex->metaFileChecksums.end() )
+  {
+    // omit unwanted translations
+    if ( str::hasPrefix( it->first, "packages" ) )
+    {
+      std::string rest( str::stripPrefix( it->first, "packages" ) );
+      if ( ! (   rest.empty()
+              || rest == ".DU"
+              || rest == ".en"
+              || rest == ".gz"
+              || rest == ".DU.gz"
+              || rest == ".en.gz" ) )
+      {
+        // Not 100% correct as we take each fallback of textLocale
+        Locale toParse( ZConfig::instance().textLocale() );
+        while ( toParse != Locale::noCode )
+        {
+          if ( rest == ("."+toParse.code()) || (rest == ("."+toParse.code()+".gz")) )
+            break;
+          toParse = toParse.fallback();
+        }
+        if ( toParse == Locale::noCode )
+        {
+          // discard
+          continue;
+        }
+      }
+    }
+    else if ( it->first == "patterns.pat"
+              || it->first == "patterns.pat.gz" )
+    {
+      // take all patterns in one go
+    }
+    else if ( str::endsWith( it->first, ".pat" )
+              || str::endsWith( it->first, ".pat.gz" ) )
+    {
+
+      // *** see also zypp/parser/susetags/RepoParser.cc ***
+
+      // omit unwanted patterns, see https://bugzilla.novell.com/show_bug.cgi?id=298716
+      // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
+      // split at dots, take .pat or .pat.gz into account
+
+      std::vector<std::string> patparts;
+      unsigned archpos = 2;
+      // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
+      unsigned count = str::split( it->first, std::back_inserter(patparts), "." );
+      if ( patparts[count-1] == "gz" )
+          archpos++;
+
+      if ( count > archpos )
+      {
+        try                            // might by an invalid architecture
+        {
+          Arch patarch( patparts[count-archpos] );
+          if ( !patarch.compatibleWith( ZConfig::instance().systemArchitecture() ) )
+          {
+            // discard, if not compatible
+            MIL << "Discarding pattern " << it->first << endl;
+            continue;
+          }
+        }
+        catch ( const Exception & excpt )
+        {
+          WAR << "Pattern file name does not contain recognizable architecture: " << it->first << endl;
+          // keep .pat file if it doesn't contain an recognizable arch
+        }
+      }
+    }
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + descr_dir + it->first, 1 );
+    location.setChecksum( it->second );
+    this->enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir + descr_dir, it->first));
+  }
+
+  for_( it, _repoindex->mediaFileChecksums.begin(), _repoindex->mediaFileChecksums.end() )
+  {
+    // Repo adopts license files listed in HASH
+    if ( it->first != "license.tar.gz" )
+      continue;
+
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + it->first, 1 );
+    location.setChecksum( it->second );
+    this->enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir, it->first));
+  }
+
+  for_( it, _repoindex->signingKeys.begin(),_repoindex->signingKeys.end() )
+  {
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + it->first, 1 );
+    location.setChecksum( it->second );
+    this->enqueueDigested(location);
+  }
+
+  this->start( dest_dir, media );
+}
+
+void Downloader::consumeIndex( const RepoIndex_Ptr & data_r )
+{
+  MIL << "Consuming repo index" << endl;
+  _repoindex = data_r;
+}
+
+}// ns susetags
+}// ns source
+} // ns zypp
diff --git a/zypp/repo/susetags/Downloader.h b/zypp/repo/susetags/Downloader.h
new file mode 100644 (file)
index 0000000..1b4be79
--- /dev/null
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_SOURCE_SUSETAGS_DOWNLOADER
+#define ZYPP_SOURCE_SUSETAGS_DOWNLOADER
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/ProgressData.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/repo/Downloader.h"
+#include "zypp/parser/susetags/ContentFileReader.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    namespace susetags
+    {
+  
+      /**
+       * \short Downloader for SUSETags (YaST2) repositories
+       * Encapsulates all the knowledge of which files have
+       * to be downloaded to the local disk.
+       */
+      class Downloader : public repo::Downloader
+      {
+      public:
+        /**
+         * \short Constructor from the repository information
+         *
+         * The repository information allows more context to be given
+         * to the user when something fails.
+         *
+         * \param info Repository information
+         */
+        Downloader( const RepoInfo &info, const Pathname &delta_dir = Pathname() );
+
+        /**
+         * \short Download metadata to a local directory
+         *
+         * \param media Media access to the repository url
+         * \param dest_dir Local destination directory
+         * \param progress progress receiver
+         */
+        void download( MediaSetAccess &media,
+                       const Pathname &dest_dir,
+                       const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+        /**
+         * \short Status of the remote repository
+         */
+        RepoStatus status( MediaSetAccess &media );
+        
+        /**
+         * Content file parser consumer
+         */
+        void consumeIndex( const parser::susetags::RepoIndex_Ptr & data_r );
+
+      private:
+        parser::susetags::RepoIndex_Ptr _repoindex;
+       Pathname _delta_dir;
+      };
+
+    } // ns susetags
+  } // ns source
+} // ns zypp
+
+#endif
diff --git a/zypp/repo/yum/Downloader.cc b/zypp/repo/yum/Downloader.cc
new file mode 100644 (file)
index 0000000..529f091
--- /dev/null
@@ -0,0 +1,200 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <fstream>
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/Date.h"
+
+#include "zypp/parser/yum/RepomdFileReader.h"
+#include "zypp/parser/yum/PatchesFileReader.h"
+#include "Downloader.h"
+#include "zypp/repo/MediaInfoDownloader.h"
+#include "zypp/base/UserRequestException.h"
+#include "zypp/parser/xml/Reader.h"
+#include "zypp/KeyContext.h"
+
+using namespace std;
+using namespace zypp::xml;
+using namespace zypp::parser::yum;
+
+namespace zypp
+{
+namespace repo
+{
+namespace yum
+{
+
+Downloader::Downloader( const RepoInfo &repoinfo , const Pathname &delta_dir)
+  : repo::Downloader(repoinfo), _delta_dir(delta_dir), _media_ptr(0L)
+{
+}
+
+
+RepoStatus Downloader::status( MediaSetAccess &media )
+{
+  Pathname repomd = media.provideFile( repoInfo().path() + "/repodata/repomd.xml");
+  return RepoStatus(repomd);
+}
+
+static OnMediaLocation
+loc_with_path_prefix(const OnMediaLocation & loc,
+                     const Pathname & prefix)
+{
+  if (prefix.empty() || prefix == "/")
+    return loc;
+
+  OnMediaLocation loc_with_path(loc);
+  loc_with_path.changeFilename(prefix / loc.filename());
+  return loc_with_path;
+}
+
+// search old repository file file to run the delta algorithm on
+static Pathname search_deltafile( const Pathname &dir, const Pathname &file )
+{
+  Pathname deltafile;
+  if (!PathInfo(dir).isDir())
+    return deltafile;
+  string base = file.basename();
+  size_t hypoff = base.find("-");
+  if (hypoff != string::npos)
+    base.replace(0, hypoff + 1, "");
+  size_t basesize = base.size();
+  std::list<Pathname> retlist;
+  if (!filesystem::readdir(retlist, dir, false))
+  {
+    for_( it, retlist.begin(), retlist.end() )
+    {
+      string fn = it->asString();
+      if (fn.size() >= basesize && fn.substr(fn.size() - basesize, basesize) == base)
+       deltafile = *it;
+    }
+  }
+  return deltafile;
+}
+
+bool Downloader::patches_Callback( const OnMediaLocation &loc,
+                                   const string &id )
+{
+  OnMediaLocation loc_with_path(loc_with_path_prefix(loc, repoInfo().path()));
+  MIL << id << " : " << loc_with_path << endl;
+  this->enqueueDigested(loc_with_path,  FileChecker(), search_deltafile(_delta_dir + "repodata", loc.filename()));
+  return true;
+}
+
+bool Downloader::repomd_Callback( const OnMediaLocation &loc,
+                                  const ResourceType &dtype )
+{
+  OnMediaLocation loc_with_path(loc_with_path_prefix(loc, repoInfo().path()));
+  MIL << dtype << " : " << loc_with_path << endl;
+
+  //! \todo do this through a ZConfig call so that it is always in sync with parser
+  // skip other
+  if ( dtype == ResourceType::OTHER )
+  {
+    MIL << "Skipping other.xml" << endl;
+    return true;
+  }
+  // skip filelists
+  if ( dtype == ResourceType::FILELISTS )
+  {
+    MIL << "Skipping filelists.xml.gz" << endl;
+    return true;
+  }
+
+  this->enqueueDigested(loc_with_path, FileChecker(), search_deltafile(_delta_dir + "repodata", loc.filename()));
+
+  // We got a patches file we need to read, to add patches listed
+  // there, so we transfer what we have in the queue, and
+  // queue the patches in the patches callback
+  if ( dtype == ResourceType::PATCHES )
+  {
+    this->start( _dest_dir, *_media_ptr );
+    // now the patches.xml file must exists
+    PatchesFileReader( _dest_dir + repoInfo().path() + loc.filename(),
+                       bind( &Downloader::patches_Callback, this, _1, _2));
+  }
+
+  return true;
+}
+
+void Downloader::download( MediaSetAccess &media,
+                           const Pathname &dest_dir,
+                           const ProgressData::ReceiverFnc & progressrcv )
+{
+  Pathname repomdpath =  repoInfo().path() + "/repodata/repomd.xml";
+  Pathname keypath =  repoInfo().path() + "/repodata/repomd.xml.key";
+  Pathname sigpath =  repoInfo().path() + "/repodata/repomd.xml.asc";
+
+  _media_ptr = (&media);
+
+  ProgressData progress;
+  progress.sendTo(progressrcv);
+  progress.toMin();
+
+  //downloadMediaInfo( dest_dir, _media );
+
+  _dest_dir = dest_dir;
+
+#warning Do we need SignatureFileChecker(string descr)?
+  SignatureFileChecker sigchecker/*(repoInfo().name())*/;
+
+  this->enqueue( OnMediaLocation(sigpath,1).setOptional(true) );
+  this->start( dest_dir, *_media_ptr);
+  // only add the signature if it exists
+  if ( PathInfo(dest_dir / sigpath).isExist() )
+      sigchecker = SignatureFileChecker(dest_dir / sigpath);
+  this->reset();
+
+  this->enqueue( OnMediaLocation(keypath,1).setOptional(true) );
+  this->start( dest_dir, *_media_ptr);
+
+  KeyContext context;
+  context.setRepoInfo(repoInfo());
+  // only add the key if it exists
+  if ( PathInfo(dest_dir / keypath).isExist() )
+    sigchecker.addPublicKey(dest_dir + keypath, context);
+  // set the checker context even if the key is not known (unsigned repo, key
+  // file missing; bnc #495977)
+  else
+    sigchecker.setKeyContext(context);
+
+  this->reset();
+
+  if ( ! progress.tick() )
+    ZYPP_THROW(AbortRequestException());
+
+  if ( ! repoInfo().gpgCheck() )
+    WAR << "Signature checking disabled in config of repository " << repoInfo().alias() << endl;
+
+  this->enqueue( OnMediaLocation(repomdpath,1),
+                 repoInfo().gpgCheck() ? FileChecker(sigchecker) : FileChecker(NullFileChecker()) );
+  this->start( dest_dir, *_media_ptr);
+
+  if ( ! progress.tick() )
+        ZYPP_THROW(AbortRequestException());
+
+  this->reset();
+
+  Reader reader( dest_dir + repoInfo().path() + "/repodata/repomd.xml" );
+  RepomdFileReader( dest_dir + repoInfo().path() + "/repodata/repomd.xml", bind( &Downloader::repomd_Callback, this, _1, _2));
+
+  // ready, go!
+  this->start( dest_dir, *_media_ptr);
+  progress.toMax();
+}
+
+}// ns yum
+}// ns source
+} // ns zypp
+
+
+
diff --git a/zypp/repo/yum/Downloader.h b/zypp/repo/yum/Downloader.h
new file mode 100644 (file)
index 0000000..f34b42b
--- /dev/null
@@ -0,0 +1,86 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef ZYPP_SOURCE_YUM_DOWNLOADER
+#define ZYPP_SOURCE_YUM_DOWNLOADER
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/Fetcher.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/MediaSetAccess.h"
+#include "zypp/ProgressData.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/repo/Downloader.h"
+#include "zypp/repo/yum/ResourceType.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    namespace yum
+    {
+     /**
+      * \short Downloader for YUM (rpm-nmd) repositories
+      * Encapsulates all the knowledge of which files have
+      * to be downloaded to the local disk.
+      *
+      * \code
+      * MediaSetAccess media(url);
+      * Downloader yum(path);
+      * yum.download( media, "localdir");
+      * \endcode
+      */
+      class Downloader : public repo::Downloader
+      {
+      public:
+         
+        /**
+         * \short Constructor from the repository information
+         *
+         * The repository information allows more context to be given
+         * to the user when something fails.
+         *
+         * \param info Repository information
+         */
+        Downloader( const RepoInfo &info , const Pathname &delta_dir = Pathname());
+
+        /**
+         * \short Download metadata to a local directory
+         *
+         * \param media Media access to the repository url
+         * \param dest_dir Local destination directory
+         * \param progress progress receiver
+         */
+        void download( MediaSetAccess &media,
+                       const Pathname &dest_dir,
+                       const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
+        
+        /**
+         * \short Status of the remote repository
+         */
+        RepoStatus status( MediaSetAccess &media );
+        
+       protected:
+        bool repomd_Callback( const OnMediaLocation &loc, const ResourceType &dtype );
+        bool patches_Callback( const OnMediaLocation &loc, const std::string &id );
+       private:
+        Pathname _dest_dir;
+        Pathname _delta_dir;
+        std::list<OnMediaLocation> _patches_files;
+        
+        MediaSetAccess *_media_ptr;
+      };
+
+    } // ns yum
+  } // ns source
+} // ns zypp
+
+#endif
diff --git a/zypp/repo/yum/ResourceType.cc b/zypp/repo/yum/ResourceType.cc
new file mode 100644 (file)
index 0000000..650ba9f
--- /dev/null
@@ -0,0 +1,98 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#include <iostream>
+#include <map>
+#include "zypp/base/Exception.h"
+#include "ResourceType.h"
+
+namespace zypp
+{
+  namespace repo
+  {
+    namespace yum
+    {
+
+
+  static std::map<std::string,ResourceType::Type> _table;
+
+  const ResourceType ResourceType::NONE(ResourceType::NONE_e);
+  const ResourceType ResourceType::REPOMD(ResourceType::REPOMD_e);
+  const ResourceType ResourceType::PRIMARY(ResourceType::PRIMARY_e);
+  const ResourceType ResourceType::OTHER(ResourceType::OTHER_e);
+  const ResourceType ResourceType::FILELISTS(ResourceType::FILELISTS_e);
+  const ResourceType ResourceType::GROUP(ResourceType::GROUP_e);
+  const ResourceType ResourceType::PATCHES(ResourceType::PATCHES_e);
+  const ResourceType ResourceType::PATCH(ResourceType::PATCH_e);
+  const ResourceType ResourceType::PRODUCT(ResourceType::PRODUCT_e);
+  const ResourceType ResourceType::PATTERNS(ResourceType::PATTERNS_e);
+  const ResourceType ResourceType::PRIMARY_DB(ResourceType::PRIMARY_DB_e);
+  const ResourceType ResourceType::OTHER_DB(ResourceType::OTHER_DB_e);
+
+  ResourceType::ResourceType(const std::string & strval_r)
+    : _type(parse(strval_r))
+  {}
+
+  ResourceType::Type ResourceType::parse(const std::string & strval_r)
+  {
+    if (_table.empty())
+    {
+      // initialize it
+      _table["repomd"] = ResourceType::REPOMD_e;
+      _table["primary"] = ResourceType::PRIMARY_e;
+      _table["other"] = ResourceType::OTHER_e;
+      _table["filelists"] = ResourceType::FILELISTS_e;
+      _table["group"] = ResourceType::GROUP_e;
+      _table["patches"] = ResourceType::PATCHES_e;
+      _table["patch"] = ResourceType::PATCH_e;
+      _table["product"] = ResourceType::PRODUCT_e;
+      _table["patterns"] = ResourceType::PATTERNS_e;
+      _table["primary_db"] = ResourceType::PRIMARY_DB_e;
+      _table["other_db"] = ResourceType::OTHER_DB_e;
+      _table["NONE"] = _table["none"] = ResourceType::NONE_e;
+    }
+
+    std::map<std::string,ResourceType::Type>::const_iterator it
+      = _table.find(strval_r);
+    if (it == _table.end())
+    {
+      return ResourceType::NONE_e;
+    }
+    return it->second;
+  }
+
+
+  const std::string & ResourceType::asString() const
+  {
+    static std::map<Type, std::string> _table;
+    if ( _table.empty() )
+    {
+      // initialize it
+      _table[REPOMD_e]   = "repomd";
+      _table[PRIMARY_e]   = "primary";
+      _table[OTHER_e]   = "other";
+      _table[FILELISTS_e]   = "filelists";
+      _table[GROUP_e]   = "group";
+      _table[PATCHES_e]   = "patches";
+      _table[PATCH_e]  = "patch";
+      _table[PRODUCT_e]  = "product";
+      _table[PATTERNS_e]  = "patterns";
+      _table[OTHER_DB_e]  = "other_db";
+      _table[PRIMARY_DB_e]  = "primary_db";
+      _table[NONE_e] = "NONE";
+    }
+    return _table[_type];
+  }
+
+
+    } // ns yum
+  } // ns source
+} // ns zypp
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/repo/yum/ResourceType.h b/zypp/repo/yum/ResourceType.h
new file mode 100644 (file)
index 0000000..2d5cf9e
--- /dev/null
@@ -0,0 +1,86 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+
+#ifndef YUMRESOURCETYPE_H_
+#define YUMRESOURCETYPE_H_
+
+#include <iosfwd>
+#include<string>
+
+namespace zypp
+{
+  namespace repo
+  {
+    namespace yum
+    {
+
+
+  /**
+   *
+   */
+  struct ResourceType
+  {
+    static const ResourceType NONE; // unknown
+    static const ResourceType REPOMD;
+    static const ResourceType PRIMARY;
+    static const ResourceType OTHER;
+    static const ResourceType FILELISTS;
+    static const ResourceType GROUP;
+    static const ResourceType PATCHES; // suse extension
+    static const ResourceType PATCH;   // suse extension
+    static const ResourceType PRODUCT; // suse extension
+    static const ResourceType PATTERNS; // suse extension
+    // sqlite caches yum extensions:
+    static const ResourceType PRIMARY_DB; // yum extension
+    static const ResourceType OTHER_DB; // yum extension
+
+    enum Type
+    {
+      NONE_e,
+      REPOMD_e,
+      PRIMARY_e,
+      OTHER_e,
+      FILELISTS_e,
+      GROUP_e,
+      PATCHES_e,
+      PATCH_e,
+      PRODUCT_e,
+      PATTERNS_e,
+      PRIMARY_DB_e,
+      OTHER_DB_e,
+    };
+
+    ResourceType(Type type) : _type(type) {}
+
+    explicit ResourceType(const std::string & strval_r);
+
+    Type toEnum() const { return _type; }
+
+    ResourceType::Type parse(const std::string & strval_r);
+
+    const std::string & asString() const;
+
+    Type _type;
+  };
+
+
+  inline std::ostream & operator<<( std::ostream & str, const ResourceType & obj )
+  { return str << obj.asString(); }
+
+  inline bool operator==(const ResourceType & obj1, const ResourceType & obj2)
+  { return obj1._type == obj2._type; }
+
+
+    } // ns yum
+  } // ns source
+} // ns zypp
+
+#endif /*YUMRESOURCETYPE_H_*/
+
+// vim: set ts=2 sts=2 sw=2 et ai:
diff --git a/zypp/sat/AttrMatcher.cc b/zypp/sat/AttrMatcher.cc
new file mode 100644 (file)
index 0000000..3646be1
--- /dev/null
@@ -0,0 +1,342 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/AttrMatcher.cc
+ *
+*/
+extern "C"
+{
+#include <satsolver/repo.h>
+}
+
+#include <iostream>
+#include <sstream>
+#include <boost/mpl/int.hpp>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+
+#include "zypp/sat/AttrMatcher.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Match
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const int Match::_modemask = SEARCH_STRINGMASK;
+  const int Match::_flagmask = ~_modemask;
+
+  // option flags
+  const Match Match::NOCASE            (SEARCH_NOCASE);
+  const Match Match::NO_STORAGE_SOLVABLE(SEARCH_NO_STORAGE_SOLVABLE);
+  const Match Match::SUB               (SEARCH_SUB);
+  const Match Match::ARRAYSENTINEL     (SEARCH_ARRAYSENTINEL);
+  const Match Match::SKIP_KIND         (SEARCH_SKIP_KIND);
+  const Match Match::FILES             (SEARCH_FILES);
+
+  Match::Mode Match::mode() const
+  {
+    switch ( modeval() )
+    {
+      case 0:                  return NOTHING;         break;
+      case SEARCH_STRING:      return STRING;          break;
+      case SEARCH_STRINGSTART: return STRINGSTART;     break;
+      case SEARCH_STRINGEND:   return STRINGEND;       break;
+      case SEARCH_SUBSTRING:   return SUBSTRING;       break;
+      case SEARCH_GLOB:                return GLOB;            break;
+      case SEARCH_REGEX:       return REGEX;           break;
+    }
+    return OTHER;
+  }
+
+  int Match::modeval( Mode mode_r )
+  {
+    switch ( mode_r )
+    {
+      case NOTHING:    return 0;                       break;
+      case STRING:     return SEARCH_STRING;           break;
+      case STRINGSTART:        return SEARCH_STRINGSTART;      break;
+      case STRINGEND:  return SEARCH_STRINGEND;        break;
+      case SUBSTRING:  return SEARCH_SUBSTRING;        break;
+      case GLOB:       return SEARCH_GLOB;             break;
+      case REGEX:      return SEARCH_REGEX;            break;
+      case OTHER:      return SEARCH_STRINGMASK;       break;
+    }
+    return SEARCH_STRINGMASK;
+  }
+
+  std::string Match::asString() const
+  { std::ostringstream str; str << *this; return str.str(); }
+
+  std::ostream & operator<<( std::ostream & str, Match::Mode obj )
+  {
+    switch ( obj )
+    {
+#define OUTS(V) case Match::V: return str << #V; break
+      OUTS( NOTHING );
+      OUTS( STRING );
+      OUTS( STRINGSTART );
+      OUTS( STRINGEND );
+      OUTS( SUBSTRING );
+      OUTS( GLOB );
+      OUTS( REGEX );
+      OUTS( OTHER );
+#undef OUTS
+    }
+    return str << "Match::Mode::UNKNOWN";
+  }
+
+  std::ostream & operator<<( std::ostream & str, const Match & obj )
+  {
+    if ( ! obj )
+      return str << "NOTHING";
+
+    const char * sep = "|";
+    Match::Mode mode( obj.mode() );
+    switch ( mode )
+    {
+      case Match::NOTHING:
+        sep = 0; // suppress 'NOTHING|'
+        break;
+      case Match::OTHER:
+        str << mode<<"("<<obj.modeval()<<")"; // check whether satsolver has introduced new modes!
+        break;
+      default:
+        str << mode;
+        break;
+    }
+
+    int val = obj.flagval();
+    if ( val )
+    {
+#define OUTS(V) if ( val & Match::V.get() ) { val &= ~Match::V.get(); if ( sep ) str << sep; else sep = "|"; str << #V; }
+      OUTS( NOCASE );
+      OUTS( NO_STORAGE_SOLVABLE );
+      OUTS( SUB );
+      OUTS( ARRAYSENTINEL );
+      OUTS( SKIP_KIND );
+      OUTS( FILES );
+#undef OUTS
+      if ( val )
+      {
+        if ( sep ) str << sep;
+        str << zypp::str::hexstring( val ); // check whether satsolver has introduced new flags.
+      }
+    }
+    return str;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : MatchException
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  MatchUnknownModeException::MatchUnknownModeException( const Match & mode_r, const std::string & msg_r )
+  : MatchException( msg_r.empty() ? str::form(_("Unknown match mode '%s'"), mode_r.asString().c_str() )
+                                  : str::form(_("Unknown match mode '%s' for pattern '%s'"), mode_r.asString().c_str(), msg_r.c_str() ) )
+  {}
+
+  MatchInvalidRegexException::MatchInvalidRegexException( const std::string & regex_r, int regcomp_r )
+  : MatchException( regcomp_r ? str::form(_("Invalid regular expression '%s': regcomp returned %d"), regex_r.c_str(), regcomp_r )
+                              : str::form(_("Invalid regular expression '%s'"), regex_r.c_str() ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher::Impl
+    //
+    /** AttrMatcher implementation.
+     *
+     * Take care to release any allocated regex by calling
+     * \c ::datamatcher_free.
+     */
+    struct AttrMatcher::Impl
+    {
+      Impl()
+      {}
+
+      Impl( const std::string & search_r, const Match & flags_r )
+        : _search( search_r )
+        , _flags( flags_r )
+      {}
+
+      ~Impl()
+      { invalidate(); }
+
+      /** Compile the pattern. */
+      void compile() const
+      {
+        if ( !_matcher )
+        {
+          if ( _flags.mode() == Match::OTHER )
+            ZYPP_THROW( MatchUnknownModeException( _flags, _search ) );
+
+          _matcher.reset( new ::_Datamatcher );
+          int res = ::datamatcher_init( _matcher.get(), _search.c_str(), _flags.get() );
+          if ( res )
+          {
+            _matcher.reset();
+            ZYPP_THROW( MatchInvalidRegexException( _search, res ) );
+          }
+        }
+      }
+
+      /** Whether the pattern is already compiled. */
+      bool isCompiled() const
+      { return _matcher; }
+
+      /** Return whether string matches. */
+      bool doMatch( const char * string_r ) const
+      {
+        compile(); // nop if already compiled.
+
+        if ( ! string_r )
+          return false; // NULL never matches
+        return ::datamatcher_match( _matcher.get(), string_r );
+      }
+
+      /** The current searchstring. */
+      const std::string & searchstring() const
+      { return _search; }
+
+      /** Set a new searchstring. */
+      void setSearchstring( const std::string & string_r )
+      { invalidate(); _search = string_r; }
+
+      /** The current search flags. */
+      const Match & flags() const
+      { return _flags; }
+
+      /** Set new search flags. */
+      void setFlags( const Match & flags_r )
+      { invalidate(); _flags = flags_r; }
+
+      private:
+        /** Has to be called if _search or _flags change. */
+        void invalidate()
+        {
+          if ( _matcher )
+            ::datamatcher_free( _matcher.get() );
+          _matcher.reset();
+        }
+
+      private:
+        std::string _search;
+        Match       _flags;
+        mutable scoped_ptr< ::_Datamatcher> _matcher;
+
+      private:
+        friend Impl * rwcowClone<Impl>( const Impl * rhs );
+        /** clone for RWCOW_pointer */
+        Impl * clone() const
+        { return new Impl( _search, _flags ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates AttrMatcher::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const AttrMatcher::Impl & obj )
+    {
+      return str << "\"" << obj.searchstring() << "\"{" << obj.flags() << "}";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    AttrMatcher::AttrMatcher()
+    : _pimpl( new Impl )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r )
+    : _pimpl( new Impl( search_r, Match::STRING ) )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r, const Match & flags_r )
+    : _pimpl( new Impl( search_r, flags_r ) )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r, const Match::Mode & flags_r )
+    : _pimpl( new Impl( search_r, flags_r ) )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r, int flags_r )
+    : _pimpl( new Impl( search_r, Match(flags_r) ) )
+    {}
+
+    void AttrMatcher::compile() const
+    { return _pimpl->compile(); }
+
+    bool AttrMatcher::isCompiled() const
+    { return _pimpl->isCompiled(); }
+
+    bool AttrMatcher::doMatch( const char * string_r ) const
+    { return _pimpl->doMatch( string_r ); }
+
+    const std::string & AttrMatcher::searchstring() const
+    { return _pimpl->searchstring(); }
+
+    void AttrMatcher::setSearchstring( const std::string & string_r )
+    { _pimpl->setSearchstring( string_r ); }
+
+    void AttrMatcher::setSearchstring( const std::string & string_r, const Match & flags_r )
+    {
+      _pimpl->setSearchstring( string_r );
+      _pimpl->setFlags( flags_r );
+    }
+
+    const Match & AttrMatcher::flags() const
+    { return _pimpl->flags(); }
+
+    void AttrMatcher::setFlags( const Match & flags_r )
+    { _pimpl->setFlags( flags_r ); }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj )
+    {
+      return str << *obj._pimpl;
+    }
+
+    bool operator==( const AttrMatcher & lhs, const AttrMatcher & rhs )
+    {
+      return ( lhs.flags() == rhs.flags()
+               && lhs.searchstring() == rhs.searchstring() );
+    }
+
+    bool operator<( const AttrMatcher & lhs, const AttrMatcher & rhs )
+    {
+      if ( lhs.flags().get() != rhs.flags().get() )
+        return ( lhs.flags().get() < rhs.flags().get() );
+
+      return ( lhs.searchstring() < rhs.searchstring() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/AttrMatcher.h b/zypp/sat/AttrMatcher.h
new file mode 100644 (file)
index 0000000..e557b07
--- /dev/null
@@ -0,0 +1,424 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/AttrMatcher.h
+ *
+*/
+#ifndef ZYPP_SAT_ATTRMATCHER_H
+#define ZYPP_SAT_ATTRMATCHER_H
+
+extern "C"
+{
+struct _Datamatcher;
+}
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/SafeBool.h"
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Match
+  //
+  /** String matching option flags as used e.g. by \ref sat::AttrMatcher.
+   *
+   * \code
+   * Match mode( Match::GLOB | Match::NOCASE );
+   * \endcode
+   */
+  class Match : private base::SafeBool<Match>
+  {
+    private:
+      static const int _modemask;
+      static const int _flagmask;
+
+    public:
+      /** Mode flags (mutual exclusive). */
+      enum Mode
+      {
+        NOTHING,       //!< Match nothing
+        STRING,                //!< Excat matching
+        STRINGSTART,   //!< Match at string start
+        STRINGEND,     //!< Match at string end
+        SUBSTRING,     //!< Match substring
+        GLOB,          //!< Glob
+        REGEX,         //!< Regular Expression
+        OTHER          //!< Something else.
+      };
+
+      /** \name Option flags
+       * Some flags are actually \ref sat::LookupAttr specific, as they tell
+       * how to retrieve the attribute values. The plain \ref sat::AttrMatcher
+       * will ignore those flags and use the ones related to string matching only
+       * (like \ref NOCASE).
+       */
+      //@{
+      /** If set, match case insensitive. */
+      static const Match NOCASE;
+      /** internal */
+      static const Match NO_STORAGE_SOLVABLE;
+      /** internal */
+      static const Match SUB;
+      /** internal */
+      static const Match ARRAYSENTINEL;
+      /** If set, skip any \c kind: prefix when looking at a \ref Solvable name. */
+      static const Match SKIP_KIND;
+      /**  If set, match full path when matching in filelists, otherwise just the basenames. */
+      static const Match FILES;
+      //@}
+
+    public:
+      /** Default ctor \c 0 or \ref NOTHING. */
+      Match()
+      : _val( 0 )
+      {}
+
+      /** Ctor from \ref Mode value. */
+      Match( Mode val_r )
+      : _val( modeval( val_r ) )
+      {}
+
+      /** Just in case one needs it. */
+      explicit Match( int val_r )
+      : _val( val_r )
+      {}
+
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( != 0 )</tt>. */
+      using base::SafeBool<Match>::operator bool_type;
+#endif
+
+    public:
+      /** Test whether \c all of the \a rhs bits are set (same mode if \a rhs has one). */
+      bool test( const Match & rhs ) const
+      { return ( ( flagval() & rhs.flagval() ) == rhs.flagval() )
+          && ( !rhs.modeval() || rhs.modeval() == modeval() ); }
+
+      /** Whether at least one of the \a rhs bits is set (or the same mode). */
+      bool testAnyOf( const Match & rhs ) const
+      { return ( flagval() & rhs.flagval() )
+          || ( rhs.modeval() && rhs.modeval() == modeval() ); }
+
+      /** Set all of the \a rhs bits (setting a new mode if \a rhs has one). */
+      void set( const Match & rhs )
+      {
+        if ( rhs.modeval() )
+          _val = rhs._val | flagval(); // also set the rhs mode
+        else
+          _val |= rhs._val; // just set the flags
+      }
+
+      /** Unset all of the \a rhs bits (unsets mode if the same as \a rhs). */
+      void unset( const Match & rhs )
+      {
+        if ( modeval() == rhs.modeval() )
+          _val = flagval() & ~rhs.flagval(); // also unset mode
+        else
+          _val &= ~rhs.flagval(); // just unset falgs
+      }
+
+      /** Depending on the value of \a onoff, set or unset flags. */
+      void turn( const Match & rhs, bool onoff )
+      { onoff ? set( rhs ) : unset( rhs ); }
+
+      /** Add flags. */
+      Match & operator|=( const Match & rhs )
+      { set( rhs ); return *this; }
+
+      /** Remove flags.*/
+      Match & operator-=( const Match & rhs )
+      { unset( rhs ); return *this; }
+
+    public:
+      /** Return the \c mode part. */
+      Mode mode() const;
+
+      /** Return the \c flags part. */
+      Match flags() const
+      { return Match( flagval() ); }
+
+    public:
+      /** \name Low level integer representation. */
+      //@{
+      /** Return the integer representation. */
+      int get() const          { return _val; }
+      /** Return the modes integer representation. */
+      int modeval() const      { return _val & _modemask; }
+      /** Return the flags integer representation. */
+      int flagval() const      { return _val & _flagmask; }
+      //@}
+
+    public:
+      /** \name Mode flag manip/query convenience. */
+      //@{
+      /** Whether this has mode \a rhs */
+      bool isMode( Mode rhs ) const
+      { return modeval() == modeval( rhs ); }
+      /** Whether this has mode \ref STRING. */
+      bool isModeString() const
+      { return isMode( STRING ); }
+      /** Whether this has mode \ref STRINGSTART. */
+      bool isModeStringstart() const
+      { return isMode( STRINGSTART ); }
+      /** Whether this has mode \ref STRINGEND. */
+      bool isModeStringend() const
+      { return isMode( STRINGEND ); }
+      /** Whether this has mode \ref SUBSTRING. */
+      bool isModeSubstring() const
+      { return isMode( SUBSTRING ); }
+      /** Whether this has mode \ref GLOB. */
+      bool isModeGlob() const
+      { return isMode( GLOB ); }
+      /** Whether this has mode \ref REGEX. */
+      bool isModeRegex() const
+      { return isMode( REGEX ); }
+
+      /** Set the mode part to \a rhs . */
+      void setMode( Mode rhs )
+      { _val = modeval( rhs ) | flagval(); }
+      /** Set the mode \ref STRING. */
+      void setModeString()
+      { setMode( STRING ); }
+      /** Set the mode \ref STRINGSTART. */
+      void setModeStringstart()
+      { setMode( STRINGSTART ); }
+      /** Set the mode \ref STRINGEND. */
+      void setModeStringend()
+      { setMode( STRINGEND ); }
+      /** Set the mode \ref SUBSTRING. */
+      void setModeSubstring()
+      { setMode( SUBSTRING ); }
+      /** Set the mode \ref GLOB. */
+      void setModeGlob()
+      { setMode( GLOB ); }
+      /** Set the mode \ref REGEX. */
+      void setModeRegex()
+      { setMode( REGEX ); }
+      //@}
+
+      /** String representation. */
+      std::string asString() const;
+
+    private:
+      friend base::SafeBool<Match>::operator bool_type() const;
+      bool boolTest() const    { return _val; }
+
+      /** Numeric value for enum (short for <tt>Match(m).get()</tt>). */
+      static int modeval( Mode mode_r );
+
+    private:
+      int _val;
+  };
+
+  /** \relates Match */
+  inline bool operator==( const Match & lhs, const Match & rhs )
+  { return lhs.get() == rhs.get(); }
+  /** \relates Match */
+  inline bool operator!=( const Match & lhs, const Match & rhs )
+  { return lhs.get() != rhs.get(); }
+
+  /** \relates Match */
+  inline Match operator|( const Match & lhs, const Match & rhs )
+  { return Match(lhs) |= rhs; }
+  /** \relates Match \overload to disambiguate 'int|int'. */
+  inline Match operator|( Match::Mode lhs, Match::Mode rhs )
+  { return Match(lhs) |= rhs; }
+
+  /** \relates Match */
+  inline Match operator-( const Match & lhs, const Match & rhs )
+  { return Match(lhs) -= rhs; }
+  /** \relates Match \overload to disambiguate 'int-int'. */
+  inline Match operator-( Match::Mode lhs, Match::Mode rhs )
+  { return Match(lhs) -= rhs; }
+
+  /** \relates Match::Mode Stream output */
+  std::ostream & operator<<( std::ostream & str, Match::Mode obj );
+
+  /** \relates Match Stream output */
+  std::ostream & operator<<( std::ostream & str, const Match & obj );
+
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : MatchException
+  //
+  /** Exceptions thrown from attribute matching. */
+  struct MatchException : public Exception
+  {
+    /** Supplied message. */
+    explicit MatchException( const std::string & msg_r ) : Exception( msg_r ) {}
+  };
+
+  /** Unknown match mode. */
+  struct MatchUnknownModeException : public MatchException
+  {
+    /** Supplied message. */
+    explicit MatchUnknownModeException( const std::string & msg_r ) : MatchException( msg_r ) {}
+
+    /** Build message including the \a mode and optional the pattern string. */
+    MatchUnknownModeException( const Match & mode_r, const std::string & msg_r = std::string() );
+  };
+
+  /** Invalid regular expression (failed ::regcomp). */
+  struct MatchInvalidRegexException : public MatchException
+  {
+    /** Supplied message. */
+    explicit MatchInvalidRegexException( const std::string & msg_r ) : MatchException( msg_r ) {}
+
+    /** Build message including the \a regex and \c ::regcomp returncode (use \c 0 if unknown). */
+    MatchInvalidRegexException( const std::string & regex_r, int regcomp_r );
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher
+    //
+    /** String matching (STRING|SUBSTRING|GLOB|REGEX).
+     *
+     * Used by e.g. \ref PoolQuery and \ref LookupAttr for queries,
+     * but it can also be used for matching arbitrary strings.
+     *
+     * \code
+     *  AttrMatcher matches( "foo" );
+     *  for_( it, stringlist.begin(), stringlist().end() )
+     *  {
+     *    if ( matches( *it ) )
+     *      cout << *it << " has substring 'foo'" << endl;
+     *  }
+     * \endcode
+     *
+     * \Note Those flags are always set: <tt>REG_EXTENDED | REG_NOSUB | REG_NEWLINE</tt>
+     */
+    class AttrMatcher : private base::SafeBool<AttrMatcher>
+    {
+      friend std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj );
+
+      public:
+        typedef MatchException Exception;
+
+      public:
+        /** Implementation  */
+        class Impl;
+
+      public:
+        /** Default ctor matches nothing. */
+        AttrMatcher();
+
+        /** Ctor from string matches in \ref Match::STRING mode per default. */
+        AttrMatcher( const std::string & search_r );
+
+        /** Ctor taking string and \ref Match flags. */
+        AttrMatcher( const std::string & search_r, const Match & flags_r );
+
+        /** Ctor taking string and \ref Match::Mode.
+         * Needed because we want them to be treated as \ref Match,
+         * and not as \ref int as the compiler woud do.
+        */
+        AttrMatcher( const std::string & search_r, const Match::Mode & flags_r );
+
+        /** Low level interface wraps \a flags into \ref Match. */
+        AttrMatcher( const std::string & search_r, int flags_r );
+
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( ! searchstring().empty() )</tt>. */
+      using base::SafeBool<AttrMatcher>::operator bool_type;
+#endif
+
+      public:
+        /** Return whether string matches.
+         * You can use it with any class that impements \c c_str.
+         * (\c std::string, \ref Pathname, \ref IdString, ...).
+         * \Note \c NULL never matches.
+         */
+        template<class _Tp>
+            bool operator()( const _Tp & string_r ) const
+        { return doMatch( string_r.c_str() ); }
+        /** \overload */
+        bool operator()( const char * string_r ) const
+        { return doMatch( string_r ); }
+
+      public:
+        /** The current searchstring. */
+        const std::string & searchstring() const;
+
+        /** Set a new searchstring. */
+        void setSearchstring( const std::string & string_r );
+
+        /** Set a new searchstring and flags. */
+        void setSearchstring( const std::string & string_r, const Match & flags_r );
+
+        /** The current search flags. */
+        const Match & flags() const;
+
+        /** Set new search flags. */
+        void setFlags( const Match & flags_r );
+
+      public:
+        /** Compile the pattern e.g. in case of \c REGEX.
+         * \throws MatchUnknownModeException If the \ref Match flag more than
+         *         one mode bit set.
+         * \throws MatchInvalidRegexException If \ref Match::REGEX is set
+         *         and \ref searchstring is not a valid regular expression.
+         */
+        void compile() const;
+
+        /** Whether the \ref AttrMatcher is already compiled. */
+        bool isCompiled() const;
+
+        /** Return whether string matches.
+         * Compiles the \ref AttrMatcher if this was not yet done.
+         * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+         */
+        bool doMatch( const char * string_r ) const;
+
+      private:
+        friend base::SafeBool<AttrMatcher>::operator bool_type() const;
+        bool boolTest() const
+        { return !searchstring().empty(); }
+
+      private:
+        /** Pointer to implementation */
+        RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates AttrMatcher Stream output */
+    std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj );
+
+    /** \relates AttrMatcher */
+    bool operator==( const AttrMatcher & lhs, const AttrMatcher & rhs );
+
+    /** \relates AttrMatcher */
+    inline bool operator!=( const AttrMatcher & lhs, const AttrMatcher & rhs )
+    { return !( lhs == rhs ); }
+
+    /** \relates AttrMatcher Arbitrary order for std::container. */
+    bool operator<( const AttrMatcher & lhs, const AttrMatcher & rhs );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_ATTRMATCHER_H
diff --git a/zypp/sat/LocaleSupport.cc b/zypp/sat/LocaleSupport.cc
new file mode 100644 (file)
index 0000000..c5cab3b
--- /dev/null
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/LocaleSupport.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/sat/LocaleSupport.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const LocaleSupport & obj )
+    {
+      return str << obj.locale() << '(' << (obj.isAvailable()?'a':'_') << (obj.isRequested()?'R':'_') << ')';
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : dumpOn
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & dumpOn( std::ostream & str, const LocaleSupport & obj )
+    {
+      return dumpRange( str << obj, obj.begin(), obj.end() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/LocaleSupport.h b/zypp/sat/LocaleSupport.h
new file mode 100644 (file)
index 0000000..af85f4d
--- /dev/null
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/LocaleSupport.h
+ *
+*/
+#ifndef ZYPP_SAT_LOCALESUPPORT_H
+#define ZYPP_SAT_LOCALESUPPORT_H
+
+#include <iosfwd>
+
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/Locale.h"
+#include "zypp/Filter.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LocaleSupport
+    //
+    /** Convenience methods to manage support for a specific \ref Locale.
+     *
+     * \code
+     *   sat::LocaleSupport myLocale( Locale("de") );
+     *
+     *   if ( myLocale.isAvailable() )
+     *   {
+     *     MIL << "Support for locale '" << myLocale.locale() << "' is available." << endl;
+     *   }
+     *   if ( ! myLocale.isRequested() )
+     *   {
+     *     MIL << "Will enable support for locale '" << myLocale.locale() << "'." << endl;
+     *     myLocale.setRequested( true );
+     *   }
+     *   MIL << "Packages supporting locale '" << myLocale.locale() << "':" << endl;
+     *   for_( it, myLocale.begin(), myLocale.end() )
+     *   {
+     *     // iterate over sat::Solvables
+     *     MIL << "  " << *it << endl;
+     *     // or get the PoolItems
+     *     DBG << "  " << PoolItem(*it) << endl;
+     *   }
+     * \endcode
+     *
+     * \todo If iterator is too slow install a proxy watching the Pool::serial.
+     */
+    class LocaleSupport : public SolvIterMixin<LocaleSupport,filter_iterator<filter::ByLocaleSupport,Pool::SolvableIterator> >
+                        , protected detail::PoolMember
+    {
+      public:
+        /** Default ctor */
+        LocaleSupport()
+        {}
+        /** Ctor taking a \ref Locale. */
+        LocaleSupport( const Locale & locale_r )
+        :  _locale( locale_r )
+        {}
+
+      public:
+        /** My \ref Locale */
+        const Locale & locale() const
+        { return _locale; }
+
+        /** Whether there are language specific packages supporting my \ref Locale. */
+        bool isAvailable() const
+        { return Pool(*this).isAvailableLocale( _locale ); }
+
+        /** Whether the solver will automatically select language specific packages for my \ref Locale. */
+        bool isRequested() const
+        { return Pool(*this).isRequestedLocale( _locale ); }
+
+        /** Turn on/off solver support for my \ref Locale.*/
+        void setRequested( bool yesno_r )
+        { yesno_r ? Pool(*this).addRequestedLocale( _locale ) : Pool(*this).eraseRequestedLocale( _locale ); }
+
+      public:
+        /** \name Iterate through all \ref sat::Solvables supporting my \ref Locale. */
+        //@{
+        typedef Solvable_iterator iterator;  // from SolvIterMixin
+
+        iterator begin() const
+        { return Pool(*this).filterBegin( filter::ByLocaleSupport( _locale ) ); }
+
+        iterator end() const
+        { return Pool(*this).filterEnd( filter::ByLocaleSupport( _locale ) ); }
+        //@}
+
+      private:
+        Locale _locale;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates LocaleSupport Stream output */
+    std::ostream & operator<<( std::ostream & str, const LocaleSupport & obj );
+
+    /** \relates LocaleSupport More verbose stream output including dependencies */
+    std::ostream & dumpOn( std::ostream & str, const LocaleSupport & obj );
+
+    /** \relates LocaleSupport */
+    inline bool operator==( const LocaleSupport & lhs, const LocaleSupport & rhs )
+    { return lhs.locale() == rhs.locale(); }
+
+    /** \relates LocaleSupport */
+    inline bool operator!=( const LocaleSupport & lhs, const LocaleSupport & rhs )
+    { return lhs.locale() != rhs.locale(); }
+
+    /** \relates LocaleSupport */
+    inline bool operator<( const LocaleSupport & lhs, const LocaleSupport & rhs )
+    { return lhs.locale() < rhs.locale(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_LOCALESUPPORT_H
diff --git a/zypp/sat/LookupAttr.cc b/zypp/sat/LookupAttr.cc
new file mode 100644 (file)
index 0000000..1cd3ff9
--- /dev/null
@@ -0,0 +1,791 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/LookupAttr.cc
+ *
+*/
+#include <iostream>
+#include <sstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/AttrMatcher.h"
+
+#include "zypp/CheckSum.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    using detail::noSolvableId;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr::Impl
+    //
+    ///////////////////////////////////////////////////////////////////
+    /**
+     * LookupAttr implememtation.
+     *
+     * Repository and Solvable must not be set at the same time!
+     *
+     * \note When looking in pool or repo, \ref Solvable \c _solv is
+     * somewhat abused to store eiter \c Id \c 0 or \c SOLVID_META, which
+     * indicates whether the dataiterator should look into solvable or
+     * repository metadata. Remember that all \ref Solvables with an
+     * \e invalid \c Id, are treated as <tt>== Solvable::noSolvable</tt>,
+     * and in a boolean context evaluate to \c false. Thus \c noSolvable
+     * may have different \c Ids.
+     */
+    class LookupAttr::Impl
+    {
+      public:
+        Impl()
+        : _parent( SolvAttr::noAttr )
+        {}
+        Impl( SolvAttr attr_r, Location loc_r )
+        : _attr( attr_r ), _parent( attr_r.parent() ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
+        {}
+        Impl( SolvAttr attr_r, Repository repo_r, Location loc_r )
+        : _attr( attr_r ), _parent( attr_r.parent() ), _repo( repo_r ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
+        {}
+        Impl( SolvAttr attr_r, Solvable solv_r )
+        : _attr( attr_r ), _parent( attr_r.parent() ), _solv( solv_r )
+        {}
+
+      public:
+        SolvAttr attr() const
+        { return _attr; }
+
+        void setAttr( SolvAttr attr_r )
+        {
+          _attr = attr_r;
+          SolvAttr p( _attr.parent() );
+          if ( p != SolvAttr::noAttr )
+            _parent = p;
+        }
+
+        const AttrMatcher & attrMatcher() const
+        { return _attrMatcher; }
+
+        void setAttrMatcher( const AttrMatcher & matcher_r )
+        {
+          matcher_r.compile();
+          _attrMatcher = matcher_r;
+        }
+
+      public:
+        bool pool() const
+        { return ! (_repo || _solv); }
+
+        void setPool( Location loc_r )
+        {
+          _repo = Repository::noRepository;
+          _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
+        }
+
+        Repository repo() const
+        { return _repo; }
+
+        void setRepo( Repository repo_r, Location loc_r  )
+        {
+          _repo = repo_r;
+          _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
+        }
+
+        Solvable solvable() const
+        { return _solv; }
+
+        void setSolvable( Solvable solv_r )
+        {
+          _repo = Repository::noRepository;
+          _solv = solv_r;
+        }
+
+        SolvAttr parent() const
+        { return _parent; }
+
+        void setParent( SolvAttr attr_r )
+        { _parent = attr_r; }
+
+      public:
+        LookupAttr::iterator begin() const
+        {
+          if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() )
+            return end();
+
+          detail::RepoIdType whichRepo = detail::noRepoId; // all repos
+          if ( _solv )
+            whichRepo = _solv.repository().id();
+          else if ( _repo )
+            whichRepo = _repo.id();
+
+          detail::DIWrap dip( whichRepo, _solv.id(), _attr.id(), _attrMatcher.searchstring(), _attrMatcher.flags().get() );
+          if ( _parent != SolvAttr::noAttr )
+            ::dataiterator_prepend_keyname( dip.get(), _parent.id() );
+
+          return iterator( dip ); // iterator takes over ownership!
+        }
+
+        LookupAttr::iterator end() const
+        { return iterator(); }
+
+      private:
+        SolvAttr   _attr;
+        SolvAttr   _parent;
+        Repository _repo;
+        Solvable   _solv;
+        AttrMatcher _attrMatcher;
+
+      private:
+        friend Impl * rwcowClone<Impl>( const Impl * rhs );
+        /** clone for RWCOW_pointer */
+        Impl * clone() const
+        { return new Impl( *this ); }
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    LookupAttr::LookupAttr()
+      : _pimpl( new Impl )
+    {}
+
+    LookupAttr::LookupAttr( SolvAttr attr_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, loc_r ) )
+    {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, loc_r ) )
+    { _pimpl->setParent( parent_r ); }
+
+    LookupAttr::LookupAttr( SolvAttr attr_r, Repository repo_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
+    {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Repository repo_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
+    { _pimpl->setParent( parent_r ); }
+
+    LookupAttr::LookupAttr( SolvAttr attr_r, Solvable solv_r )
+      : _pimpl( new Impl( attr_r, solv_r ) )
+    {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Solvable solv_r )
+      : _pimpl( new Impl( attr_r, solv_r ) )
+    { _pimpl->setParent( parent_r ); }
+
+
+    ///////////////////////////////////////////////////////////////////
+
+    SolvAttr LookupAttr::attr() const
+    { return _pimpl->attr(); }
+
+    void LookupAttr::setAttr( SolvAttr attr_r )
+    { _pimpl->setAttr( attr_r ); }
+
+    const AttrMatcher & LookupAttr::attrMatcher() const
+    { return _pimpl->attrMatcher(); }
+
+    void LookupAttr::setAttrMatcher( const AttrMatcher & matcher_r )
+    { _pimpl->setAttrMatcher( matcher_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    bool LookupAttr::pool() const
+    { return _pimpl->pool(); }
+
+    void LookupAttr::setPool( Location loc_r )
+    { _pimpl->setPool( loc_r ); }
+
+    Repository LookupAttr::repo() const
+    { return _pimpl->repo(); }
+
+    void LookupAttr::setRepo( Repository repo_r, Location loc_r )
+    { _pimpl->setRepo( repo_r, loc_r ); }
+
+    Solvable LookupAttr::solvable() const
+    { return _pimpl->solvable(); }
+
+    void LookupAttr::setSolvable( Solvable solv_r )
+    { _pimpl->setSolvable( solv_r ); }
+
+    SolvAttr LookupAttr::parent() const
+    { return _pimpl->parent(); }
+
+    void LookupAttr::setParent( SolvAttr attr_r )
+    { _pimpl->setParent( attr_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    LookupAttr::iterator LookupAttr::begin() const
+    { return _pimpl->begin(); }
+
+    LookupAttr::iterator LookupAttr::end() const
+    { return _pimpl->end(); }
+
+    bool LookupAttr::empty() const
+    { return begin() == end(); }
+
+    LookupAttr::size_type LookupAttr::size() const
+    {
+      size_type c = 0;
+      for_( it, begin(), end() )
+        ++c;
+      return c;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    std::ostream & operator<<( std::ostream & str, const LookupAttr & obj )
+    {
+      if ( obj.attr() == SolvAttr::noAttr )
+        return str << "search nothing";
+
+      if ( obj.attr() )
+        str << "seach " << obj.attr() << " in ";
+      else
+        str << "seach ALL in ";
+
+      if ( obj.solvable() )
+        return str << obj.solvable();
+      if ( obj.repo() )
+        return str << obj.repo();
+      return str << "pool";
+    }
+
+    std::ostream & dumpOn( std::ostream & str, const LookupAttr & obj )
+    {
+      return dumpRange( str << obj, obj.begin(), obj.end() );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupRepoAttr
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    LookupRepoAttr::LookupRepoAttr( SolvAttr attr_r, Repository repo_r )
+      : LookupAttr( attr_r, repo_r, REPO_ATTR )
+    {}
+
+    void LookupRepoAttr::setRepo( Repository repo_r )
+    { LookupAttr::setRepo( repo_r, REPO_ATTR ); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  CLASS NAME : detail::DIWrap
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                      const std::string & mstring_r, int flags_r )
+      : _dip( new ::Dataiterator )
+      , _mstring( mstring_r )
+      {
+        ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
+                             _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
+      }
+
+      DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                      const char * mstring_r, int flags_r )
+      : _dip( new ::Dataiterator )
+      , _mstring( mstring_r ? mstring_r : "" )
+      {
+        ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
+                             _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
+      }
+
+      DIWrap::DIWrap( const DIWrap & rhs )
+        : _dip( 0 )
+        , _mstring( rhs._mstring )
+      {
+        if ( rhs._dip )
+        {
+          _dip = new ::Dataiterator;
+          ::dataiterator_init_clone( _dip, rhs._dip );
+        }
+      }
+
+      DIWrap::~DIWrap()
+      {
+        if ( _dip )
+        {
+          ::dataiterator_free( _dip );
+          delete _dip;
+        }
+      }
+
+      std::ostream & operator<<( std::ostream & str, const DIWrap & obj )
+      { return str << obj.get(); }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr::iterator
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    // position and moving
+    ///////////////////////////////////////////////////////////////////
+
+    Repository LookupAttr::iterator::inRepo() const
+    { return _dip ? Repository( _dip->repo ) : Repository::noRepository; }
+
+    Solvable LookupAttr::iterator::inSolvable() const
+    { return _dip ? Solvable( _dip->solvid ) : Solvable::noSolvable; }
+
+    SolvAttr LookupAttr::iterator::inSolvAttr() const
+    { return _dip ? SolvAttr( _dip->key->name ) : SolvAttr::noAttr; }
+
+    void LookupAttr::iterator::nextSkipSolvAttr()
+    { if ( _dip ) ::dataiterator_skip_attribute( _dip.get() ); }
+
+    void LookupAttr::iterator::nextSkipSolvable()
+    { if ( _dip ) ::dataiterator_skip_solvable( _dip.get() ); }
+
+    void LookupAttr::iterator::nextSkipRepo()
+    { if ( _dip ) ::dataiterator_skip_repo( _dip.get() ); }
+
+    void LookupAttr::iterator::stayInThisSolvable()
+    { if ( _dip ) { _dip.get()->repoid = -1; _dip.get()->flags |= SEARCH_THISSOLVID; } }
+
+    void LookupAttr::iterator::stayInThisRepo()
+    { if ( _dip ) { _dip.get()->repoid = -1; } }
+
+    ///////////////////////////////////////////////////////////////////
+    // attr value type test
+    ///////////////////////////////////////////////////////////////////
+
+    detail::IdType LookupAttr::iterator::solvAttrType() const
+    { return _dip ? _dip->key->type : detail::noId; }
+
+    bool LookupAttr::iterator::solvAttrNumeric() const
+    {
+      switch ( solvAttrType() )
+      {
+        case REPOKEY_TYPE_U32:
+        case REPOKEY_TYPE_NUM:
+        case REPOKEY_TYPE_CONSTANT:
+          return true;
+          break;
+      }
+      return false;
+    }
+
+    bool LookupAttr::iterator::solvAttrString() const
+    {
+      switch ( solvAttrType() )
+      {
+        case REPOKEY_TYPE_ID:
+        case REPOKEY_TYPE_IDARRAY:
+        case REPOKEY_TYPE_CONSTANTID:
+        case REPOKEY_TYPE_STR:
+        case REPOKEY_TYPE_DIRSTRARRAY:
+          return true;
+          break;
+      }
+      return false;
+    }
+
+    bool LookupAttr::iterator::solvAttrIdString() const
+    {
+      switch ( solvAttrType() )
+      {
+        case REPOKEY_TYPE_ID:
+        case REPOKEY_TYPE_IDARRAY:
+        case REPOKEY_TYPE_CONSTANTID:
+          return true;
+          break;
+      }
+      return false;
+    }
+
+    bool LookupAttr::iterator::solvAttrCheckSum() const
+    {
+      switch ( solvAttrType() )
+      {
+        case REPOKEY_TYPE_MD5:
+        case REPOKEY_TYPE_SHA1:
+        case REPOKEY_TYPE_SHA256:
+          return true;
+          break;
+      }
+      return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    {
+      enum SubType { ST_NONE,  // no sub-structure
+                     ST_FLEX,  // flexarray
+                     ST_SUB }; // inside sub-structure
+      SubType subType( const detail::DIWrap & dip )
+      {
+        if ( ! dip )
+          return ST_NONE;
+        if ( dip.get()->key->type == REPOKEY_TYPE_FLEXARRAY )
+          return ST_FLEX;
+        return dip.get()->kv.parent ? ST_SUB : ST_NONE;
+      }
+    }
+    ///////////////////////////////////////////////////////////////////
+
+    bool LookupAttr::iterator::solvAttrSubEntry() const
+    { return subType( _dip ) != ST_NONE; }
+
+    ///////////////////////////////////////////////////////////////////
+    // Iterate sub-structures.
+    ///////////////////////////////////////////////////////////////////
+
+    bool LookupAttr::iterator::subEmpty() const
+    { return( subBegin() == subEnd() ); }
+
+    LookupAttr::size_type LookupAttr::iterator::subSize() const
+    {
+      size_type c = 0;
+      for_( it, subBegin(), subEnd() )
+        ++c;
+      return c;
+    }
+
+    LookupAttr::iterator LookupAttr::iterator::subBegin() const
+    {
+      SubType subtype( subType( _dip ) );
+      if ( subtype == ST_NONE )
+        return subEnd();
+      // setup the new sub iterator with the remembered position
+      detail::DIWrap dip( 0, 0, 0 );
+      ::dataiterator_clonepos( dip.get(), _dip.get() );
+      switch ( subtype )
+      {
+        case ST_NONE:  // not reached
+          break;
+        case ST_FLEX:
+          ::dataiterator_seek( dip.get(), DI_SEEK_CHILD|DI_SEEK_STAY );
+          break;
+        case ST_SUB:
+          ::dataiterator_seek( dip.get(), DI_SEEK_REWIND|DI_SEEK_STAY );
+          break;
+      }
+      return iterator( dip ); // iterator takes over ownership!
+    }
+
+    LookupAttr::iterator LookupAttr::iterator::subEnd() const
+    {
+      return iterator();
+    }
+
+    LookupAttr::iterator LookupAttr::iterator::subFind( SolvAttr attr_r ) const
+    {
+      iterator it = subBegin();
+      if ( attr_r != sat::SolvAttr::allAttr )
+      {
+        while ( it != subEnd() && it.inSolvAttr() != attr_r )
+          ++it;
+      }
+      return it;
+    }
+
+    LookupAttr::iterator LookupAttr::iterator::subFind( const C_Str & attrname_r ) const
+    {
+      if ( attrname_r.empty() )
+        return subBegin();
+
+      SubType subtype( subType( _dip ) );
+      if ( subtype == ST_NONE )
+        return subBegin();
+
+      std::string subattr( inSolvAttr().asString() );
+      if ( subtype == ST_FLEX )
+      {
+        // append ":attrname"
+        subattr += ":";
+        subattr += attrname_r;
+      }
+      else
+      {
+        // replace "oldname" after ':' with "attrname"
+        std::string::size_type pos( subattr.rfind( ':' ) );
+        if ( pos != std::string::npos )
+        {
+          subattr.erase( pos+1 );
+          subattr += attrname_r;
+        }
+        else
+          subattr = attrname_r; // no ':' so replace all.
+      }
+      return subFind( SolvAttr( subattr ) );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // attr value retrieval
+    ///////////////////////////////////////////////////////////////////
+
+    int LookupAttr::iterator::asInt() const
+    {
+      if ( _dip )
+      {
+        switch ( solvAttrType() )
+        {
+          case REPOKEY_TYPE_U32:
+          case REPOKEY_TYPE_NUM:
+          case REPOKEY_TYPE_CONSTANT:
+            return _dip->kv.num;
+            break;
+        }
+      }
+      return 0;
+    }
+
+    unsigned LookupAttr::iterator::asUnsigned() const
+    { return asInt(); }
+
+    bool LookupAttr::iterator::asBool() const
+    { return asInt(); }
+
+
+    const char * LookupAttr::iterator::c_str() const
+    {
+      if ( _dip )
+      {
+        switch ( solvAttrType() )
+        {
+          case REPOKEY_TYPE_ID:
+          case REPOKEY_TYPE_IDARRAY:
+          case REPOKEY_TYPE_CONSTANTID:
+            if ( _dip->data && _dip->data->localpool )
+              return ::stringpool_id2str( &_dip->data->spool, _dip->kv.id ); // in local pool
+            else
+              return IdString( _dip->kv.id ).c_str(); // in global pool
+            break;
+
+          case REPOKEY_TYPE_STR:
+            return _dip->kv.str;
+            break;
+
+          case REPOKEY_TYPE_DIRSTRARRAY:
+            return ::repodata_dir2str( _dip->data, _dip->kv.id, _dip->kv.str );
+            break;
+        }
+      }
+      return 0;
+    }
+
+    std::string LookupAttr::iterator::asString() const
+    {
+      if ( _dip )
+      {
+        switch ( solvAttrType() )
+        {
+          case REPOKEY_TYPE_ID:
+          case REPOKEY_TYPE_IDARRAY:
+          case REPOKEY_TYPE_CONSTANTID:
+            {
+              detail::IdType id = ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 );
+              return ISRELDEP(id) ? Capability( id ).asString()
+                                  : IdString( id ).asString();
+            }
+            break;
+
+          case REPOKEY_TYPE_STR:
+          case REPOKEY_TYPE_DIRSTRARRAY:
+            {
+              const char * ret( c_str() );
+              return ret ? ret : "";
+            }
+            break;
+
+          case REPOKEY_TYPE_U32:
+          case REPOKEY_TYPE_NUM:
+          case REPOKEY_TYPE_CONSTANT:
+            return str::numstring( asInt() );
+            break;
+
+          case REPOKEY_TYPE_MD5:
+          case REPOKEY_TYPE_SHA1:
+          case REPOKEY_TYPE_SHA256:
+            {
+              return asCheckSum().asString();
+            }
+            break;
+
+          case REPOKEY_TYPE_FLEXARRAY:
+            {
+              std::ostringstream str;
+              str << "{" << endl;
+              for_( it, subBegin(), subEnd() )
+              {
+                str << "  " << it.inSolvAttr() << " = " << it.asString() << endl;
+              }
+              str << "}";
+             return str.str();
+            }
+            break;
+        }
+      }
+      return std::string();
+    }
+
+    IdString LookupAttr::iterator::idStr() const
+    {
+      if ( _dip )
+      {
+        switch ( solvAttrType() )
+        {
+          case REPOKEY_TYPE_ID:
+          case REPOKEY_TYPE_IDARRAY:
+          case REPOKEY_TYPE_CONSTANTID:
+            return IdString( ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 ) );
+            break;
+        }
+      }
+      return IdString();
+    }
+
+    CheckSum LookupAttr::iterator::asCheckSum() const
+    {
+      if ( _dip )
+      {
+        switch ( solvAttrType() )
+        {
+          case REPOKEY_TYPE_MD5:
+            return CheckSum::md5( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
+            break;
+
+          case REPOKEY_TYPE_SHA1:
+            return CheckSum::sha1( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
+            break;
+
+          case REPOKEY_TYPE_SHA256:
+            return CheckSum::sha256( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
+            break;
+        }
+      }
+      return CheckSum();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // internal stuff below
+    ///////////////////////////////////////////////////////////////////
+
+    LookupAttr::iterator::iterator()
+    : iterator_adaptor_( 0 )
+    {}
+
+    LookupAttr::iterator::iterator( const iterator & rhs )
+    : iterator_adaptor_( 0 )
+    , _dip( rhs._dip )
+    {
+      base_reference() = _dip.get();
+    }
+
+    LookupAttr::iterator::iterator( detail::DIWrap & dip_r )
+    : iterator_adaptor_( 0 )
+    {
+      _dip.swap( dip_r ); // take ownership!
+      base_reference() = _dip.get();
+      increment();
+    }
+
+    LookupAttr::iterator::~iterator()
+    {}
+
+    LookupAttr::iterator & LookupAttr::iterator::operator=( const iterator & rhs )
+    {
+      if ( &rhs != this )
+      {
+        _dip = rhs._dip;
+        base_reference() = _dip.get();
+      }
+      return *this;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    bool LookupAttr::iterator::dip_equal( const ::_Dataiterator & lhs, const ::_Dataiterator & rhs ) const
+    {
+      // Iterator equal is same position in same container.
+      // Here: same attribute in same solvable.
+      return( lhs.solvid == rhs.solvid && lhs.key->name == rhs.key->name );
+    }
+
+    detail::IdType LookupAttr::iterator::dereference() const
+    {
+      return _dip ? ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 )
+                  : detail::noId;
+    }
+
+    void LookupAttr::iterator::increment()
+    {
+      if ( _dip && ! ::dataiterator_step( _dip.get() ) )
+      {
+        _dip.reset();
+        base_reference() = 0;
+      }
+    }
+
+    std::ostream & operator<<( std::ostream & str, const LookupAttr::iterator & obj )
+    {
+      const ::_Dataiterator * dip = obj.get();
+      if ( ! dip )
+        return str << "EndOfQuery";
+
+      if ( obj.inSolvable() )
+        str << obj.inSolvable();
+      else if ( obj.inRepo() )
+        str << obj.inRepo();
+
+      str << '<' << obj.inSolvAttr() << (obj.solvAttrSubEntry() ? ">(*" : ">(")
+          <<  IdString(obj.solvAttrType()) << ") = " << obj.asString();
+      return str;
+    }
+
+    template<> CheckSum LookupAttr::iterator::asType<CheckSum>() const
+    { return asCheckSum(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+std::ostream & operator<<( std::ostream & str, const ::_Dataiterator * obj )
+{
+  str << "::_Dataiterator(";
+  if ( ! obj )
+  {
+    str << "NULL";
+  }
+  else
+  {
+    str << "|" << zypp::Repository(obj->repo);
+    str << "|" << zypp::sat::Solvable(obj->solvid);
+    str << "|" << zypp::IdString(obj->key->name);
+    str << "|" << zypp::IdString(obj->key->type);
+    str << "|" << obj->repodataid;
+    str << "|" << obj->repoid;
+  }
+  return str << ")";
+}
+
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/LookupAttr.h b/zypp/sat/LookupAttr.h
new file mode 100644 (file)
index 0000000..9b2ed28
--- /dev/null
@@ -0,0 +1,613 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/LookupAttr.h
+ *
+*/
+#ifndef ZYPP_SAT_LOOKUPATTR_H
+#define ZYPP_SAT_LOOKUPATTR_H
+
+extern "C"
+{
+struct _Dataiterator;
+}
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/sat/SolvAttr.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class CheckSum;
+  class Match;
+  class MatchException;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    class AttrMatcher;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr
+    //
+    /** Lightweight attribute value lookup.
+     *
+     * Search for an attribute in \ref Pool, one \ref Repository
+     * or one \ref Solvable. \ref LookupAttr builds the query,
+     * \ref LookupAttr::iterator iterates over the result.
+     *
+     * Per default \ref LookupAttr looks for attributes associated with
+     * a \ref Solvable. But you may also pass \ref REPO_ATTR as
+     * \ref Location argument, to lookup attributes associated with
+     * the \ref Repository (e.g. DeltaRpm information).
+     *
+     * For convenience \see \ref LookupRepoAttr.
+     *
+     * Modifying the query will not affect any running
+     * iterator.
+     *
+     * Use \ref SolvAttr::allAttr to search all attributes.
+     *
+     * To search for attributes located in a sub-structure (flexarray)
+     * you also have to pass the sub-structures attribute as parent.
+     * Passing \ref SolvAttr::allAttr a parent will lookup the attribute
+     * in \c any sub-structure. Few attributes are known to have a parent
+     * (\see \ref SolvAttr::parent). Setting those attributes will automatically
+     * initialize their parent value.
+     *
+     * \code
+     *    // Lookup all 'name' attributes:
+     *    sat::LookupAttr q( sat::SolvAttr::name );
+     *    // Lookup all 'name' attributes within a sub-structure 'data':
+     *    sat::LookupAttr q( sat::SolvAttr::name, sat::SolvAttr::data );
+     *    // Lookup all 'name' attributes within any sub-structure:
+     *    sat::LookupAttr q( sat::SolvAttr::name, sat::SolvAttr::allAttr );
+     * \endcode
+     *
+     * \code
+     *  // look for all attributes of one solvable
+     *  void ditest( sat::Solvable slv_r )
+     *  {
+     *    sat::LookupAttr q( sat::SolvAttr::allAttr, slv_r );
+     *    MIL << q << ": " << endl;
+     *    for_( it, q.begin(), q.end() )
+     *    {
+     *      MIL << "    " << it.inSolvAttr() << " = " << it.asString() << endl;
+     *    }
+     *  }
+     * \endcode
+     *
+     * \code
+     *  // look for an attribute in the pool.
+     *  sat::LookupAttr q( sat::SolvAttr("susetags:datadir") );
+     *  MIL << q << ": " << endl;
+     *  for_( it, q.begin(), q.end() )
+     *  {
+     *    MIL << "    " << it << endl;
+     *  }
+     * \endcode
+     *
+     * \code
+     *  // look for a repo attribute in the pool.
+     *  sat::LookupRepoAttr q( sat::SolvAttr::repositoryAddedFileProvides );
+     *  MIL << q << ": " << endl;
+     *  for_( it, q.begin(), q.end() )
+     *  {
+     *    MIL << "    " << it << endl;
+     *  }
+     * \endcode
+     */
+    class LookupAttr
+    {
+      public:
+        typedef MatchException Exception;
+
+      public:
+        typedef unsigned size_type;
+
+        /** Specify the where to look for the attribule. */
+        enum Location {
+          SOLV_ATTR = 0,  //!< Search for solvable attributes (default)
+          REPO_ATTR = -1  //!< Search for repository attributes
+        };
+
+      public:
+        /** Default ctor finds nothing. */
+        LookupAttr();
+
+        /** Lookup \ref SolvAttr in \ref Pool (all repositories). */
+        explicit LookupAttr( SolvAttr attr_r, Location = SOLV_ATTR );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Location = SOLV_ATTR );
+
+        /** Lookup \ref SolvAttr in one\ref Repository. */
+        LookupAttr( SolvAttr attr_r, Repository repo_r, Location = SOLV_ATTR );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Repository repo_r, Location = SOLV_ATTR );
+
+        /** Lookup \ref SolvAttr in one \ref Solvable. */
+        LookupAttr( SolvAttr attr_r, Solvable solv_r );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Solvable solv_r );
+
+      public:
+        /** \name Search result. */
+        //@{
+        /** Result iterator. */
+        class iterator;
+
+        /** Iterator to the begin of query results. */
+        iterator begin() const;
+
+        /** Iterator behind the end of query results. */
+        iterator end() const;
+
+        /** Whether the query is empty. */
+        bool empty() const;
+
+        /** Ammount of results.
+         * \note This is not a cheap call. It runs the query.
+        */
+        size_type size() const;
+
+        /** TransformIterator returning an \ref iterator vaue of type \c _ResultT. */
+        template<class _ResultT, class _AttrT = _ResultT> class transformIterator;
+        //@}
+
+      public:
+        /** \name What to search. */
+        //@{
+        /** The \ref SolvAttr to search. */
+        SolvAttr attr() const;
+
+        /** Set the \ref SolvAttr to search. */
+        void setAttr( SolvAttr attr_r );
+        //@}
+
+        /** \name Restrict attributes to match a pattern. */
+        //@{
+        /** The pattern to match.
+         * You can also evaluate \ref AttrMatcher in a boolean context,
+         * in order to test whether an \ref AttrMatcher is set:
+         * \code
+         *   LookupAttr q;
+         *   if ( q.attrMatcher() )
+         *     ...; // an AttrMatcher is set
+         * \endcode
+        */
+        const AttrMatcher & attrMatcher() const;
+
+        /** Set the pattern to match.
+         * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+         */
+        void setAttrMatcher( const AttrMatcher & matcher_r );
+
+        /** Reset the pattern to match. */
+        void resetAttrMatcher();
+       //@}
+
+      public:
+        /** \name Where to search. */
+        //@{
+        /** Wheter to search in \ref Pool. */
+        bool pool() const;
+
+        /** Set search in \ref Pool (all repositories). */
+        void setPool( Location = SOLV_ATTR );
+
+        /** Wheter to search in one \ref Repository. */
+        Repository repo() const;
+
+        /** Set search in one \ref Repository. */
+        void setRepo( Repository repo_r, Location = SOLV_ATTR );
+
+        /** Wheter to search in one \ref Solvable. */
+        Solvable solvable() const;
+
+        /** Set search in one \ref Solvable. */
+        void setSolvable( Solvable solv_r );
+
+        /** Whether to search within a sub-structure (\ref SolvAttr::noAttr if not) */
+        SolvAttr parent() const;
+
+        /** Set search within a sub-structure (\ref SolvAttr::noAttr for none) */
+        void setParent( SolvAttr attr_r );
+        //@}
+
+      private:
+        class Impl;
+        RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates LookupAttr Stream output. */
+    std::ostream & operator<<( std::ostream & str, const LookupAttr & obj );
+
+    /** \relates LookupAttr Verbose stream output including the query result. */
+    std::ostream & dumpOn( std::ostream & str, const LookupAttr & obj );
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupRepoAttr
+    //
+    /** Lightweight repository attribute value lookup.
+     *
+     * This is just a convenience class that overloads all
+     * \ref LookupAttr methods which take a \ref LookupAttr::Location
+     * argument and sets it to \ref REPO_ATTR.
+     *
+     * \code
+     * // look for a repo attribute in the pool:
+     * sat::LookupAttr     p( sat::SolvAttr::repositoryAddedFileProvides, sat::LookupAttr::REPO_ATTR );
+     *
+     * // Equivalent but using LookupRepoAttr:
+     * sat::LookupRepoAttr q( sat::SolvAttr::repositoryAddedFileProvides );
+     * \endcode
+     *
+     * \see \ref LookupAttr
+     */
+    class LookupRepoAttr : public LookupAttr
+    {
+      public:
+        /** \copydoc LookupAttr::LookupAttr() */
+        LookupRepoAttr()
+        {}
+        /** \copydoc LookupAttr::LookupAttr(SolvAttr) */
+        explicit LookupRepoAttr( SolvAttr attr_r )
+        : LookupAttr( attr_r, REPO_ATTR )
+        {}
+        /** \copydoc LookupAttr::LookupAttr(SolvAttr,Repository) */
+        explicit LookupRepoAttr( SolvAttr attr_r, Repository repo_r );
+
+      public:
+        /** \copydoc LookupAttr::setPool */
+        void setPool()
+        { LookupAttr::setPool( REPO_ATTR ); }
+        /** \copydoc LookupAttr::setRepo */
+        void setRepo( Repository repo_r );
+      private:
+        // Hide. You can't look inside and outside Solvables at the same time.
+        using LookupAttr::solvable;
+        using LookupAttr::setSolvable;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      /** Wrapper around sat \c ::_Dataiterator.
+       *
+       * Manages copy and assign, and release of allocated
+       * resources like datamatcher inside the dataiterator.
+       * Also maintains a copy of the matchstring in order to
+       * keep the char* passed to the dataiterator valid.
+       */
+      class DIWrap : private base::SafeBool<DIWrap>
+      {
+        public:
+          /** \c NULL \c ::_Dataiterator */
+          DIWrap()
+          : _dip( 0 )
+          {}
+          /** Initializes */
+          DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                  const std::string & mstring_r = std::string(), int flags_r = 0 );
+          /** \overload to catch \c NULL \a mstring_r. */
+          DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                  const char * mstring_r, int flags_r = 0 );
+          DIWrap( const DIWrap & rhs );
+          ~DIWrap();
+        public:
+          void swap( DIWrap & rhs )
+          {
+            if ( &rhs != this ) // prevent self assign!
+            {
+              std::swap( _dip, rhs._dip );
+              std::swap( _mstring, rhs._mstring );
+            }
+          }
+          DIWrap & operator=( const DIWrap & rhs )
+          {
+            if ( &rhs != this ) // prevent self assign!
+              DIWrap( rhs ).swap( *this );
+            return *this;
+          }
+          void reset()
+          { DIWrap().swap( *this ); }
+        public:
+#ifndef SWIG // Swig treats it as syntax error
+          /** Evaluate in a boolean context <tt>( _dip != NULL )</tt>. */
+          using base::SafeBool<DIWrap>::operator bool_type;
+#endif
+        public:
+          ::_Dataiterator * operator->() const  { return _dip; }
+          ::_Dataiterator * get()        const  { return _dip; }
+          const std::string & getstr()   const  { return _mstring; }
+        private:
+          friend base::SafeBool<DIWrap>::operator bool_type() const;
+          bool boolTest() const
+          { return _dip; }
+        private:
+          ::_Dataiterator * _dip;
+          std::string _mstring;
+      };
+      /** \relates DIWrap Stream output. */
+      std::ostream & operator<<( std::ostream & str, const DIWrap & obj );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr::iterator
+    //
+    /** Result iterator.
+     * Extended iterator methods valid only if not @end.
+     * \note Implementation: Keep iterator_adaptor base and _dip in sync!
+     */
+    class LookupAttr::iterator : public boost::iterator_adaptor<
+        iterator                       // Derived
+        , ::_Dataiterator *            // Base
+        , detail::IdType               // Value
+        , boost::forward_traversal_tag // CategoryOrTraversal
+        , detail::IdType               // Reference
+    >
+    {
+      public:
+        /** \name Moving fast forward. */
+        //@{
+        /** On the next call to \ref operator++ advance to the next \ref SolvAttr. */
+        void nextSkipSolvAttr();
+
+        /** On the next call to \ref operator++ advance to the next \ref Solvable. */
+        void nextSkipSolvable();
+
+        /** On the next call to \ref operator++ advance to the next \ref Repository. */
+        void nextSkipRepo();
+
+        /** Immediately advance to the next \ref SolvAttr. */
+        void skipSolvAttr()
+        { nextSkipSolvAttr(); increment(); }
+
+        /** Immediately advance to the next \ref Solvable. */
+        void skipSolvable()
+        { nextSkipSolvable(); increment(); }
+
+        /** Immediately advance to the next \ref Repository. */
+        void skipRepo()
+        { nextSkipRepo(); increment(); }
+
+        /** Stop after all matches in the current \ref Solvable are processed. */
+        void stayInThisSolvable();
+
+        /** Stop after all matches in the current \ref Repository are processed. */
+        void stayInThisRepo();
+        //@}
+
+        /** \name Current position info. */
+        //@{
+        /** The current \ref Repository. */
+        Repository inRepo() const;
+
+        /** The current \ref Solvable. */
+        Solvable inSolvable() const;
+
+        /** The current \ref SolvAttr. */
+        SolvAttr inSolvAttr() const;
+
+        /** Whether this points to the end of a query (Iterator is invalid). */
+        bool atEnd() const
+        { return !_dip; }
+        //@}
+
+        /** \name Test attribute value type. */
+        //@{
+        /** The current \ref SolvAttr type. */
+        detail::IdType solvAttrType() const;
+
+        /** Whether this is a numeric attribute (incl. boolean). */
+        bool solvAttrNumeric() const;
+
+        /** Whether this is a string attribute. */
+        bool solvAttrString() const;
+
+        /** Whether this string attribute is available as \ref IdString. */
+        bool solvAttrIdString() const;
+
+        /** Whether this is a CheckSum attribute.*/
+        bool solvAttrCheckSum() const;
+
+        /** Whether this is the entry to a sub-structure (flexarray).
+         * This is the entry to a sequence of attributes. To
+         * acces them use \ref subBegin and  \ref subEnd.
+        */
+        bool solvAttrSubEntry() const;
+        //@}
+
+        /** \name Iterate sub-structures.
+         *
+         * These are usable iff \ref solvAttrSubEntry is \c true.
+         *
+         * \note Unfortunately the underlying satsolver dataiterator as returned
+         * by \ref subBegin and \ref subFind loses some context when being created.
+         * Thus it's not possible to invoke \ref subBegin and \ref subFind on an
+         * iterator that was previously returned by one of those methods. The result
+         * will be an \c end iterator. For the same reason it is not possible for an
+         * iterator to leave the sub-structure again.
+         *
+         * \code
+         * // Lookup all "update:reference" entries for a specific solvable
+         * sat::LookupAttr q( sat::SolvAttr::updateReference, p->satSolvable() );
+         * for_( res, q.begin(), q.end() )
+         * {
+         *   // List all sub values
+         *   for_( sub, res.subBegin(), res.subEnd() )
+         *   {
+         *     cout << sub.asString() << endl;
+         *   }
+         *
+         *   // Directly access c specific value:
+         *   sat::LookupAttr::iterator it( res.subFind( sat::SolvAttr::updateReferenceHref ) );
+         *   if ( it != res.subEnd() )
+         *     cout << it.asString() << endl;
+         * }
+         * \endcode
+         */
+        //@{
+        /** Wheter the sub-structure is empty. */
+        bool subEmpty() const;
+
+        /** Ammount of attributes in the sub-structure.
+         * \note This is not a cheap call. It runs the query.
+        */
+        size_type subSize() const;
+
+        /** Iterator to the begin of a sub-structure.
+         * \see \ref solvAttrSubEntry
+        */
+        iterator subBegin() const;
+        /** Iterator behind the end of a sub-structure.
+         * \see \ref solvAttrSubEntry
+        */
+        iterator subEnd() const;
+         /** Iterator pointing to the first occurance of \ref SolvAttr \a attr_r in sub-structure.
+          * If \ref sat::SolvAttr::allAttr is passed, \ref subBegin is returned.
+          * \see \ref solvAttrSubEntry
+         */
+        iterator subFind( SolvAttr attr_r ) const;
+        /** \overload Extending the current attribute name with by \c ":attrname_r".
+         *
+         * This assumes a sub-structur \c "update:reference" has attributes
+         * like \c "update:reference:type", \c "update:reference:href".
+         *
+         * If an empty \c attrname_r is passed, \ref subBegin is returned.
+        */
+        iterator subFind( const C_Str & attrname_r ) const;
+        //@}
+
+        /** \name Retrieving attribute values. */
+        //@{
+        /** Conversion to numeric types. */
+        int asInt() const;
+        /** \overload */
+        unsigned asUnsigned() const;
+        /** \overload */
+        bool asBool() const;
+
+        /** Conversion to string types. */
+        const char * c_str() const;
+        /** \overload
+         * If used with non-string types, this method tries to create
+         * some appropriate string representation.
+        */
+        std::string asString() const;
+
+        /** As \ref IdStr.
+         * This is only done for poolized string types. Large strings like
+         * summary or descriptions are not available via \ref IdStr, only
+         * via \ref c_str and \ref asString.
+         */
+        IdString idStr() const;
+       /** \overload Directly returning the \c Id */
+        detail::IdType id() const
+        { return idStr().id(); }
+
+        /** As \ref CheckSum. */
+        CheckSum asCheckSum() const;
+
+        /** Templated return type.
+         * Per default assumes an Id based type, so try to construct
+         * it from the Id.
+         *
+         * Should be specialized for supported types above.
+        */
+        template<class _Tp> _Tp asType() const { return _Tp(id()); }
+        //@}
+
+        ///////////////////////////////////////////////////////////////////
+        // internal stuff below
+        ///////////////////////////////////////////////////////////////////
+      public:
+        iterator();
+
+        iterator( const iterator & rhs );
+
+        iterator & operator=( const iterator & rhs );
+
+        ~iterator();
+
+      public:
+        /**
+         * C-tor taking over ownership of the passed \c ::_Dataiterator
+         * and doing it's first iteration (::dataiterator_step)
+         */
+        iterator( detail::DIWrap & dip_r );
+
+      private:
+        friend class boost::iterator_core_access;
+
+        template <class OtherDerived, class OtherIterator, class V, class C, class R, class D>
+        bool equal( const boost::iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> & rhs ) const
+        {
+          return ( bool(base()) == bool(rhs.base()) )
+              && ( ! base() || dip_equal( *base(), *rhs.base() ) );
+        }
+
+        bool dip_equal( const ::_Dataiterator & lhs, const ::_Dataiterator & rhs ) const;
+
+        detail::IdType dereference() const;
+
+        void increment();
+
+      public:
+        /** Expert backdoor. */
+        ::_Dataiterator * get() const
+        { return _dip.get(); }
+      private:
+        detail::DIWrap _dip;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates LookupAttr::iterator Stream output. */
+    std::ostream & operator<<( std::ostream & str, const LookupAttr::iterator & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name Helpers and forward declarations from LookupAttrTools.h */
+    //@{
+    template<> inline int          LookupAttr::iterator::asType<int>()          const { return asInt(); }
+    template<> inline unsigned     LookupAttr::iterator::asType<unsigned>()     const { return asUnsigned(); }
+    template<> inline bool         LookupAttr::iterator::asType<bool>()         const { return asBool(); }
+    template<> inline const char * LookupAttr::iterator::asType<const char *>() const { return c_str(); }
+    template<> inline std::string  LookupAttr::iterator::asType<std::string>()  const { return asString(); }
+    template<> inline IdString     LookupAttr::iterator::asType<IdString>()     const { return idStr(); }
+    template<>        CheckSum     LookupAttr::iterator::asType<CheckSum>()     const;
+
+    template<class _ResultT, class _AttrT>
+    class ArrayAttr;
+    //@}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+/** \relates LookupAttr::iterator Stream output of the underlying iterator for debug. */
+std::ostream & operator<<( std::ostream & str, const ::_Dataiterator * obj );
+
+/** \relates LookupAttr::iterator Stream output of the underlying iterator for debug. */
+inline std::ostream & operator<<( std::ostream & str, const ::_Dataiterator & obj )
+{ return str << &obj; }
+
+#endif // ZYPP_SAT_LOOKUPATTR_H
diff --git a/zypp/sat/LookupAttrTools.h b/zypp/sat/LookupAttrTools.h
new file mode 100644 (file)
index 0000000..800e9c2
--- /dev/null
@@ -0,0 +1,223 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/LookupAttrTools.h
+ *
+*/
+#ifndef ZYPP_SAT_LOOKUPATTRTOOLS_H
+#define ZYPP_SAT_LOOKUPATTRTOOLS_H
+
+#include "zypp/sat/LookupAttr.h"
+#include "zypp/Repository.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : LookupAttr::transformIterator
+    //
+    /** TransformIterator returning an \ref iterator value of type \c _ResultT.
+     *
+     * The underlying LookupAttr::iterators value is retrieved \ref asType<_AttrT>
+     * and the returned \ref ResultT is constructed fron that value.
+     *
+     * \code
+     *   class Keywords
+     *   {
+     *     public:
+     *       Keywords( sat::Solvable solv_r )
+     *       : _q( sat::SolvAttr::keywords, solv_r )
+     *       {}
+     *
+     *     public:
+     *       typedef sat::LookupAttr::transformIterator<PackageKeyword,IdString> iterator;
+     *
+     *       iterator begin() const { return iterator( _q.begin() ); }
+     *       iterator end() const   { return iterator( _q.end() ); }
+     *
+     *     private:
+     *       sat::LookupAttr _q;
+     *   };
+     * \endcode
+     *
+     * \see \ref ArrayAttr.
+     */
+    template<class _ResultT, class _AttrT>
+    class LookupAttr::transformIterator : public boost::iterator_adaptor<
+          transformIterator<_ResultT,_AttrT> // Derived
+          , LookupAttr::iterator         // Base
+          , _ResultT                     // Value
+          , boost::forward_traversal_tag // CategoryOrTraversal
+          , _ResultT                     // Reference
+    >
+    {
+      public:
+        transformIterator()
+        {}
+
+        explicit
+        transformIterator( const LookupAttr::iterator & val_r )
+        { this->base_reference() = val_r; }
+
+      public:
+
+        /** \name Moving fast forward. */
+        //@{
+        /** On the next call to \ref operator++ advance to the next \ref SolvAttr. */
+        void nextSkipSolvAttr()
+        { this->base_reference().nextSkipSolvAttr(); }
+
+        /** On the next call to \ref operator++ advance to the next \ref Solvable. */
+        void nextSkipSolvable()
+        { this->base_reference().nextSkipSolvable(); }
+
+        /** On the next call to \ref operator++ advance to the next \ref Repository. */
+        void nextSkipRepo()
+        { this->base_reference().nextSkipRepo(); }
+
+        /** Immediately advance to the next \ref SolvAttr. */
+        void skipSolvAttr()
+        { this->base_reference().skipSolvAttr(); }
+
+        /** Immediately advance to the next \ref Solvable. */
+        void skipSolvable()
+        { this->base_reference().skipSolvable(); }
+
+        /** Immediately advance to the next \ref Repository. */
+        void skipRepo()
+        { this->base_reference().skipRepo(); }
+        //@}
+
+        /** \name Current position info. */
+        //@{
+        /** The current \ref Repository. */
+        Repository inRepo() const
+        { return this->base_reference().inRepo(); }
+
+        /** The current \ref Solvabele. */
+        Solvable inSolvable() const
+        { return this->base_reference().inSolvable(); }
+
+        /** The current \ref SolvAttr. */
+        SolvAttr inSolvAttr() const
+        { return this->base_reference().inSolvAttr(); }
+        //@}
+
+      private:
+        friend class boost::iterator_core_access;
+
+        _ResultT dereference() const
+        {
+          const LookupAttr::iterator lit( this->base_reference() );
+          return _ResultT( lit.asType<_AttrT>() );
+        }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    template<class _ResultT, class _AttrT>
+    class ArrayAttr;
+
+    template<class _ResultT, class _AttrT>
+    std::ostream & operator<<( std::ostream & str, const ArrayAttr<_ResultT,_AttrT> & obj );
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ArrayAttr
+    //
+    /** \ref LookupAttr::transformIterator based container to retrieve list attributes.
+     *
+     * You may pass \ref LookupAttr::REPO_ATTR as \ref LookupAttr::Location argument,
+     * to lookup attributes associated with the \ref Repository as a whole
+     * (e.g. repository keywords).
+     *
+     * \see \ref LookupAttr for details.
+     *
+     * \code
+     *  typedef ArrayAttr<PackageKeyword,IdString> Keywords;
+     *  Keywords k( sat::SolvAttr::keywords );
+     *  dumpRange( MIL << "All Keywords: ", k.begin(), k.end() ) << endl;
+     * \endcode
+     *
+     * \todo Maybe add some way to unify the result.
+     */
+    template<class _ResultT, class _AttrT>
+    class ArrayAttr
+    {
+      friend std::ostream & operator<< <_ResultT,_AttrT>( std::ostream & str, const ArrayAttr<_ResultT,_AttrT> & obj );
+
+      public:
+        ArrayAttr()
+        {}
+
+        ArrayAttr( SolvAttr attr_r, LookupAttr::Location loc_r = LookupAttr::SOLV_ATTR )
+        : _q( attr_r, loc_r )
+        {}
+
+        ArrayAttr( SolvAttr attr_r, Repository repo_r, LookupAttr::Location loc_r = LookupAttr::SOLV_ATTR )
+        : _q( attr_r, repo_r, loc_r )
+        {}
+
+        ArrayAttr( SolvAttr attr_r, Solvable solv_r )
+        : _q( attr_r, solv_r )
+        {}
+
+      public:
+        typedef LookupAttr::transformIterator<_ResultT,_AttrT> iterator;
+        typedef LookupAttr::size_type size_type;
+
+        iterator begin() const
+        { return iterator( _q.begin() ); }
+
+        iterator end() const
+        { return iterator( _q.end() ); }
+
+        bool empty() const
+        { return _q.empty(); }
+
+        size_type size() const
+        {
+          size_type count = 0;
+          for_( it, begin(), end() )
+            ++count;
+          return count;
+        }
+
+      public:
+
+        iterator find( const _ResultT & key_r ) const
+        {
+          for_( it, begin(), end() )
+          {
+            if ( *it == key_r )
+              return it;
+          }
+          return end();
+        }
+
+      private:
+        LookupAttr _q;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates LookupAttr::iterator Stream output. */
+    template<class _ResultT, class _AttrT>
+    inline std::ostream & operator<<( std::ostream & str, const ArrayAttr<_ResultT,_AttrT> & obj )
+    { return dumpOn( str, obj._q ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_LOOKUPATTRTOOLS_H
diff --git a/zypp/sat/Pool.cc b/zypp/sat/Pool.cc
new file mode 100644 (file)
index 0000000..cc0b336
--- /dev/null
@@ -0,0 +1,236 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Pool.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/AutoDispose.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/LookupAttr.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    const std::string & Pool::systemRepoAlias()
+    { return detail::PoolImpl::systemRepoAlias(); }
+
+    ::_Pool * Pool::get() const
+    { return myPool().getPool(); }
+
+    Pool::size_type Pool::capacity() const
+    { return myPool()->nsolvables; }
+
+    const SerialNumber & Pool::serial() const
+    { return myPool().serial(); }
+
+    void Pool::prepare() const
+    { return myPool().prepare(); }
+
+    bool Pool::reposEmpty() const
+    { return ! myPool()->nrepos; }
+
+    Pool::size_type Pool::reposSize() const
+    { return myPool()->nrepos; }
+
+    Pool::RepositoryIterator Pool::reposBegin() const
+    { return RepositoryIterator( myPool()->repos ); }
+
+    Pool::RepositoryIterator Pool::reposEnd() const
+    { return RepositoryIterator( myPool()->repos+myPool()->nrepos ); }
+
+    bool Pool::solvablesEmpty() const
+    {
+      // return myPool()->nsolvables;
+      // nsolvables is the array size including
+      // invalid Solvables.
+      for_( it, reposBegin(), reposEnd() )
+      {
+        if ( ! it->solvablesEmpty() )
+          return false;
+      }
+      return true;
+    }
+
+    Pool::size_type Pool::solvablesSize() const
+    {
+      // Do not return myPool()->nsolvables;
+      // nsolvables is the array size including
+      // invalid Solvables.
+      size_type ret = 0;
+      for_( it, reposBegin(), reposEnd() )
+      {
+        ret += it->solvablesSize();
+      }
+      return ret;
+    }
+
+    Pool::SolvableIterator Pool::solvablesBegin() const
+    { return SolvableIterator( myPool().getFirstId() ); }
+
+    Pool::SolvableIterator Pool::solvablesEnd() const
+    { return SolvableIterator(); }
+
+    Repository Pool::reposInsert( const std::string & alias_r )
+    {
+      Repository ret( reposFind( alias_r ) );
+      if ( ret )
+        return ret;
+
+      ret = Repository( myPool()._createRepo( alias_r ) );
+      if ( ret.isSystemRepo() )
+      {
+        // autoprovide (dummy) RepoInfo
+        RepoInfo info;
+        info.setAlias( alias_r );
+        info.setName( alias_r );
+        info.setAutorefresh( true );
+        info.setEnabled( true );
+        ret.setInfo( info );
+      }
+      return ret;
+    }
+
+    Repository Pool::reposFind( const std::string & alias_r ) const
+    {
+      for_( it, reposBegin(), reposEnd() )
+      {
+        if ( alias_r == it->alias() )
+          return *it;
+      }
+      return Repository();
+    }
+
+    Repository Pool::findSystemRepo() const
+    {
+      return Repository( myPool().systemRepo() );
+    }
+
+    Repository Pool::systemRepo()
+    {
+      if ( myPool().systemRepo() )
+        return Repository( myPool().systemRepo() );
+      return reposInsert( systemRepoAlias() );
+    }
+
+    Repository Pool::addRepoSolv( const Pathname & file_r, const std::string & alias_r )
+    {
+      // Using a temporay repo! (The additional parenthesis are required.)
+      AutoDispose<Repository> tmprepo( (Repository::EraseFromPool()) );
+      *tmprepo = reposInsert( alias_r );
+      tmprepo->addSolv( file_r );
+
+      // no exceptions so we keep it:
+      tmprepo.resetDispose();
+      return tmprepo;
+    }
+
+    Repository Pool::addRepoSolv( const Pathname & file_r )
+    { return addRepoSolv( file_r, file_r.basename() ); }
+
+    Repository Pool::addRepoSolv( const Pathname & file_r, const RepoInfo & info_r )
+    {
+      Repository ret( addRepoSolv( file_r, info_r.alias() ) );
+      ret.setInfo( info_r );
+      return ret;
+    }
+
+    /////////////////////////////////////////////////////////////////
+
+    Repository Pool::addRepoHelix( const Pathname & file_r, const std::string & alias_r )
+    {
+      // Using a temporay repo! (The additional parenthesis are required.)
+      AutoDispose<Repository> tmprepo( (Repository::EraseFromPool()) );
+      *tmprepo = reposInsert( alias_r );
+      tmprepo->addHelix( file_r );
+
+      // no exceptions so we keep it:
+      tmprepo.resetDispose();
+      return tmprepo;
+    }
+
+    Repository Pool::addRepoHelix( const Pathname & file_r )
+    { return addRepoHelix( file_r, file_r.basename() ); }
+
+    Repository Pool::addRepoHelix( const Pathname & file_r, const RepoInfo & info_r )
+    {
+      Repository ret( addRepoHelix( file_r, info_r.alias() ) );
+      ret.setInfo( info_r );
+      return ret;
+    }
+
+   /////////////////////////////////////////////////////////////////
+
+    void Pool::setTextLocale( const Locale & locale_r )
+    { myPool().setTextLocale( locale_r ); }
+
+    void Pool::setRequestedLocales( const LocaleSet & locales_r )
+    { myPool().setRequestedLocales( locales_r ); }
+
+    bool Pool::addRequestedLocale( const Locale & locale_r )
+    { return myPool().addRequestedLocale( locale_r ); }
+
+    bool Pool::eraseRequestedLocale( const Locale & locale_r )
+    { return myPool().eraseRequestedLocale( locale_r ); }
+
+    const LocaleSet & Pool::getRequestedLocales() const
+    { return myPool().getRequestedLocales(); }
+
+    bool Pool::isRequestedLocale( const Locale & locale_r ) const
+    { return myPool().isRequestedLocale( locale_r ); }
+
+    const LocaleSet & Pool::getAvailableLocales() const
+    {  return myPool().getAvailableLocales(); }
+
+    bool Pool::isAvailableLocale( const Locale & locale_r ) const
+    { return myPool().isAvailableLocale( locale_r ); }
+
+    bool Pool::multiversionEmpty() const                       { return myPool().multiversionList().empty(); }
+    size_t Pool::multiversionSize() const                      { return myPool().multiversionList().size(); }
+    Pool::MultiversionIterator Pool::multiversionBegin() const { return myPool().multiversionList().begin(); }
+    Pool::MultiversionIterator Pool::multiversionEnd() const   { return myPool().multiversionList().end(); }
+    bool Pool::isMultiversion( IdString ident_r ) const                { return myPool().isMultiversion( ident_r ); }
+
+    bool Pool::onSystemByUserEmpty() const                     { return myPool().onSystemByUserList().empty(); }
+    size_t Pool::onSystemByUserSize() const                    { return myPool().onSystemByUserList().size(); }
+    Pool::OnSystemByUserIterator Pool::onSystemByUserBegin() const     { return myPool().onSystemByUserList().begin(); }
+    Pool::OnSystemByUserIterator Pool::onSystemByUserEnd() const       { return myPool().onSystemByUserList().end(); }
+    bool Pool::isOnSystemByUser( IdString ident_r ) const      { return myPool().isOnSystemByUser( ident_r ); }
+
+   /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Pool & obj )
+    {
+      return str << "sat::pool(" << obj.serial() << ")["
+          << obj.capacity() << "]{"
+          << obj.reposSize() << "repos|"
+         << obj.solvablesSize() << "slov}";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/Pool.h b/zypp/sat/Pool.h
new file mode 100644 (file)
index 0000000..d88e5c4
--- /dev/null
@@ -0,0 +1,266 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Pool.h
+ *
+*/
+#ifndef ZYPP_SAT_POOL_H
+#define ZYPP_SAT_POOL_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/Repository.h"
+#include "zypp/sat/WhatProvides.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class SerialNumber;
+  class RepoInfo;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Pool
+    //
+    /** Global sat-pool.
+     *
+     * Explicitly shared singleton \ref Pool::instance.
+     */
+    class Pool : protected detail::PoolMember
+    {
+      public:
+        typedef detail::SolvableIterator SolvableIterator;
+       typedef zypp::detail::RepositoryIterator     RepositoryIterator;
+        typedef detail::size_type        size_type;
+
+      public:
+        /** Singleton ctor. */
+        static Pool instance()
+        { return Pool(); }
+
+        /** Ctor from \ref PoolMember. */
+        Pool( const detail::PoolMember & )
+        {}
+
+      public:
+        /** Internal array size for stats only. */
+        size_type capacity() const;
+
+        /** Housekeeping data serial number. */
+        const SerialNumber & serial() const;
+
+        /** Update housekeeping data if necessary (e.g. whatprovides). */
+        void prepare() const;
+
+      public:
+        /** Whether \ref Pool contains repos. */
+        bool reposEmpty() const;
+
+        /** Number of repos in \ref Pool. */
+        size_type reposSize() const;
+
+        /** Iterator to the first \ref Repository. */
+        RepositoryIterator reposBegin() const;
+
+        /** Iterator behind the last \ref Repository. */
+        RepositoryIterator reposEnd() const;
+
+        /** Return a \ref Repository named \c alias_r.
+         * It a such a \ref Repository does not already exist
+         * a new empty \ref Repository is created.
+         */
+        Repository reposInsert( const std::string & alias_r );
+
+        /** Find a \ref Repository named \c alias_r.
+         * Returns \ref norepository if there is no such \ref Repository.
+         */
+        Repository reposFind( const std::string & alias_r ) const;
+
+        /** Remove a \ref Repository named \c alias_r.
+         * \see \ref Repository::eraseFromPool
+         */
+        void reposErase( const std::string & alias_r )
+        { reposFind( alias_r ).eraseFromPool(); }
+
+      public:
+        /** Reserved system repository alias \c @System. */
+        static const std::string & systemRepoAlias();
+
+        /** Return the system repository if it is on the pool. */
+        Repository findSystemRepo() const;
+
+        /** Return the system repository, create it if missing. */
+        Repository systemRepo();
+
+      public:
+        /** Load \ref Solvables from a solv-file into a \ref Repository named \c name_r.
+         * In case of an exception the \ref Repository is removed from the \ref Pool.
+         * \throws Exception if loading the solv-file fails.
+         * \see \ref Repository::EraseFromPool
+        */
+        Repository addRepoSolv( const Pathname & file_r, const std::string & name_r );
+        /** \overload Using the files basename as \ref Repository name. */
+        Repository addRepoSolv( const Pathname & file_r );
+        /** \overload Using the \ref RepoInfo::alias \ref Repo name.
+         * Additionally stores the \ref RepoInfo. \See \ref Prool::setInfo.
+        */
+        Repository addRepoSolv( const Pathname & file_r, const RepoInfo & info_r );
+
+      public:
+        /** Load \ref Solvables from a helix-file into a \ref Repository named \c name_r.
+         * Supports loading of gzip compressed files (.gz). In case of an exception
+         * the \ref Repository is removed from the \ref Pool.
+         * \throws Exception if loading the helix-file fails.
+         * \see \ref Repository::EraseFromPool
+        */
+        Repository addRepoHelix( const Pathname & file_r, const std::string & name_r );
+        /** \overload Using the files basename as \ref Repository name. */
+        Repository addRepoHelix( const Pathname & file_r );
+        /** \overload Using the \ref RepoInfo::alias \ref Repo name.
+         * Additionally stores the \ref RepoInfo. \See \ref Prool::setInfo.
+        */
+        Repository addRepoHelix( const Pathname & file_r, const RepoInfo & info_r );
+
+      public:
+        /** Whether \ref Pool contains solvables. */
+        bool solvablesEmpty() const;
+
+        /** Number of solvables in \ref Pool. */
+        size_type solvablesSize() const;
+
+        /** Iterator to the first \ref Solvable. */
+        SolvableIterator solvablesBegin() const;
+
+        /** Iterator behind the last \ref Solvable. */
+        SolvableIterator solvablesEnd() const;
+
+      public:
+        /** \name Iterate all Solvables matching a \c _Filter. */
+        //@{
+        template<class _Filter>
+        filter_iterator<_Filter,SolvableIterator> filterBegin( const _Filter & filter_r ) const
+        { return make_filter_iterator( filter_r, solvablesBegin(), solvablesEnd() ); }
+
+        template<class _Filter>
+        filter_iterator<_Filter,SolvableIterator> filterEnd( const _Filter & filter_r ) const
+        { return make_filter_iterator( filter_r, solvablesEnd(), solvablesEnd() ); }
+        //@}
+
+     public:
+        /** Conainer of all \ref Solvable providing \c cap_r.  */
+        WhatProvides whatProvides( Capability cap_r ) const
+        { return WhatProvides( cap_r ); }
+
+      public:
+        /** \name Requested locales. */
+        //@{
+       /** Set the default language for retrieving translated texts.
+        * Updated when calling \ref ZConfig::setTextLocale.
+        */
+       void setTextLocale( const Locale & locale_r );
+
+        /** Set the requested locales.
+         * Languages to be supported by the system, e.g. language specific
+         * packages to be installed.
+         */
+        void setRequestedLocales( const LocaleSet & locales_r );
+
+        /** Add one \ref Locale to the set of requested locales.
+         * Return \c true if \c locale_r was newly added to the set.
+        */
+        bool addRequestedLocale( const Locale & locale_r );
+
+        /** Erase one \ref Locale from the set of requested locales.
+        * Return \c false if \c locale_r was not found in the set.
+         */
+        bool eraseRequestedLocale( const Locale & locale_r );
+
+        /** Return the requested locales.
+         * \see \ref setRequestedLocales
+        */
+        const LocaleSet & getRequestedLocales() const;
+
+        /** Wheter this \ref Locale is in the set of requested locales. */
+        bool isRequestedLocale( const Locale & locale_r ) const;
+
+        /** Get the set of available locales.
+         * This is computed from the package data so it actually
+         * represents all locales packages claim to support.
+         */
+        const LocaleSet & getAvailableLocales() const;
+
+        /** Wheter this \ref Locale is in the set of available locales. */
+        bool isAvailableLocale( const Locale & locale_r ) const;
+        //@}
+
+      public:
+        /** \name Multiversion install.
+         * Ident list of all packages that can be installed in different version
+         * at the same time. (\see \ref ZConfig::multiversionSpec)
+         */
+        //@{
+        typedef IdStringSet::const_iterator MultiversionIterator;
+
+        bool multiversionEmpty() const;
+        size_t multiversionSize() const;
+        MultiversionIterator multiversionBegin() const;
+        MultiversionIterator multiversionEnd() const;
+
+        bool isMultiversion( IdString ident_r ) const;
+        //@}
+
+      public:
+        /** \name Installed on behalf of a user request hint.
+        * This is a hint guessed by evaluating an available install history.
+         */
+        //@{
+        typedef IdStringSet::const_iterator OnSystemByUserIterator;
+
+        bool onSystemByUserEmpty() const;
+        size_t onSystemByUserSize() const;
+        OnSystemByUserIterator onSystemByUserBegin() const;
+        OnSystemByUserIterator onSystemByUserEnd() const;
+
+        bool isOnSystemByUser( IdString ident_r ) const;
+        //@}
+
+      public:
+        /** Expert backdoor. */
+        ::_Pool * get() const;
+      private:
+        /** Default ctor */
+        Pool() {}
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Pool Stream output */
+    std::ostream & operator<<( std::ostream & str, const Pool & obj );
+
+    /** \relates Pool */
+    inline bool operator==( const Pool & lhs, const Pool & rhs )
+    { return lhs.get() == rhs.get(); }
+
+    /** \relates Pool */
+    inline bool operator!=( const Pool & lhs, const Pool & rhs )
+    { return lhs.get() != rhs.get(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_POOL_H
diff --git a/zypp/sat/Queue.cc b/zypp/sat/Queue.cc
new file mode 100644 (file)
index 0000000..d029129
--- /dev/null
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Queue.cc
+ */
+extern "C"
+{
+#include "satsolver/queue.h"
+}
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/sat/Queue.h"
+#include "zypp/sat/Solvable.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    Queue::Queue()
+      : _pimpl( new struct ::_Queue )
+    {
+      ::queue_init( _pimpl );
+    }
+
+    Queue::~Queue()
+    {
+      ::queue_free( _pimpl );
+      delete( _pimpl );
+    }
+
+    bool Queue::empty() const
+    { return( _pimpl->count == 0 ); }
+
+    Queue::size_type Queue::size() const
+    { return _pimpl->count; }
+
+    Queue::const_iterator Queue::begin() const
+    { return _pimpl->elements; }
+
+    Queue::const_iterator Queue::end() const
+    { return _pimpl->elements + _pimpl->count;}
+
+    Queue::const_iterator Queue::find( value_type val_r ) const
+    {
+      for_( it, begin(), end() )
+       if ( *it != val_r )
+         return it;
+      return end();
+    }
+
+    Queue::value_type Queue::first() const
+    {
+      if ( empty() )
+       return 0;
+      return *_pimpl->elements;
+    }
+
+    Queue::value_type Queue::last() const
+    {
+      if ( empty() )
+       return 0;
+      return _pimpl->elements[_pimpl->count-1];
+    }
+
+    void Queue::clear()
+    { ::queue_empty( *this ); }
+
+    void Queue::remove( value_type val_r )
+    {
+      const_iterator it( find( val_r ) );
+      if ( it != end() )
+      {
+       ::queue_delete( _pimpl, it - begin() );
+      }
+    }
+
+    void Queue::push( value_type val_r )
+    { ::queue_push( _pimpl, val_r ); }
+
+    Queue::value_type Queue::pop()
+    { return ::queue_pop( _pimpl ); }
+
+    void Queue::push_front( value_type val_r )
+    { ::queue_unshift( _pimpl, val_r ); }
+
+    Queue::value_type Queue::pop_front()
+    { return ::queue_shift( _pimpl ); }
+
+    std::ostream & operator<<( std::ostream & str, const Queue & obj )
+    { return dumpRangeLine( str << "Queue ", obj.begin(), obj.end() );  }
+
+    std::ostream & dumpOn( std::ostream & str, const Queue & obj )
+    {
+      str << "Queue {";
+      if ( ! obj.empty() )
+      {
+       str << endl;
+       for_( it, obj.begin(), obj.end() )
+         str << "  " << Solvable(*it) << endl;
+      }
+      return str << "}";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/Queue.h b/zypp/sat/Queue.h
new file mode 100644 (file)
index 0000000..a28b337
--- /dev/null
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Queue.h
+ */
+#ifndef ZYPP_SAT_QUEUE_H
+#define ZYPP_SAT_QUEUE_H
+
+extern "C"
+{
+  struct _Queue;
+}
+#include <iosfwd>
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/sat/detail/PoolMember.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    /** Satsolver Id queue wrapper.
+     */
+    class Queue : private base::NonCopyable
+    {
+      public:
+       typedef unsigned size_type;
+       typedef detail::IdType value_type;
+       typedef const value_type* const_iterator;
+
+      public:
+        /** Default ctor: empty Queue. */
+        Queue();
+
+        /** Dtor */
+        ~Queue();
+
+       bool empty() const;
+       size_type size() const;
+       const_iterator begin() const;
+       const_iterator end() const;
+
+       /** Return iterator to the 1st occurance of \a val_r or \ref end. */
+       const_iterator find( value_type val_r ) const;
+
+       /** Return whether the Queue contais at lest one element with value \a val_r. */
+       bool contains( value_type val_r ) const
+       { return( find( val_r ) != end() ); }
+
+       /** Return the 1st Id in the queue or \c 0 if empty. */
+       value_type first() const;
+
+       /** Return the last Id in the queue or \c 0 if empty. */
+       value_type last() const;
+
+       /** Clear the queue. */
+       void clear();
+
+       /** Remove all occurances of \a val_r from the queue. */
+       void remove( value_type val_r );
+
+       /** Push a value to the end off the Queue. */
+       void push( value_type val_r );
+       /** \overload */
+       void push_back( value_type val_r )
+       { push( val_r ); }
+
+       /** Pop and return the last Id from the queue or \c 0 if empty. */
+       value_type pop();
+       /** \overload */
+       value_type pop_back()
+       { return pop(); }
+
+       /** Push a value to the beginning off the Queue. */
+       void push_front( value_type val_r );
+
+       /** Pop and return the 1st Id from the queue or \c 0 if empty. */
+       value_type pop_front();
+
+     public:
+       /** Backdoor */
+       operator struct ::_Queue *()
+       { return _pimpl; }
+       /** Backdoor */
+       operator const struct ::_Queue *() const
+       { return _pimpl; }
+
+      private:
+        /** Pointer to implementation */
+       struct ::_Queue * _pimpl;
+    };
+
+    /** \relates Queue Stream output */
+    std::ostream & operator<<( std::ostream & str, const Queue & obj );
+
+    /** \relates Queue Verbose stream output */
+    std::ostream & dumpOn( std::ostream & str, const Queue & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_QUEUE_H
diff --git a/zypp/sat/SolvAttr.cc b/zypp/sat/SolvAttr.cc
new file mode 100644 (file)
index 0000000..0b773ad
--- /dev/null
@@ -0,0 +1,168 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/SolvAttr.cc
+ *
+*/
+extern "C"
+{
+#include <satsolver/knownid.h>
+}
+
+#include <iostream>
+
+#include "zypp/base/String.h"
+#include "zypp/sat/SolvAttr.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace sat
+{ /////////////////////////////////////////////////////////////////
+
+  const SolvAttr SolvAttr::allAttr( detail::noId );
+  const SolvAttr SolvAttr::noAttr;
+
+#warning STILL ATTRIBUTES HERE WHICH ARE NOT PROVIDED BY SOLV FILES
+// At least the ones that do nat have a satsolver/knownid.
+
+  const SolvAttr SolvAttr::name         ( SOLVABLE_NAME );
+  const SolvAttr SolvAttr::edition      ( SOLVABLE_EVR );
+  const SolvAttr SolvAttr::arch         ( SOLVABLE_ARCH );
+  const SolvAttr SolvAttr::vendor      ( SOLVABLE_VENDOR );
+
+  const SolvAttr SolvAttr::provides    ( SOLVABLE_PROVIDES );
+  const SolvAttr SolvAttr::obsoletes   ( SOLVABLE_OBSOLETES );
+  const SolvAttr SolvAttr::conflicts   ( SOLVABLE_CONFLICTS );
+  const SolvAttr SolvAttr::requires    ( SOLVABLE_REQUIRES );
+  const SolvAttr SolvAttr::recommends  ( SOLVABLE_RECOMMENDS );
+  const SolvAttr SolvAttr::suggests    ( SOLVABLE_SUGGESTS );
+  const SolvAttr SolvAttr::supplements ( SOLVABLE_SUPPLEMENTS );
+  const SolvAttr SolvAttr::enhances    ( SOLVABLE_ENHANCES );
+
+  const SolvAttr SolvAttr::summary      ( SOLVABLE_SUMMARY );       // translated
+  const SolvAttr SolvAttr::description  ( SOLVABLE_DESCRIPTION );   // translated
+  const SolvAttr SolvAttr::insnotify    ( SOLVABLE_MESSAGEINS );    // translated
+  const SolvAttr SolvAttr::delnotify    ( SOLVABLE_MESSAGEDEL );    // translated
+  const SolvAttr SolvAttr::eula                ( SOLVABLE_EULA );          // translated
+  const SolvAttr SolvAttr::cpeid        ( SOLVABLE_CPEID );
+  const SolvAttr SolvAttr::installtime  ( SOLVABLE_INSTALLTIME );
+  const SolvAttr SolvAttr::buildtime    ( SOLVABLE_BUILDTIME );
+  const SolvAttr SolvAttr::installsize  ( SOLVABLE_INSTALLSIZE );
+  const SolvAttr SolvAttr::downloadsize ( SOLVABLE_DOWNLOADSIZE );
+  const SolvAttr SolvAttr::diskusage    ( SOLVABLE_DISKUSAGE );
+
+  //package
+  const SolvAttr SolvAttr::checksum     ( SOLVABLE_CHECKSUM );
+  const SolvAttr SolvAttr::medianr     ( SOLVABLE_MEDIANR );
+  const SolvAttr SolvAttr::mediafile   ( SOLVABLE_MEDIAFILE );
+  const SolvAttr SolvAttr::mediadir    ( SOLVABLE_MEDIADIR );
+  const SolvAttr SolvAttr::changelog    ( "changelog" );
+  const SolvAttr SolvAttr::buildhost    ( SOLVABLE_BUILDHOST );
+  const SolvAttr SolvAttr::distribution ( SOLVABLE_DISTRIBUTION );
+  const SolvAttr SolvAttr::license      ( SOLVABLE_LICENSE );
+  const SolvAttr SolvAttr::packager     ( SOLVABLE_PACKAGER );
+  const SolvAttr SolvAttr::group        ( SOLVABLE_GROUP );
+  const SolvAttr SolvAttr::keywords     ( SOLVABLE_KEYWORDS );
+  const SolvAttr SolvAttr::sourcesize   ( "sourcesize" );
+  const SolvAttr SolvAttr::authors      ( SOLVABLE_AUTHORS );
+  const SolvAttr SolvAttr::filelist     ( SOLVABLE_FILELIST );
+  const SolvAttr SolvAttr::sourcearch   ( SOLVABLE_SOURCEARCH );
+  const SolvAttr SolvAttr::sourcename   ( SOLVABLE_SOURCENAME );
+  const SolvAttr SolvAttr::sourceevr    ( SOLVABLE_SOURCEEVR );
+  const SolvAttr SolvAttr::headerend    ( SOLVABLE_HEADEREND );
+  const SolvAttr SolvAttr::url          ( SOLVABLE_URL );
+
+  // patch
+  const SolvAttr SolvAttr::patchcategory            ( SOLVABLE_PATCHCATEGORY );
+  const SolvAttr SolvAttr::rebootSuggested          ( UPDATE_REBOOT );
+  const SolvAttr SolvAttr::restartSuggested         ( UPDATE_RESTART );
+  const SolvAttr SolvAttr::reloginSuggested         ( UPDATE_RELOGIN );
+  const SolvAttr SolvAttr::message                  ( UPDATE_MESSAGE );
+  const SolvAttr SolvAttr::updateCollection         ( UPDATE_COLLECTION );
+  const SolvAttr SolvAttr::updateCollectionName     ( UPDATE_COLLECTION_NAME );
+  const SolvAttr SolvAttr::updateCollectionEvr      ( UPDATE_COLLECTION_EVR );
+  const SolvAttr SolvAttr::updateCollectionArch     ( UPDATE_COLLECTION_ARCH );
+  const SolvAttr SolvAttr::updateCollectionFilename ( UPDATE_COLLECTION_FILENAME );
+  const SolvAttr SolvAttr::updateCollectionFlags    ( UPDATE_COLLECTION_FLAGS );
+  const SolvAttr SolvAttr::updateReference          ( UPDATE_REFERENCE );
+  const SolvAttr SolvAttr::updateReferenceType      ( UPDATE_REFERENCE_TYPE );
+  const SolvAttr SolvAttr::updateReferenceHref      ( UPDATE_REFERENCE_HREF );
+  const SolvAttr SolvAttr::updateReferenceId        ( UPDATE_REFERENCE_ID );
+  const SolvAttr SolvAttr::updateReferenceTitle     ( UPDATE_REFERENCE_TITLE );
+
+  //pattern
+  const SolvAttr SolvAttr::isvisible    ( SOLVABLE_ISVISIBLE );
+  const SolvAttr SolvAttr::icon         ( SOLVABLE_ICON );
+  const SolvAttr SolvAttr::order        ( SOLVABLE_ORDER );
+  const SolvAttr SolvAttr::isdefault    ( "isdefault" );
+  const SolvAttr SolvAttr::category     ( SOLVABLE_CATEGORY );    // translated
+  const SolvAttr SolvAttr::script       ( "script" );
+  const SolvAttr SolvAttr::includes     ( SOLVABLE_INCLUDES );
+  const SolvAttr SolvAttr::extends      ( SOLVABLE_EXTENDS );
+
+  // product
+  const SolvAttr SolvAttr::productReferenceFile  ( PRODUCT_REFERENCEFILE );
+  const SolvAttr SolvAttr::productProductLine    ( PRODUCT_PRODUCTLINE );
+  const SolvAttr SolvAttr::productShortlabel     ( PRODUCT_SHORTLABEL );
+  const SolvAttr SolvAttr::productDistproduct    ( PRODUCT_DISTPRODUCT );
+  const SolvAttr SolvAttr::productDistversion    ( PRODUCT_DISTVERSION );
+  const SolvAttr SolvAttr::productType           ( PRODUCT_TYPE );
+  const SolvAttr SolvAttr::productFlags          ( PRODUCT_FLAGS );
+  const SolvAttr SolvAttr::productRegisterTarget ( PRODUCT_REGISTER_TARGET );
+  const SolvAttr SolvAttr::productRegisterRelease( PRODUCT_REGISTER_RELEASE );
+  const SolvAttr SolvAttr::productUrl            ( PRODUCT_URL );
+  const SolvAttr SolvAttr::productUrlType        ( PRODUCT_URL_TYPE );
+
+  // repository
+  const SolvAttr SolvAttr::repositoryTimestamp   ( REPOSITORY_TIMESTAMP );
+  const SolvAttr SolvAttr::repositoryExpire      ( REPOSITORY_EXPIRE );
+  /** array of repositoryProductLabel repositoryProductCpeid pairs */
+  const SolvAttr SolvAttr::repositoryUpdates     ( REPOSITORY_UPDATES );
+  /** array of repositoryProductLabel repositoryProductCpeid pairs */
+  const SolvAttr SolvAttr::repositoryDistros     ( REPOSITORY_DISTROS );
+  const SolvAttr SolvAttr::repositoryProductLabel( REPOSITORY_PRODUCT_LABEL );
+  const SolvAttr SolvAttr::repositoryProductCpeid( REPOSITORY_PRODUCT_CPEID );
+  const SolvAttr SolvAttr::repositoryKeywords    ( REPOSITORY_KEYWORDS );
+
+  const SolvAttr SolvAttr::repositoryAddedFileProvides( REPOSITORY_ADDEDFILEPROVIDES );
+  const SolvAttr SolvAttr::repositoryRpmDbCookie      ( REPOSITORY_RPMDBCOOKIE );
+  const SolvAttr SolvAttr::repositoryDeltaInfo        ( REPOSITORY_DELTAINFO );
+
+  const SolvAttr SolvAttr::repositoryToolVersion       ( REPOSITORY_TOOLVERSION );
+
+  /////////////////////////////////////////////////////////////////
+
+  SolvAttr SolvAttr::parent() const
+  {
+    switch( id() )
+    {
+      case UPDATE_COLLECTION_NAME:
+      case UPDATE_COLLECTION_EVR:
+      case UPDATE_COLLECTION_ARCH:
+      case UPDATE_COLLECTION_FILENAME:
+      case UPDATE_COLLECTION_FLAGS:
+        return updateCollection;
+        break;
+
+      case UPDATE_REFERENCE_TYPE:
+      case UPDATE_REFERENCE_HREF:
+      case UPDATE_REFERENCE_ID:
+      case UPDATE_REFERENCE_TITLE:
+        return updateReference;
+        break;
+    }
+    return noAttr;
+  }
+
+} // namespace sat
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/SolvAttr.h b/zypp/sat/SolvAttr.h
new file mode 100644 (file)
index 0000000..a56334c
--- /dev/null
@@ -0,0 +1,201 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/SolvAttr.h
+ *
+*/
+#ifndef ZYPP_SAT_SOLVATTR_H
+#define ZYPP_SAT_SOLVATTR_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/String.h"
+#include "zypp/IdStringType.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace sat
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : SolvAttr
+  //
+  /** Solvable attribute keys.
+   *
+   * Attributes associated with individual solvables,
+   * or with the repository as a whole.
+   *
+   * \note If you add well known subsructure attributes, update \ref parent.
+   *
+   * \see \ref LookupAttr
+   */
+  class SolvAttr : public IdStringType<SolvAttr>
+  {
+    public:
+      /** \name Some builtin SolvAttr constants. */
+      //@{
+      /** Value to request searching all Attributes (0). */
+      static const SolvAttr allAttr;
+      /** Value representing \c noAttr (<tt>""</tt>)*/
+      static const SolvAttr noAttr;
+
+      /** \name special solvable attributes which are part of the ::Solvable struct */
+      //@{
+      static const SolvAttr name;
+      static const SolvAttr edition;
+      static const SolvAttr arch;
+      static const SolvAttr vendor;
+      //@}
+
+      /** \name dependency attributes */
+      //@{
+      static const SolvAttr provides;
+      static const SolvAttr obsoletes;
+      static const SolvAttr conflicts;
+      static const SolvAttr requires;
+      static const SolvAttr recommends;
+      static const SolvAttr suggests;
+      static const SolvAttr supplements;
+      static const SolvAttr enhances;
+
+      /** \name common */
+      //@{
+      static const SolvAttr summary;
+      static const SolvAttr description;
+      static const SolvAttr insnotify;
+      static const SolvAttr delnotify;
+      static const SolvAttr eula;
+      static const SolvAttr installtime;
+      static const SolvAttr buildtime;
+      static const SolvAttr installsize;
+      static const SolvAttr downloadsize;
+      static const SolvAttr diskusage;
+      static const SolvAttr cpeid;
+      //@}
+
+      /** \name package */
+      //@{
+      static const SolvAttr checksum;
+      static const SolvAttr mediadir;
+      static const SolvAttr medianr;
+      static const SolvAttr mediafile;
+      static const SolvAttr changelog;
+      static const SolvAttr buildhost;
+      static const SolvAttr distribution;
+      static const SolvAttr license;
+      static const SolvAttr packager;
+      static const SolvAttr group;
+      static const SolvAttr keywords;
+      static const SolvAttr sourcesize;
+      static const SolvAttr authors;
+      static const SolvAttr filelist;
+      static const SolvAttr sourcearch;
+      static const SolvAttr sourcename;
+      static const SolvAttr sourceevr;
+      static const SolvAttr headerend;
+      static const SolvAttr url;
+      //@}
+
+       /** \name patch */
+      //@{
+      static const SolvAttr patchcategory;
+      static const SolvAttr rebootSuggested;
+      static const SolvAttr restartSuggested;
+      static const SolvAttr reloginSuggested;
+      static const SolvAttr message;
+      static const SolvAttr updateCollection;          // SUB-STRUCTURE:
+      static const SolvAttr updateCollectionName;      // name
+      static const SolvAttr updateCollectionEvr;       // evr
+      static const SolvAttr updateCollectionArch;      // arch
+      static const SolvAttr updateCollectionFilename;  // filename
+      static const SolvAttr updateCollectionFlags;     // flags
+      static const SolvAttr updateReference;           // SUB-STRUCTURE:
+      static const SolvAttr updateReferenceType;       // type
+      static const SolvAttr updateReferenceHref;       // href
+      static const SolvAttr updateReferenceId;         // id
+      static const SolvAttr updateReferenceTitle;      // title
+      //@}
+
+      /** \name pattern */
+      //@{
+      static const SolvAttr isvisible;
+      static const SolvAttr icon;
+      static const SolvAttr order;
+      static const SolvAttr isdefault;
+      static const SolvAttr category;
+      static const SolvAttr script;
+      static const SolvAttr includes;
+      static const SolvAttr extends;
+      //@}
+
+      /** \name product */
+      //@{
+      static const SolvAttr productReferenceFile;
+      static const SolvAttr productProductLine;
+      static const SolvAttr productShortlabel;
+      static const SolvAttr productDistproduct;
+      static const SolvAttr productDistversion;
+      static const SolvAttr productType;
+      static const SolvAttr productFlags;
+      static const SolvAttr productRegisterTarget;
+      static const SolvAttr productRegisterRelease;
+      static const SolvAttr productUrl;
+      static const SolvAttr productUrlType;
+
+      //@}
+
+      /** \name repository */
+      //@{
+      static const SolvAttr repositoryTimestamp;
+      static const SolvAttr repositoryExpire;
+      static const SolvAttr repositoryKeywords;
+      static const SolvAttr repositoryUpdates;
+      static const SolvAttr repositoryDistros;
+      static const SolvAttr repositoryProductLabel;
+      static const SolvAttr repositoryProductCpeid;
+      static const SolvAttr repositoryRevision;
+      static const SolvAttr repositoryAddedFileProvides;
+      static const SolvAttr repositoryRpmDbCookie;
+      static const SolvAttr repositoryDeltaInfo;
+      static const SolvAttr repositoryToolVersion;
+      //@}
+
+      //@}
+    public:
+      /** Default ctor: \ref noAttr */
+      SolvAttr() {}
+
+      /** Ctor taking kind as string. */
+      explicit SolvAttr( sat::detail::IdType id_r )  : _str( id_r ) {}
+      explicit SolvAttr( const IdString & idstr_r )  : _str( idstr_r ) {}
+      explicit SolvAttr( const std::string & str_r ) : _str( str_r ) {}
+      explicit SolvAttr( const char * cstr_r )       : _str( cstr_r ) {}
+
+      /** Return the parent of well know sub-structure attributes (\ref SolvAttr::noAttr if none).
+       * \li \ref updateCollection
+       * \li \ref updateReference
+       */
+      SolvAttr parent() const;
+
+      /** Whether this is a well know sub-structure attribute. */
+      bool hasParent() const
+      { return parent() != noAttr; }
+
+    private:
+      friend class IdStringType<SolvAttr>;
+      IdString _str;
+  };
+
+  /////////////////////////////////////////////////////////////////
+} // namespace sat
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_sat_SolvAttr_H
diff --git a/zypp/sat/SolvIterMixin.cc b/zypp/sat/SolvIterMixin.cc
new file mode 100644 (file)
index 0000000..57bde4a
--- /dev/null
@@ -0,0 +1,69 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/SolvIterMixin.cc
+ *
+*/
+//#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/ResPoolProxy.h"
+#include "zypp/pool/PoolTraits.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    namespace solvitermixin_detail
+    {
+      bool UnifyByIdent::operator()( const Solvable & solv_r ) const
+      {
+        // Need to use pool::ByIdent because packages and srcpackages have the same id.
+        return( solv_r && _uset->insert( pool::ByIdent( solv_r ).get() ).second );
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // asSolvable
+    ///////////////////////////////////////////////////////////////////
+    Solvable asSolvable::operator()( const PoolItem & pi_r ) const
+    {
+      return pi_r.satSolvable();
+    }
+
+    Solvable asSolvable::operator()( const ResObject_constPtr & res_r ) const
+    {
+      return res_r ? res_r->satSolvable() : Solvable();
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    Selectable_Ptr asSelectable::operator()( const sat::Solvable & sov_r ) const
+    {
+      return ResPool::instance().proxy().lookup( sov_r );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/SolvIterMixin.h b/zypp/sat/SolvIterMixin.h
new file mode 100644 (file)
index 0000000..811d60e
--- /dev/null
@@ -0,0 +1,209 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/SolvIterMixin.h
+ *
+*/
+#ifndef ZYPP_SAT_SOLVITERMIXIN_H
+#define ZYPP_SAT_SOLVITERMIXIN_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/sat/Solvable.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class PoolItem;
+  class asPoolItem; // transform functor
+
+  namespace ui
+  {
+    class asSelectable; // transform functor
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    class Solvable;
+    class asSolvable; // transform functor
+
+    namespace solvitermixin_detail
+    {
+      /** Unify by \c ident \c (kind:name).
+       * Return true on the 1st appearance of a new \c ident. This is
+       * used in \ref SolvIterMixin when mapping a  Solvable iterator
+       * to a Selectable iterator.
+      */
+      struct UnifyByIdent
+      {
+        bool operator()( const Solvable & solv_r ) const;
+
+        typedef std::tr1::unordered_set<unsigned> Uset;
+        UnifyByIdent()
+          : _uset( new Uset )
+        {}
+        shared_ptr<Uset> _uset;
+      };
+
+
+    } // namespace solvitermixin_detail
+
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SolvIterMixin<Derived,DerivedSolvable_iterator>
+    //
+    /** Base class providing common iterator types based on a \ref Solvable iterator.
+     *
+     * A class deriving from \ref SolvIterMixin must provide two methods
+     * \c begin and \c end returning iterator over \ref sat::Solvable.
+     *
+     * \ref SolvIterMixin will then provide iterators over the corresponding
+     * \ref PoolItem and \ref ui::Selectable_Ptr.
+     *
+     * \ref SolvIterMixin will also provide default implementations for \ref empty
+     * and \ref size by iterating from \c begin to \c end. In case \c Derived is
+     * able to provide a more efficient implementation, the methods should be overloaded.
+     *
+     * \note You will sometimes face the problem, that when using the \ref PoolItem
+     * iterator you hit multiple version of the same package, while when using the
+     * \ref ui::Selectable iterator the information which of the available candidates
+     * actually matched got lost. In this case class \ref PoolItemBest may help you.
+     * Use it to pick the best version only.
+     *
+     * \code
+     *     namespace detail
+     *     {
+     *       class WhatProvidesIterator;
+     *     }
+     *
+     *     class WhatProvides : public SolvIterMixin<WhatProvides,detail::WhatProvidesIterator>
+     *     {
+     *       public:
+     *         typedef detail::WhatProvidesIterator const_iterator;
+     *
+     *         // Iterator pointing to the first Solvable.
+     *         const_iterator begin() const;
+     *
+     *         // Iterator pointing behind the last Solvable.
+     *         const_iterator end() const;
+     *
+     *     };
+     *
+     *     namespace detail
+     *     {
+     *       class WhatProvidesIterator : public boost::iterator_adaptor<
+     *           WhatProvidesIterator          // Derived
+     *          , const detail::IdType *       // Base
+     *          , const Solvable               // Value
+     *          , boost::forward_traversal_tag // CategoryOrTraversal
+     *          , const Solvable               // Reference
+     *       >
+     *       {
+     *          ...
+     *       };
+     *     }
+     * \endcode
+     * \ingroup g_CRTP
+     */
+    template <class Derived,class DerivedSolvable_iterator>
+    class SolvIterMixin
+    {
+      public:
+       typedef size_t size_type;
+
+      public:
+        /** \name Convenience methods.
+         * In case \c Derived is able to provide a more efficient implementation,
+        * the methods should be overloaded.
+        */
+        //@{
+       /** Whether the collection is epmty. */
+        bool empty() const
+        { return( self().begin() == self().end() ); }
+
+        /** Size of the collection. */
+        size_type size() const
+        { size_type s = 0; for_( it, self().begin(), self().end() ) ++s; return s;}
+
+       /** Whether collection contains a specific \ref Solvable. */
+       template<class _Solv>
+       bool contains( const _Solv & solv_r ) const
+       {
+         Solvable solv( asSolvable()( solv_r ) );
+         for_( it, self().begin(), self().end() )
+           if ( *it == solv )
+             return true;
+         return false;
+       }
+       //@}
+
+      public:
+        /** \name Iterate as Solvable */
+        //@{
+        typedef  DerivedSolvable_iterator Solvable_iterator;
+        Solvable_iterator solvableBegin() const
+        { return self().begin(); }
+        Solvable_iterator solvableEnd() const
+        { return self().end(); }
+        //@}
+
+        /** \name Iterate as PoolItem */
+        //@{
+        typedef transform_iterator<asPoolItem,Solvable_iterator> PoolItem_iterator;
+        PoolItem_iterator poolItemBegin() const
+        { return make_transform_iterator( solvableBegin(), asPoolItem() ); }
+        PoolItem_iterator poolItemEnd() const
+        { return make_transform_iterator( solvableEnd(), asPoolItem() ); }
+        //@}
+
+      private:
+        typedef filter_iterator<solvitermixin_detail::UnifyByIdent,Solvable_iterator> UnifiedSolvable_iterator;
+      public:
+        /** \name Iterate ui::Selectable::Ptr */
+        //@{
+        typedef transform_iterator<ui::asSelectable,UnifiedSolvable_iterator> Selectable_iterator;
+        Selectable_iterator selectableBegin() const
+        { return make_transform_iterator( unifiedSolvableBegin(), ui::asSelectable() ); }
+        Selectable_iterator selectableEnd() const
+        { return make_transform_iterator( unifiedSolvableEnd(), ui::asSelectable() ); }
+        //@}
+
+      private:
+        /** \name Iterate unified Solbvables to be transformed into Selectable. */
+        //@{
+        UnifiedSolvable_iterator unifiedSolvableBegin() const
+        { return make_filter_iterator( solvitermixin_detail::UnifyByIdent(), solvableBegin(), solvableEnd() ); }
+        UnifiedSolvable_iterator unifiedSolvableEnd() const
+        { return make_filter_iterator( solvitermixin_detail::UnifyByIdent(), solvableEnd(), solvableEnd() );; }
+        //@}
+      private:
+        const Derived & self() const
+        { return *static_cast<const Derived*>( this ); }
+      protected:
+        SolvIterMixin() {}
+        ~SolvIterMixin() {}
+        SolvIterMixin(const SolvIterMixin &) {}
+        void operator=(const SolvIterMixin &) {}
+     };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_SOLVITERMIXIN_H
diff --git a/zypp/sat/Solvable.cc b/zypp/sat/Solvable.cc
new file mode 100644 (file)
index 0000000..75fcf32
--- /dev/null
@@ -0,0 +1,671 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Solvable.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/Functional.h"
+#include "zypp/base/Collector.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/LookupAttr.h"
+
+#include "zypp/Repository.h"
+#include "zypp/OnMediaLocation.h"
+#include "zypp/ZConfig.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    namespace
+    {
+      void _doSplit( IdString & _ident, ResKind & _kind, IdString & _name )
+      {
+        if ( ! _ident )
+          return;
+
+       ResKind explicitKind = Solvable::SplitIdent::explicitKind( _ident.c_str() );
+       // NOTE: kind package and srcpackage do not have namespaced ident!
+       if ( ! explicitKind  )
+       {
+          _name = _ident;
+         // No kind defaults to package
+         if ( !_kind )
+           _kind = ResKind::package;
+         if ( ! ( _kind == ResKind::package || _kind == ResKind::srcpackage ) )
+           _ident = IdString( str::form( "%s:%s", _kind.c_str(), _ident.c_str() ) );
+       }
+       else
+       {
+         // strip kind spec from name
+         _name = IdString( ::strchr( _ident.c_str(), ':' )+1 );
+         _kind = explicitKind;
+         if ( _kind == ResKind::package || _kind == ResKind::srcpackage )
+           _ident = _name;
+       }
+       return;
+      }
+    }
+
+    Solvable::SplitIdent::SplitIdent( IdString ident_r )
+    : _ident( ident_r )
+    { _doSplit( _ident, _kind, _name ); }
+
+    Solvable::SplitIdent::SplitIdent( const char * ident_r )
+    : _ident( ident_r )
+    { _doSplit( _ident, _kind, _name ); }
+
+    Solvable::SplitIdent::SplitIdent( const std::string & ident_r )
+    : _ident( ident_r )
+    { _doSplit( _ident, _kind, _name ); }
+
+    Solvable::SplitIdent::SplitIdent( ResKind kind_r, IdString name_r )
+    : _ident( name_r )
+    , _kind( kind_r )
+    { _doSplit( _ident, _kind, _name ); }
+
+    Solvable::SplitIdent::SplitIdent( ResKind kind_r, const C_Str & name_r )
+    : _ident( name_r )
+    , _kind( kind_r )
+    { _doSplit( _ident, _kind, _name ); }
+
+    ResKind Solvable::SplitIdent::explicitKind( const char * ident_r )
+    {
+      if ( ! ident_r )
+       return ResKind();
+
+      const char * sep = ::strchr( ident_r, ':' );
+      if ( ! sep )
+       return ResKind();
+
+      ResKind ret;
+      if ( sep-ident_r >= 4 )
+      {
+       switch ( ident_r[3] )
+       {
+         #define OUTS(K,S) if ( !::strncmp( ident_r, ResKind::K.c_str(), S ) && ident_r[S] == ':' ) ret = ResKind::K
+         //             ----v
+         case 'c': OUTS( patch, 5 );       break;
+         case 'd': OUTS( product, 7 );     break;
+         case 'k': OUTS( package, 7 );     break;
+         case 'p': OUTS( srcpackage, 10 ); break;
+         case 't': OUTS( pattern, 7 );     break;
+         #undef OUTS
+       }
+      }
+      return ret;
+    }
+
+    /////////////////////////////////////////////////////////////////
+
+    const Solvable Solvable::noSolvable;
+
+    /////////////////////////////////////////////////////////////////
+
+    ::_Solvable * Solvable::get() const
+    { return myPool().getSolvable( _id ); }
+
+#define NO_SOLVABLE_RETURN( VAL ) \
+    ::_Solvable * _solvable( get() ); \
+    if ( ! _solvable ) return VAL
+
+    Solvable Solvable::nextInPool() const
+    { return Solvable( myPool().getNextId( _id ) ); }
+
+    Solvable Solvable::nextInRepo() const
+    {
+      NO_SOLVABLE_RETURN( noSolvable );
+      for ( detail::SolvableIdType next = _id+1; next < unsigned(_solvable->repo->end); ++next )
+      {
+        ::_Solvable * nextS( myPool().getSolvable( next ) );
+        if ( nextS && nextS->repo == _solvable->repo )
+        {
+          return Solvable( next );
+        }
+      }
+      return noSolvable;
+    }
+
+    Repository Solvable::repository() const
+    {
+      NO_SOLVABLE_RETURN( Repository::noRepository );
+      return Repository( _solvable->repo );
+    }
+
+    bool Solvable::isSystem() const
+    {
+      NO_SOLVABLE_RETURN( _id == detail::systemSolvableId );
+      return myPool().isSystemRepo( _solvable->repo );
+    }
+
+    bool Solvable::onSystemByUser() const
+    {
+      return isSystem() && myPool().isOnSystemByUser( ident() );
+    }
+
+    IdString Solvable::ident() const
+    {
+      NO_SOLVABLE_RETURN( IdString() );
+      return IdString( _solvable->name );
+    }
+
+    std::string Solvable::lookupStrAttribute( const SolvAttr & attr ) const
+    {
+      NO_SOLVABLE_RETURN( std::string() );
+      const char * s = ::solvable_lookup_str( _solvable, attr.id() );
+      return s ? s : std::string();
+    }
+
+    std::string Solvable::lookupStrAttribute( const SolvAttr & attr, const Locale & lang_r ) const
+    {
+      NO_SOLVABLE_RETURN( std::string() );
+      const char * s = 0;
+      if ( lang_r == Locale::noCode )
+      {
+        s = ::solvable_lookup_str_poollang( _solvable, attr.id() );
+      }
+      else
+      {
+       for ( Locale l( lang_r ); l != Locale::noCode; l = l.fallback() )
+         if ( (s = ::solvable_lookup_str_lang( _solvable, attr.id(), l.code().c_str(), 0 )) )
+           return s;
+         // here: no matching locale, so use default
+         s = ::solvable_lookup_str_lang( _solvable, attr.id(), 0, 0 );
+      }
+      return s ? s : std::string();
+   }
+
+    unsigned Solvable::lookupNumAttribute( const SolvAttr & attr ) const
+    {
+      NO_SOLVABLE_RETURN( 0 );
+      return ::solvable_lookup_num( _solvable, attr.id(), 0 );
+    }
+
+    bool Solvable::lookupBoolAttribute( const SolvAttr & attr ) const
+    {
+      NO_SOLVABLE_RETURN( false );
+      return ::solvable_lookup_bool( _solvable, attr.id() );
+    }
+
+    detail::IdType Solvable::lookupIdAttribute( const SolvAttr & attr ) const
+    {
+      NO_SOLVABLE_RETURN( detail::noId );
+      return ::solvable_lookup_id( _solvable, attr.id() );
+    }
+
+    CheckSum Solvable::lookupCheckSumAttribute( const SolvAttr & attr ) const
+    {
+      NO_SOLVABLE_RETURN( CheckSum() );
+      detail::IdType chksumtype = 0;
+      const char * s = ::solvable_lookup_checksum( _solvable, attr.id(), &chksumtype );
+      if ( ! s )
+        return CheckSum();
+      switch ( chksumtype )
+      {
+        case REPOKEY_TYPE_MD5:    return CheckSum::md5( s );
+        case REPOKEY_TYPE_SHA1:   return CheckSum::sha1( s );
+        case REPOKEY_TYPE_SHA256: return CheckSum::sha256( s );
+      }
+      return CheckSum( std::string(), s ); // try to autodetect
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    {
+      inline Pathname lookupDatadirIn( Repository repor_r )
+      {
+        static const sat::SolvAttr susetagsDatadir( "susetags:datadir" );
+        Pathname ret;
+        // First look for repo attribute "susetags:datadir". If not found,
+        // look into the solvables as Code11 satsolver placed it there.
+        sat::LookupRepoAttr datadir( susetagsDatadir, repor_r );
+        if ( ! datadir.empty() )
+          ret = datadir.begin().asString();
+        else
+        {
+          sat::LookupAttr datadir( susetagsDatadir, repor_r );
+          if ( ! datadir.empty() )
+            ret = datadir.begin().asString();
+        }
+        return ret;
+      }
+    }
+    ///////////////////////////////////////////////////////////////////
+
+    OnMediaLocation Solvable::lookupLocation() const
+    {
+      NO_SOLVABLE_RETURN( OnMediaLocation() );
+      // medianumber and path
+      unsigned medianr;
+      char * file = ::solvable_get_location( _solvable, &medianr );
+      if ( ! file )
+        return OnMediaLocation();
+
+      OnMediaLocation ret;
+
+      Pathname path;
+      switch ( repository().info().type().toEnum() )
+      {
+        case repo::RepoType::NONE_e:
+        {
+          path = lookupDatadirIn( repository() );
+          if ( ! path.empty() )
+            repository().info().setProbedType( repo::RepoType::YAST2_e );
+        }
+        break;
+
+        case repo::RepoType::YAST2_e:
+        {
+          path = lookupDatadirIn( repository() );
+          if ( path.empty() )
+            path = "suse";
+        }
+        break;
+
+        default:
+          break;
+      }
+      ret.setLocation    ( path/file, medianr );
+      ret.setDownloadSize( ByteCount( lookupNumAttribute( SolvAttr::downloadsize ), ByteCount::K ) );
+      ret.setChecksum    ( lookupCheckSumAttribute( SolvAttr::checksum ) );
+      // Not needed/available for solvables?
+      //ret.setOpenSize    ( ByteCount( lookupNumAttribute( SolvAttr::opensize ), ByteCount::K ) );
+      //ret.setOpenChecksum( lookupCheckSumAttribute( SolvAttr::openchecksum ) );
+      return ret;
+    }
+
+    ResKind Solvable::kind() const
+    {
+      NO_SOLVABLE_RETURN( ResKind() );
+      // detect srcpackages by 'arch'
+      switch ( _solvable->arch )
+      {
+        case ARCH_SRC:
+        case ARCH_NOSRC:
+          return ResKind::srcpackage;
+          break;
+      }
+
+      const char * ident = IdString( _solvable->name ).c_str();
+      const char * sep = ::strchr( ident, ':' );
+
+      // no ':' in package names (hopefully)
+      if ( ! sep )
+        return ResKind::package;
+
+      // quick check for well known kinds
+      if ( sep-ident >= 4 )
+      {
+        switch ( ident[3] )
+        {
+#define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) return ResKind::K
+          //             ----v
+          case 'c': OUTS( patch, 5 );       break;
+          case 'd': OUTS( product, 7 );     break;
+          case 'k': OUTS( package, 7 );     break;
+          case 'p': OUTS( srcpackage, 10 ); break;
+          case 't': OUTS( pattern, 7 );     break;
+#undef OUTS
+        }
+      }
+
+      // an unknown kind
+      return ResKind( std::string( ident, sep-ident ) );
+    }
+
+    bool Solvable::isKind( const ResKind & kind_r ) const
+    {
+      NO_SOLVABLE_RETURN( false );
+
+      // detect srcpackages by 'arch'
+      switch ( _solvable->arch )
+      {
+        case ARCH_SRC:
+        case ARCH_NOSRC:
+          return( kind_r == ResKind::srcpackage );
+          break;
+      }
+
+      // no ':' in package names (hopefully)
+      const char * ident = IdString( _solvable->name ).c_str();
+      if ( kind_r == ResKind::package )
+      {
+        return( ::strchr( ident, ':' ) == 0 );
+      }
+
+      // look for a 'kind:' prefix
+      const char * kind = kind_r.c_str();
+      unsigned     ksize = ::strlen( kind );
+      return( ::strncmp( ident, kind, ksize ) == 0
+              && ident[ksize] == ':' );
+    }
+
+    std::string Solvable::name() const
+    {
+      NO_SOLVABLE_RETURN( std::string() );
+      const char * ident = IdString( _solvable->name ).c_str();
+      const char * sep = ::strchr( ident, ':' );
+      return( sep ? sep+1 : ident );
+    }
+
+    Edition Solvable::edition() const
+    {
+      NO_SOLVABLE_RETURN( Edition() );
+      return Edition( _solvable->evr );
+    }
+
+    Arch Solvable::arch() const
+    {
+      NO_SOLVABLE_RETURN( Arch_noarch ); //ArchId() );
+      switch ( _solvable->arch )
+      {
+        case ARCH_SRC:
+        case ARCH_NOSRC:
+          return Arch_noarch; //ArchId( ARCH_NOARCH );
+          break;
+      }
+      return Arch( IdString(_solvable->arch).asString() );
+      //return ArchId( _solvable->arch );
+    }
+
+    bool Solvable::multiversionInstall() const
+    {
+      return myPool().isMultiversion( ident() );
+    }
+
+    IdString Solvable::vendor() const
+    {
+      NO_SOLVABLE_RETURN( IdString() );
+      return IdString( _solvable->vendor );
+    }
+
+    Capabilities Solvable::operator[]( Dep which_r ) const
+    {
+      switch( which_r.inSwitch() )
+      {
+        case Dep::PROVIDES_e:    return provides();    break;
+        case Dep::REQUIRES_e:    return requires();    break;
+        case Dep::CONFLICTS_e:   return conflicts();   break;
+        case Dep::OBSOLETES_e:   return obsoletes();   break;
+        case Dep::RECOMMENDS_e:  return recommends();  break;
+        case Dep::SUGGESTS_e:    return suggests();    break;
+        case Dep::ENHANCES_e:    return enhances();    break;
+        case Dep::SUPPLEMENTS_e: return supplements(); break;
+        case Dep::PREREQUIRES_e: return prerequires(); break;
+      }
+      return Capabilities();
+    }
+
+    inline Capabilities _getCapabilities( detail::IdType * idarraydata_r, ::Offset offs_r )
+    {
+      return offs_r ? Capabilities( idarraydata_r + offs_r ) : Capabilities();
+    }
+    Capabilities Solvable::provides() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->provides );
+    }
+    Capabilities Solvable::requires() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->requires );
+    }
+    Capabilities Solvable::conflicts() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->conflicts );
+    }
+    Capabilities Solvable::obsoletes() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->obsoletes );
+    }
+    Capabilities Solvable::recommends() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->recommends );
+    }
+    Capabilities Solvable::suggests() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->suggests );
+    }
+    Capabilities Solvable::enhances() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->enhances );
+    }
+    Capabilities Solvable::supplements() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      return _getCapabilities( _solvable->repo->idarraydata, _solvable->supplements );
+    }
+    Capabilities Solvable::prerequires() const
+    {
+      NO_SOLVABLE_RETURN( Capabilities() );
+      // prerequires are a subset of requires
+       ::Offset offs = _solvable->requires;
+       return offs ? Capabilities( _solvable->repo->idarraydata + offs, detail::solvablePrereqMarker )
+                   : Capabilities();
+    }
+
+    CapabilitySet Solvable::providesNamespace( const std::string & namespace_r ) const
+    {
+      NO_SOLVABLE_RETURN( CapabilitySet() );
+      CapabilitySet ret;
+      Capabilities caps( provides() );
+      for_( it, caps.begin(), caps.end() )
+      {
+        CapDetail caprep( it->detail() );
+        if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
+          ret.insert( *it );
+      }
+      return ret;
+   }
+
+    CapabilitySet Solvable::valuesOfNamespace( const std::string & namespace_r ) const
+    {
+      NO_SOLVABLE_RETURN( CapabilitySet() );
+      CapabilitySet ret;
+      Capabilities caps( provides() );
+      for_( it, caps.begin(), caps.end() )
+      {
+        CapDetail caprep( it->detail() );
+        if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
+        {
+          std::string value( caprep.name().c_str()+namespace_r.size()+1 );
+          value[value.size()-1] = '\0'; // erase the trailing ')'
+          ret.insert( Capability( value, caprep.op(), caprep.ed() ) );
+        }
+      }
+      return ret;
+    }
+
+
+    std::string Solvable::asString() const
+    {
+      NO_SOLVABLE_RETURN( (_id == detail::systemSolvableId ? "systemSolvable" : "noSolvable") );
+      return str::form( "%s-%s.%s",
+                        IdString( _solvable->name ).c_str(),
+                        IdString( _solvable->evr ).c_str(),
+                        IdString( _solvable->arch ).c_str() );
+    }
+
+    bool Solvable::identical( Solvable rhs ) const
+    {
+      NO_SOLVABLE_RETURN( ! rhs.get() );
+      ::_Solvable * rhssolvable( rhs.get() );
+      return rhssolvable && ( _solvable == rhssolvable || ::solvable_identical( _solvable, rhssolvable ) );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+      /** Expand \ref Capability and call \c fnc_r for each namescpace:language
+       * dependency. Return #invocations of fnc_r, negative if fnc_r returned
+       * false to indicate abort.
+       */
+      int invokeOnEachSupportedLocale( Capability cap_r, function<bool (const Locale &)> fnc_r )
+      {
+        CapDetail detail( cap_r );
+        if ( detail.kind() == CapDetail::EXPRESSION )
+        {
+          switch ( detail.capRel() )
+          {
+            case CapDetail::CAP_AND:
+            case CapDetail::CAP_OR:
+                // expand
+              {
+                int res = invokeOnEachSupportedLocale( detail.lhs(), fnc_r );
+                if ( res < 0 )
+                  return res; // negative on abort.
+                int res2 = invokeOnEachSupportedLocale( detail.rhs(), fnc_r );
+                if ( res2 < 0 )
+                  return -res + res2; // negative on abort.
+                return res + res2;
+              }
+              break;
+
+            case CapDetail::CAP_NAMESPACE:
+              if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
+              {
+                return ( !fnc_r || fnc_r( Locale( IdString(detail.rhs().id()) ) ) ) ? 1 : -1; // negative on abort.
+              }
+              break;
+
+            case CapDetail::REL_NONE:
+            case CapDetail::CAP_WITH:
+            case CapDetail::CAP_ARCH:
+              break; // unwanted
+          }
+        }
+        return 0;
+      }
+
+       /** Expand \ref Capability and call \c fnc_r for each namescpace:language
+       * dependency. Return #invocations of fnc_r, negative if fnc_r returned
+       * false to indicate abort.
+       */
+      inline int invokeOnEachSupportedLocale( Capabilities cap_r, function<bool (const Locale &)> fnc_r )
+      {
+        int cnt = 0;
+        for_( cit, cap_r.begin(), cap_r.end() )
+        {
+          int res = invokeOnEachSupportedLocale( *cit, fnc_r );
+          if ( res < 0 )
+            return -cnt + res; // negative on abort.
+          cnt += res;
+        }
+        return cnt;
+      }
+      //@}
+
+      // Functor returning false if a Locale is in the set.
+      struct NoMatchIn
+      {
+        NoMatchIn( const LocaleSet & locales_r ) : _locales( locales_r ) {}
+
+        bool operator()( const Locale & locale_r ) const
+        {
+          return _locales.find( locale_r ) == _locales.end();
+        }
+
+        const LocaleSet & _locales;
+      };
+
+    } /////////////////////////////////////////////////////////////////
+
+    bool Solvable::supportsLocales() const
+    {
+      // false_c stops on 1st Locale.
+      return invokeOnEachSupportedLocale( supplements(), functor::false_c() ) < 0;
+    }
+
+    bool Solvable::supportsLocale( const Locale & locale_r ) const
+    {
+      // not_equal_to stops on == Locale.
+      return invokeOnEachSupportedLocale( supplements(), bind( std::not_equal_to<Locale>(), locale_r, _1 ) ) < 0;
+    }
+
+    bool Solvable::supportsLocale( const LocaleSet & locales_r ) const
+    {
+      if ( locales_r.empty() )
+        return false;
+      // NoMatchIn stops if Locale is included.
+      return invokeOnEachSupportedLocale( supplements(), NoMatchIn(locales_r) ) < 0;
+    }
+
+    bool Solvable::supportsRequestedLocales() const
+    { return supportsLocale( myPool().getRequestedLocales() ); }
+
+    void Solvable::getSupportedLocales( LocaleSet & locales_r ) const
+    {
+      invokeOnEachSupportedLocale( supplements(),
+                                   functor::Collector( std::inserter( locales_r, locales_r.begin() ) ) );
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Solvable & obj )
+    {
+      if ( ! obj )
+        return str << (obj.isSystem() ? "systemSolvable" : "noSolvable" );
+
+      return str << "(" << obj.id() << ")"
+          << ( obj.isKind( ResKind::srcpackage ) ? "srcpackage:" : "" ) << obj.ident()
+          << '-' << obj.edition() << '.' << obj.arch() << "("
+          << obj.repository().alias() << ")";
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : dumpOn
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & dumpOn( std::ostream & str, const Solvable & obj )
+    {
+      str << obj;
+      if ( obj )
+      {
+#define OUTS(X) if ( ! obj[Dep::X].empty() ) str << endl << " " #X " " << obj[Dep::X]
+        OUTS(PROVIDES);
+        OUTS(PREREQUIRES);
+        OUTS(REQUIRES);
+        OUTS(CONFLICTS);
+        OUTS(OBSOLETES);
+        OUTS(RECOMMENDS);
+        OUTS(SUGGESTS);
+        OUTS(ENHANCES);
+        OUTS(SUPPLEMENTS);
+#undef OUTS
+      }
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/Solvable.h b/zypp/sat/Solvable.h
new file mode 100644 (file)
index 0000000..a8cb87b
--- /dev/null
@@ -0,0 +1,421 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Solvable.h
+ *
+*/
+#ifndef ZYPP_SAT_SOLVABLE_H
+#define ZYPP_SAT_SOLVABLE_H
+
+#include <iosfwd>
+
+#include "zypp/base/SafeBool.h"
+
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/sat/SolvAttr.h"
+#include "zypp/ResTraits.h"
+#include "zypp/IdString.h"
+#include "zypp/Edition.h"
+#include "zypp/Arch.h"
+#include "zypp/Dep.h"
+#include "zypp/Capabilities.h"
+#include "zypp/Capability.h"
+#include "zypp/Locale.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class CheckSum;
+  class OnMediaLocation;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Solvable
+    //
+    /** A \ref Solvable object within the sat \ref Pool.
+     *
+     * \note Unfortunately libsatsolver combines the objects kind and
+     * name in a single identifier \c "pattern:kde_multimedia",
+     * \b except for packages and source packes. They are not prefixed
+     * by any kind string. Instead the architecture is abused to store
+     * \c "src" and \c "nosrc" values.
+     *
+     * \ref Solvable will hide this inconsistency by treating source
+     * packages as an own kind of solvable and map their arch to
+     * \ref Arch_noarch.
+     */
+    class Solvable : protected detail::PoolMember,
+                     private base::SafeBool<Solvable>
+    {
+      public:
+        typedef sat::detail::SolvableIdType IdType;
+
+      public:
+        /** Default ctor creates \ref noSolvable.*/
+        Solvable()
+        : _id( detail::noSolvableId ) {}
+
+        /** \ref PoolImpl ctor. */
+        explicit Solvable( IdType id_r )
+        : _id( id_r ) {}
+
+      public:
+        /** Represents no \ref Solvable. */
+        static const Solvable noSolvable;
+
+#ifndef SWIG // Swig treats it as syntax error
+        /** Evaluate \ref Solvable in a boolean context (\c != \c noSolvable). */
+        using base::SafeBool<Solvable>::operator bool_type;
+#endif
+
+        /** Return whether this \ref Solvable belongs to the system repo.
+         * \note This includes the otherwise hidden systemSolvable.
+        */
+        bool isSystem() const;
+
+       /** Whether this is known to be installed on behalf of a user request.
+        * \note This is a hint guessed by evaluating an available install history.
+        * Returns \c false for non-system (uninstalled) solvables, or if no history
+        * is available.
+        */
+       bool onSystemByUser() const;
+
+        /** The \ref Repository this \ref Solvable belongs to. */
+        Repository repository() const;
+
+      public:
+
+        /** \name Attribute lookup.
+         * \see \ref LookupAttr and  \ref ArrayAttr providing a general, more
+         * query like interface for attribute retrieval.
+        */
+        //@{
+        /**
+         * returns the string attribute value for \ref attr
+         * or an empty string if it does not exists.
+         */
+        std::string lookupStrAttribute( const SolvAttr & attr ) const;
+        /** \overload Trying to look up a translated string attribute.
+         *
+         * Returns the translation for \c lang_r.
+         *
+         * Passing an empty \ref Locale will return the string for the
+         * current default locale (\see \ref ZConfig::TextLocale),
+         * \b considering all fallback locales.
+         *
+         * Returns an empty string if no translation is available.
+        */
+        std::string lookupStrAttribute( const SolvAttr & attr, const Locale & lang_r ) const;
+
+        /**
+         * returns the numeric attribute value for \ref attr
+         * or 0 if it does not exists.
+         */
+        unsigned lookupNumAttribute( const SolvAttr & attr ) const;
+
+        /**
+         * returns the boolean attribute value for \ref attr
+         * or \c false if it does not exists.
+         */
+        bool lookupBoolAttribute( const SolvAttr & attr ) const;
+
+       /**
+         * returns the id attribute value for \ref attr
+         * or \ref detail::noId if it does not exists.
+         */
+        detail::IdType lookupIdAttribute( const SolvAttr & attr ) const;
+
+       /**
+         * returns the CheckSum attribute value for \ref attr
+         * or an empty CheckSum if ir does not exist.
+         */
+        CheckSum lookupCheckSumAttribute( const SolvAttr & attr ) const;
+
+        /**
+         * returns OnMediaLocation data: This is everything we need to
+         * download e.g. an rpm (path, checksum, downloadsize, etc.).
+         */
+        OnMediaLocation lookupLocation() const;
+
+        //@}
+      public:
+        /** The identifier.
+         * This is the solvables \ref name, \b except for packages and
+         * source packes, prefixed by it's \ref kind.
+         */
+        IdString     ident()    const;
+
+        ResKind      kind()     const;
+        /** Test whether a Solvable is of a certain \ref ResKind.
+        * The test is far cheaper than actually retriveing and
+         * comparing the \ref kind.
+        */
+        bool isKind( const ResKind & kind_r ) const;
+        /** \overload */
+        template<class _Res>
+        bool isKind() const
+        { return isKind( resKind<_Res>() ); }
+        /** \overload Extend the test to a range of \ref ResKind. */
+        template<class _Iterator>
+        bool isKind( _Iterator begin, _Iterator end )
+        { for_( it, begin, end ) if ( isKind( *it ) ) return true; return false; }
+
+        std::string  name()     const;
+        Edition      edition()  const;
+        Arch         arch()     const;
+
+        IdString     vendor()   const;
+
+        /** Whether different versions of this package can be installed at the same time.
+         * Per default \c false. \see also \ref ZConfig::multiversion.
+         */
+        bool         multiversionInstall() const;
+
+        /** String representation <tt>"ident-edition.arch"</tt> or \c "noSolvable"
+         * \code
+         *   product:openSUSE-11.1.x86_64
+         *   autoyast2-2.16.19-0.1.src
+         *   noSolvable
+         * \endcode
+        */
+        std::string asString() const;
+
+        /** Test whether two Solvables have the same content.
+         * Basically the same name, edition, arch, vendor and buildtime.
+         */
+        bool identical( Solvable rhs ) const;
+
+        /** Test for same name-version-release.arch */
+        bool sameNVRA( Solvable rhs ) const
+        { return( ident() == rhs.ident() && edition() == rhs.edition() && arch() == rhs.arch() ); }
+
+     public:
+
+        /** \name Access to the \ref Solvable dependencies.
+         *
+         * \note Prerequires are a subset of requires.
+         */
+        //@{
+        Capabilities operator[]( Dep which_r ) const;
+
+        Capabilities provides()    const;
+        Capabilities requires()    const;
+        Capabilities conflicts()   const;
+        Capabilities obsoletes()   const;
+        Capabilities recommends()  const;
+        Capabilities suggests()    const;
+        Capabilities enhances()    const;
+        Capabilities supplements() const;
+        Capabilities prerequires() const;
+
+        /** Return the namespaced provides <tt>'namespace([value])[ op edition]'</tt> of this Solvable. */
+        CapabilitySet providesNamespace( const std::string & namespace_r ) const;
+
+        /** Return <tt>'value[ op edition]'</tt> for namespaced provides <tt>'namespace(value)[ op edition]'</tt>.
+         * Similar to \ref providesNamespace, but the namespace is stripped from the
+         * dependencies. This is convenient if the namespace denotes packages that
+         * should be looked up. E.g. the \c weakremover namespace used in a products
+         * release package denotes the packages that were dropped from the distribution.
+         * \see \ref Product::droplist
+         */
+        CapabilitySet valuesOfNamespace( const std::string & namespace_r ) const;
+        //@}
+
+      public:
+        /** \name Locale support. */
+        //@{
+        /** Whether this \c Solvable claims to support locales. */
+        bool supportsLocales() const;
+        /** Whether this \c Solvable supports a specific \ref Locale. */
+        bool supportsLocale( const Locale & locale_r ) const;
+        /** Whether this \c Solvable supports at least one of the specified locales. */
+        bool supportsLocale( const LocaleSet & locales_r ) const;
+        /** Whether this \c Solvable supports at least one requested locale.
+         * \see \ref Pool::setRequestedLocales
+        */
+        bool supportsRequestedLocales() const;
+        /** Return the supported locales via locales_r. */
+        void getSupportedLocales( LocaleSet & locales_r ) const;
+        /** \overload */
+        LocaleSet getSupportedLocales() const
+        { LocaleSet ret; getSupportedLocales( ret ); return ret; }
+        //@}
+
+      public:
+        /** Return next Solvable in \ref Pool (or \ref noSolvable). */
+        Solvable nextInPool() const;
+        /** Return next Solvable in \ref Repo (or \ref noSolvable). */
+        Solvable nextInRepo() const;
+
+        /** Helper that splits an identifier into kind and name or vice versa.
+        * \note In case \c name_r is preceded by a well known kind spec, the
+        * \c kind_r argument is ignored, and kind is derived from name.
+         * \see \ref ident
+         */
+        class SplitIdent
+        {
+          public:
+            SplitIdent() {}
+            SplitIdent( IdString ident_r );
+            SplitIdent( const char * ident_r );
+            SplitIdent( const std::string & ident_r );
+            SplitIdent( ResKind kind_r, IdString name_r );
+            SplitIdent( ResKind kind_r, const C_Str & name_r );
+
+            IdString ident() const { return _ident; }
+            ResKind  kind()  const { return _kind; }
+            IdString name()  const { return _name; }
+
+           /** Return an idents explicit kind prefix, or \ref ResKind() if none.
+            * Mainly to detect wheter a given ident string is explicitly prefixed
+            * by a known kind (e.g \c pattern:foo or \c package:foo).
+            */
+           static ResKind explicitKind( IdString ident_r )             { return explicitKind( ident_r.c_str() );  }
+           static ResKind explicitKind( const char * ident_r );
+           static ResKind explicitKind( const std::string & ident_r )  { return explicitKind( ident_r.c_str() );  }
+
+          private:
+            IdString  _ident;
+            ResKind   _kind;
+            IdString  _name;
+        };
+
+      public:
+        /** Expert backdoor. */
+        ::_Solvable * get() const;
+        /** Expert backdoor. */
+        IdType id() const { return _id; }
+      private:
+#ifndef SWIG // Swig treats it as syntax error
+        friend base::SafeBool<Solvable>::operator bool_type() const;
+#endif
+        bool boolTest() const { return get(); }
+      private:
+        IdType _id;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Solvable Stream output */
+    std::ostream & operator<<( std::ostream & str, const Solvable & obj );
+
+    /** \relates Solvable More verbose stream output including dependencies */
+    std::ostream & dumpOn( std::ostream & str, const Solvable & obj );
+
+    /** \relates Solvable */
+    inline bool operator==( const Solvable & lhs, const Solvable & rhs )
+    { return lhs.get() == rhs.get(); }
+
+    /** \relates Solvable */
+    inline bool operator!=( const Solvable & lhs, const Solvable & rhs )
+    { return lhs.get() != rhs.get(); }
+
+    /** \relates Solvable */
+    inline bool operator<( const Solvable & lhs, const Solvable & rhs )
+    { return lhs.get() < rhs.get(); }
+
+    /** \relates Solvable Test for same content. */
+    inline bool identical( Solvable lhs, Solvable rhs )
+    { return lhs.identical( rhs ); }
+
+    /** \relates Solvable Test for same name version release and arch. */
+    inline bool sameNVRA( Solvable lhs, Solvable rhs )
+    { return lhs.sameNVRA( rhs ); }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : SolvableIterator
+      //
+      /** */
+      class SolvableIterator : public boost::iterator_adaptor<
+          SolvableIterator                   // Derived
+          , ::_Solvable*                     // Base
+          , const Solvable                   // Value
+          , boost::forward_traversal_tag     // CategoryOrTraversal
+          , const Solvable                   // Reference
+          >
+      {
+        public:
+          SolvableIterator()
+          : SolvableIterator::iterator_adaptor_( 0 )
+          {}
+
+          explicit SolvableIterator( const Solvable & val_r )
+          : SolvableIterator::iterator_adaptor_( 0 )
+          { assignVal( val_r ); }
+
+          explicit SolvableIterator( SolvableIdType id_r )
+          : SolvableIterator::iterator_adaptor_( 0 )
+          { assignVal( Solvable( id_r ) ); }
+
+        private:
+          friend class boost::iterator_core_access;
+
+          Solvable dereference() const
+          { return _val; }
+
+          void increment()
+          { assignVal( _val.nextInPool() ); }
+
+        private:
+          void assignVal( const Solvable & val_r )
+          { _val = val_r; base_reference() = _val.get(); }
+
+          Solvable _val;
+      };
+      ///////////////////////////////////////////////////////////////////
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates sat::Solvable Test whether a \ref sat::Solvable is of a certain Kind. */
+  template<class _Res>
+  inline bool isKind( const sat::Solvable & solvable_r )
+  { return solvable_r.isKind( ResTraits<_Res>::kind ); }
+
+  class PoolItem;
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+    /** To Solvable transform functor.
+     * \relates Solvable
+     * \relates sat::SolvIterMixin
+     */
+    struct asSolvable
+    {
+      typedef Solvable result_type;
+
+      Solvable operator()( Solvable solv_r ) const
+      { return solv_r; }
+
+      Solvable operator()( const PoolItem & pi_r ) const;
+
+      Solvable operator()( const ResObject_constPtr & res_r ) const;
+    };
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+ZYPP_DEFINE_ID_HASHABLE( ::zypp::sat::Solvable );
+
+#endif // ZYPP_SAT_SOLVABLE_H
diff --git a/zypp/sat/SolvableSet.cc b/zypp/sat/SolvableSet.cc
new file mode 100644 (file)
index 0000000..2834f3f
--- /dev/null
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/SolvableSet.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/sat/SolvableSet.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const SolvableSet & obj )
+    {
+      return dumpRange( str, obj.begin(), obj.end() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/SolvableSet.h b/zypp/sat/SolvableSet.h
new file mode 100644 (file)
index 0000000..daa8ebd
--- /dev/null
@@ -0,0 +1,112 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/SolvableSet.h
+ *
+*/
+#ifndef ZYPP_SAT_SOLVABLESET_H
+#define ZYPP_SAT_SOLVABLESET_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Tr1hash.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/SolvIterMixin.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SolvableSet
+    //
+    /** Solvable set wrapper to allow adding additioanal convenience iterators.
+     */
+    class SolvableSet : public SolvIterMixin<SolvableSet,std::tr1::unordered_set<Solvable>::const_iterator>
+    {
+      friend std::ostream & operator<<( std::ostream & str, const SolvableSet & obj );
+
+      public:
+        typedef std::tr1::unordered_set<Solvable> Container;
+        typedef Container::value_type             value_type;
+        typedef Container::size_type              size_type;
+        typedef Solvable_iterator                 const_iterator; // from SolvIterMixin
+
+      public:
+        /** Default ctor */
+        SolvableSet()
+        : _pimpl( new Container )
+        {}
+
+        /** Ctor building a set from a range. */
+        template<class _InputIterator>
+        SolvableSet( _InputIterator begin_r, _InputIterator end_r )
+        : _pimpl( new Container( begin_r, end_r ) )
+        {}
+
+      public:
+        /** Whether the set is epmty. */
+        bool empty() const
+        { return _pimpl->empty(); }
+
+        /** Size of the set. */
+        size_type size() const
+        { return _pimpl->size(); }
+
+       /** */
+       template<class _Solv>
+       bool contains( const _Solv & solv_r ) const
+       { return( get().find( asSolvable()( solv_r ) ) != end() ); }
+
+        /** Iterator pointing to the first \ref Solvable. */
+        const_iterator begin() const
+        { return _pimpl->begin(); }
+
+        /** Iterator pointing behind the last \ref Solvable. */
+        const_iterator end() const
+        { return _pimpl->end(); }
+
+      public:
+
+       /** Insert a Solvable.
+        * \return \c true if it was actually inserted, or \c false if already present.
+       */
+       template<class _Solv>
+       bool insert( const _Solv & solv_r )
+       { return get().insert( asSolvable()( solv_r ) ).second; }
+
+      public:
+        /** The set. */
+        Container & get()
+        { return *_pimpl; }
+
+        /** The set. */
+        const Container & get() const
+        { return *_pimpl; }
+
+      private:
+        /** Pointer to implementation */
+        RWCOW_pointer<Container> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates SolvableSet Stream output */
+    std::ostream & operator<<( std::ostream & str, const SolvableSet & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_SOLVABLESET_H
diff --git a/zypp/sat/Transaction.cc b/zypp/sat/Transaction.cc
new file mode 100644 (file)
index 0000000..06dde49
--- /dev/null
@@ -0,0 +1,443 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Transaction.cc
+ */
+extern "C"
+{
+#include <satsolver/transaction.h>
+#include <satsolver/bitmap.h>
+}
+#include <iostream>
+#include "zypp/base/LogTools.h"
+#include "zypp/base/SerialNumber.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/Tr1hash.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Transaction.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/Queue.h"
+#include "zypp/ResPool.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    /** Transaction implementation.
+     *
+     * \NOTE After commit the @System repo is reloaded. This invalidates
+     * the ids off all installed items in the transaction, including their
+     * stepType. Thats why some information (stepType, NVRA) is be stored
+     * for post mortem access (i.e. tell after commit which NVRA were deleted).
+     *
+     */
+    struct Transaction::Impl : protected detail::PoolMember
+    {
+      friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
+
+      public:
+       typedef std::tr1::unordered_set<detail::IdType> set_type;
+       typedef std::tr1::unordered_map<detail::IdType,detail::IdType> map_type;
+
+       struct PostMortem
+       {
+         PostMortem()
+         {}
+         PostMortem( const sat::Solvable & solv_r )
+           : _ident( solv_r.ident() )
+           , _edition( solv_r.edition() )
+           , _arch( solv_r.arch() )
+         {}
+
+         IdString _ident;
+         Edition  _edition;
+         Arch     _arch;
+       };
+       typedef std::tr1::unordered_map<detail::IdType,PostMortem> pmmap_type;
+
+      public:
+       Impl()
+       { memset( &_trans, 0, sizeof(_trans) ); }
+
+       Impl( ::_Transaction & trans_r )
+         : _watcher( myPool().serial() )
+       {
+         memset( &_trans, 0, sizeof(_trans) );
+         ::transaction_init( &_trans, myPool().getPool() );
+
+         Queue decisionq;
+         for_( it, ResPool::instance().begin(), ResPool::instance().end() )
+         {
+           if ( ! (*it).status().transacts() )
+             continue;
+           sat::Solvable solv( (*it).satSolvable() );
+           decisionq.push( solv.isSystem() ? -solv.id() : solv.id() );
+         }
+         if ( trans_r.noobsmap.size )
+           ::map_grow( &trans_r.noobsmap, myPool()->nsolvables );
+         ::transaction_calculate( &_trans, decisionq, &trans_r.noobsmap );
+
+         // NOTE: package/product buddies share the same ResStatus
+         // so we also link the buddies stepStages. This assumes
+         // only one buddy is acting during commit (package is installed,
+         // but no extra operation for the product).
+         for_( it, _trans.steps.elements, _trans.steps.elements + _trans.steps.count )
+         {
+           sat::Solvable solv( *it );
+           // buddy list:
+           if ( ! solv.isKind<Package>() )
+           {
+             PoolItem pi( solv );
+             if ( pi.buddy() )
+             {
+               _linkMap[*it] = pi.buddy().id();
+             }
+           }
+           if ( solv.isSystem() )
+           {
+             // to delete list:
+             if ( stepType( solv ) == TRANSACTION_ERASE )
+             {
+               _systemErase.insert( *it );
+             }
+             // post mortem data
+             _pmMap[*it] = solv;
+           }
+         }
+       }
+
+       ~Impl()
+       { ::transaction_free( &_trans ); }
+
+      public:
+       bool valid() const
+       { return _watcher.isClean( myPool().serial() ); }
+
+       bool order()
+       {
+         if ( ! valid() )
+           return false;
+         if ( empty() )
+           return true;
+#if 0
+         // This is hwo we could implement out own order method.
+         // As ::transaction already groups by MediaNr, we don't
+         // need it for ORDER_BY_MEDIANR.
+         ::transaction_order( &_trans, SOLVER_TRANSACTION_KEEP_ORDERDATA );
+         detail::IdType chosen = 0;
+         Queue choices;
+
+         while ( true )
+         {
+           int ret = transaction_order_add_choices( &_trans, chosen, choices );
+           MIL << ret << ": " << chosen << ": " << choices << endl;
+           chosen = choices.pop_front(); // pick one out of choices
+           if ( ! chosen )
+             break;
+         }
+         return true;
+#endif
+         if ( !_ordered )
+         {
+           ::transaction_order( &_trans, 0 );
+           _ordered = true;
+         }
+         return true;
+       }
+
+       bool empty() const
+       { return( _trans.steps.count == 0 ); }
+
+       size_t size() const
+       { return _trans.steps.count; }
+
+       const_iterator begin( const RW_pointer<Transaction::Impl> & self_r ) const
+       { return const_iterator( self_r, _trans.steps.elements ); }
+       iterator begin( const RW_pointer<Transaction::Impl> & self_r )
+       { return iterator( self_r, _trans.steps.elements ); }
+
+       const_iterator end( const RW_pointer<Transaction::Impl> & self_r ) const
+       { return const_iterator( self_r, _trans.steps.elements + _trans.steps.count ); }
+       iterator end( const RW_pointer<Transaction::Impl> & self_r )
+       { return iterator( self_r, _trans.steps.elements + _trans.steps.count ); }
+
+       const_iterator find(const RW_pointer<Transaction::Impl> & self_r, const sat::Solvable & solv_r ) const
+       { detail::IdType * it( _find( solv_r ) ); return it ? const_iterator( self_r, it ) : end( self_r ); }
+       iterator find(const RW_pointer<Transaction::Impl> & self_r, const sat::Solvable & solv_r )
+       { detail::IdType * it( _find( solv_r ) ); return it ? iterator( self_r, it ) : end( self_r ); }
+
+      public:
+       StepType stepType( Solvable solv_r ) const
+       {
+         if ( ! solv_r )
+         {
+           // post mortem @System solvable
+           return isIn( _systemErase, solv_r.id() ) ? TRANSACTION_ERASE : TRANSACTION_IGNORE;
+         }
+
+         switch( ::transaction_type( &_trans, solv_r.id(), SOLVER_TRANSACTION_RPM_ONLY ) )
+         {
+           case SOLVER_TRANSACTION_ERASE: return TRANSACTION_ERASE; break;
+           case SOLVER_TRANSACTION_INSTALL: return TRANSACTION_INSTALL; break;
+           case SOLVER_TRANSACTION_MULTIINSTALL: return TRANSACTION_MULTIINSTALL; break;
+         }
+         return TRANSACTION_IGNORE;
+       }
+
+       StepStage stepStage( Solvable solv_r ) const
+       { return stepStage( resolve( solv_r ) ); }
+
+       void stepStage( Solvable solv_r, StepStage newval_r )
+       { stepStage( resolve( solv_r ), newval_r ); }
+
+       const PostMortem & pmdata( Solvable solv_r ) const
+       {
+         static PostMortem _none;
+         pmmap_type::const_iterator it( _pmMap.find( solv_r.id() ) );
+         return( it == _pmMap.end() ? _none : it->second );
+       }
+
+      private:
+       detail::IdType resolve( const Solvable & solv_r ) const
+       {
+         map_type::const_iterator res( _linkMap.find( solv_r.id() ) );
+         return( res == _linkMap.end() ? solv_r.id() : res->second );
+       }
+
+       bool isIn( const set_type & set_r, detail::IdType sid_r ) const
+       { return( set_r.find( sid_r ) != set_r.end() ); }
+
+       StepStage stepStage( detail::IdType sid_r ) const
+       {
+         if ( isIn( _doneSet, sid_r ) )
+           return STEP_DONE;
+         if ( isIn( _errSet, sid_r ) )
+           return STEP_ERROR;
+         return STEP_TODO;
+       }
+
+       void stepStage( detail::IdType sid_r, StepStage newval_r )
+       {
+         StepStage stage( stepStage( sid_r ) );
+         if ( stage != newval_r )
+         {
+           // reset old stage
+           if ( stage != STEP_TODO )
+           {
+             (stage == STEP_DONE ? _doneSet : _errSet).erase( sid_r );
+           }
+           if ( newval_r != STEP_TODO )
+           {
+             (newval_r == STEP_DONE ? _doneSet : _errSet).insert( sid_r );
+           }
+         }
+       }
+
+      private:
+       detail::IdType * _find( const sat::Solvable & solv_r ) const
+       {
+         if ( solv_r && _trans.steps.elements )
+         {
+           for_( it, _trans.steps.elements, _trans.steps.elements + _trans.steps.count )
+           {
+             if ( *it == detail::IdType(solv_r.id()) )
+               return it;
+           }
+         }
+         return 0;
+       }
+
+     private:
+       SerialNumberWatcher _watcher;
+       mutable ::Transaction _trans;
+       DefaultIntegral<bool,false> _ordered;
+       //
+       set_type        _doneSet;
+       set_type        _errSet;
+       map_type        _linkMap;       // buddy map to adopt buddies StepResult
+       set_type        _systemErase;   // @System packages to be eased (otherse are TRANSACTION_IGNORE)
+       pmmap_type      _pmMap;         // Post mortem data of deleted @System solvables
+
+      public:
+        /** Offer default Impl. */
+        static shared_ptr<Impl> nullimpl()
+        {
+          static shared_ptr<Impl> _nullimpl( new Impl );
+          return _nullimpl;
+        }
+    };
+
+    /** \relates Transaction::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Transaction::Impl & obj )
+    {
+      return str << "Transaction: " << obj.size() << " (" << (obj.valid()?"valid":"INVALID") << ")";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Transaction
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Transaction::Transaction()
+      : _pimpl( Impl::nullimpl() )
+    {}
+
+    Transaction::Transaction( ::_Transaction & trans_r )
+      : _pimpl( new Impl( trans_r ) )
+    {}
+
+    Transaction::~Transaction()
+    {}
+
+    bool Transaction::valid() const
+    { return _pimpl->valid(); }
+
+    bool Transaction::order()
+    { return _pimpl->order(); }
+
+    bool Transaction::empty() const
+    { return _pimpl->empty(); }
+
+    size_t Transaction::size() const
+    { return _pimpl->size(); }
+
+    Transaction::const_iterator Transaction::begin() const
+    { return _pimpl->begin( _pimpl ); }
+
+    Transaction::iterator Transaction::begin()
+    { return _pimpl->begin( _pimpl ); }
+
+    Transaction::const_iterator Transaction::end() const
+    { return _pimpl->end( _pimpl ); }
+
+    Transaction::iterator Transaction::end()
+    { return _pimpl->end( _pimpl ); }
+
+    Transaction::const_iterator Transaction::find( const sat::Solvable & solv_r ) const
+    { return _pimpl->find( _pimpl, solv_r ); }
+
+    Transaction::iterator Transaction::find( const sat::Solvable & solv_r )
+    { return _pimpl->find( _pimpl, solv_r ); }
+
+    std::ostream & operator<<( std::ostream & str, const Transaction & obj )
+    { return str << *obj._pimpl; }
+
+    std::ostream & dumpOn( std::ostream & str, const Transaction & obj )
+    {
+      for_( it, obj.begin(), obj.end() )
+      {
+       str << *it << endl;
+      }
+      return str;
+    }
+
+    bool operator==( const Transaction & lhs, const Transaction & rhs )
+    { return lhs._pimpl == rhs._pimpl; }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Transaction::Step
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Transaction::Step::Step()
+    {}
+
+    Transaction::StepType Transaction::Step::stepType() const
+    { return _pimpl->stepType( _solv ); }
+
+    Transaction::StepStage Transaction::Step::stepStage() const
+    { return _pimpl->stepStage( _solv ); }
+
+    void Transaction::Step::stepStage( StepStage val_r )
+    { _pimpl->stepStage( _solv, val_r ); }
+
+    IdString Transaction::Step::ident() const
+    { return _solv ? _solv.ident() : _pimpl->pmdata(_solv )._ident; }
+
+    Edition Transaction::Step::edition() const
+    { return _solv ? _solv.edition() : _pimpl->pmdata(_solv )._edition; }
+
+    Arch Transaction::Step::arch() const
+    { return _solv ? _solv.arch() : _pimpl->pmdata(_solv )._arch; }
+
+    std::ostream & operator<<( std::ostream & str, const Transaction::Step & obj )
+    {
+      str << obj.stepType() << obj.stepStage() << " ";
+      if ( obj.satSolvable() )
+       str << PoolItem( obj.satSolvable() );
+      else
+       str << '[' << obj.ident() << '-' << obj.edition() << '.' << obj.arch() << ']';
+      return str;
+    }
+
+    std::ostream & operator<<( std::ostream & str, Transaction::StepType obj )
+    {
+      switch ( obj )
+      {
+       #define OUTS(E,S) case Transaction::E: return str << #S; break
+       OUTS( TRANSACTION_IGNORE,       [ ] );
+       OUTS( TRANSACTION_ERASE,        [-] );
+       OUTS( TRANSACTION_INSTALL,      [+] );
+       OUTS( TRANSACTION_MULTIINSTALL, [M] );
+       #undef OUTS
+      }
+      return str << "[?]";
+    }
+
+    std::ostream & operator<<( std::ostream & str, Transaction::StepStage obj )
+    {
+      switch ( obj )
+      {
+       #define OUTS(E,S) case Transaction::E: return str << #S; break
+       OUTS( STEP_TODO,        [__] );
+       OUTS( STEP_DONE,        [OK] );
+       OUTS( STEP_ERROR,       [**] );
+       #undef OUTS
+      }
+      return str << "[??]";
+    }
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : Transaction::const_iterator/iterator
+      //
+      ///////////////////////////////////////////////////////////////////
+
+      Transaction_const_iterator::Transaction_const_iterator()
+      : Transaction_const_iterator::iterator_adaptor_( 0 )
+      {}
+
+      Transaction_const_iterator::Transaction_const_iterator( const Transaction_iterator & iter_r )
+      : Transaction_const_iterator::iterator_adaptor_( iter_r.base() )
+      , _pimpl( iter_r._pimpl )
+      {}
+
+      Transaction_iterator::Transaction_iterator()
+      : Transaction_iterator::iterator_adaptor_( 0 )
+      {}
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/Transaction.h b/zypp/sat/Transaction.h
new file mode 100644 (file)
index 0000000..0155905
--- /dev/null
@@ -0,0 +1,395 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/Transaction.h
+ */
+extern "C"
+{
+  struct _Transaction;
+}
+#ifndef ZYPP_SAT_TRANSACTION_H
+#define ZYPP_SAT_TRANSACTION_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Flags.h"
+#include "zypp/base/SafeBool.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/DefaultIntegral.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/SolvIterMixin.h"
+#include "zypp/sat/Solvable.h"
+
+#include "zypp/PoolItem.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      /** Needs to be outside \ref Transaction in order to be usable in SolvIterMixin. */
+      class Transaction_iterator;
+      /** Needs to be outside \ref Transaction in order to be usable in SolvIterMixin. */
+      class Transaction_const_iterator;
+    }
+
+    /** Satsolver transaction wrapper.
+     * \note Note that Transaction is derived from \ref sat::SolvIterMixin which
+     *       makes PoolItem and Selectable iterators automatically available.
+     * \note Changing the \ref ResPool content (loading/unloading repositories)
+     *       invalidates all outstanding transaction data. \see \ref valid.
+     * \note.The transaction may inlude steps of type \ref TRANSACTION_IGNORE which
+     *       do not cause/require any specific action. To skip those informal steps
+     *       when iterating, use the \ref actionBegin /\ref actionEnd methods.
+     */
+    class Transaction : public SolvIterMixin<Transaction, detail::Transaction_const_iterator>
+                     , protected base::SafeBool<Transaction>
+    {
+      friend std::ostream & operator<<( std::ostream & str, const Transaction & obj );
+      friend std::ostream & dumpOn( std::ostream & str, const Transaction & obj );
+      friend bool operator==( const Transaction & lhs, const Transaction & rhs );
+
+     public:
+       /** Represents a single step within a \ref Transaction. */
+       class Step;
+
+       /** Type of (rpm) action to perform in a \ref Step. */
+       enum StepType
+       {
+        TRANSACTION_IGNORE             = 0x00, /**< [ ] Nothing (includes implicit deletes due to obsoletes and non-package actions) */
+        TRANSACTION_ERASE              = 0x10, /**< [-] Delete item */
+        TRANSACTION_INSTALL            = 0x20, /**< [+] Install(update) item */
+        TRANSACTION_MULTIINSTALL       = 0x30  /**< [M] Install(multiversion) item (\see \ref ZConfig::multiversion) */
+       };
+
+       /** \ref Step action result. */
+       enum StepStage
+       {
+        STEP_TODO      = (1 << 0),     /**< [__] unprocessed */
+        STEP_DONE      = (1 << 1),     /**< [OK] success */
+        STEP_ERROR     = (1 << 2),     /**< [**] error */
+       };
+
+       ZYPP_DECLARE_FLAGS(StepStages,StepStage);
+
+     public:
+        /** Default ctor: empty transaction. */
+        Transaction();
+
+        /** Ctor cloning a sat transaction. */
+        Transaction( ::_Transaction & trans_r );
+
+        /** Dtor */
+        ~Transaction();
+
+      public:
+       /** Whether transaction actually contains data and also fits the current pools content. */
+       bool valid() const;
+
+        /**  Validate object in a boolean context: valid */
+        using base::SafeBool<Transaction>::operator bool_type;
+
+       /** Order transaction steps for commit.
+        * It's cheap to call it for an aleready ordered \ref Transaction.
+        * This invalidates outstanding iterators. Returns whether
+        * \ref Transaction is \ref valid.
+        */
+       bool order();
+
+       /** Whether the transaction contains any steps. */
+       bool empty() const;
+
+       /** Number of steps in transaction steps. */
+       size_t size() const;
+
+       typedef detail::Transaction_iterator iterator;
+       typedef detail::Transaction_const_iterator const_iterator;
+
+       /** Iterator to the first \ref TransactionStep */
+       const_iterator begin() const;
+       /** \overload */
+       iterator begin();
+
+       /** Iterator behind the last \ref TransactionStep */
+       const_iterator end() const;
+       /** \overload */
+       iterator end();
+
+       /** Return iterator pointing to \a solv_r or \ref end. */
+       const_iterator find( const sat::Solvable & solv_r ) const;
+       iterator find( const sat::Solvable & solv_r );
+       /** \overload */
+       const_iterator find( const ResObject::constPtr & resolvable_r ) const;
+       iterator find( const ResObject::constPtr & resolvable_r );
+       /** \overload */
+       const_iterator find( const PoolItem & pi_r ) const;
+       iterator find( const PoolItem & pi_r );
+
+      public:
+       /** \name Iterate action steps (omit TRANSACTION_IGNORE steps).
+        *
+        * All these methods allow to pass an optional OR'd combination of
+        * \ref StepStages as filter. Per default all steps are processed/counted.
+        *
+        * \code
+        *    Transaction trans;
+        *    for_( it, trans.actionBegin(~sat::Transaction::STEP_DONE), trans.actionEnd() )
+        *    {
+        *       ... // process all steps not DONE (ERROR and TODO)
+        *    }
+        * \endcode
+        */
+       //@{
+       struct FilterAction;
+       typedef filter_iterator<FilterAction,const_iterator> action_iterator;
+
+       /** Whether the [filtered] transaction contains any steps . */
+       bool actionEmpty( StepStages filter_r = StepStages() ) const;
+
+       /** Number of steps in [filtered] transaction steps. */
+       size_t actionSize( StepStages filter_r = StepStages() ) const;
+
+       /** Pointer to the 1st action step in [filtered] transaction. */
+       action_iterator actionBegin( StepStages filter_r = StepStages() ) const;
+
+       /** Pointer behind the last action step in transaction. */
+       action_iterator actionEnd() const;
+
+       //@}
+
+      private:
+        friend base::SafeBool<Transaction>::operator bool_type() const;
+        /**  Validate object in a boolean context. */
+        bool boolTest() const
+        { return valid(); }
+      public:
+        /** Implementation  */
+        class Impl;
+      private:
+        /** Pointer to implementation */
+        RW_pointer<Impl> _pimpl;
+    };
+
+    ZYPP_DECLARE_OPERATORS_FOR_FLAGS(Transaction::StepStages);
+
+    /** \relates Transaction Stream output */
+    std::ostream & operator<<( std::ostream & str, const Transaction & obj );
+
+    /** \relates Transaction Verbose stream output */
+    std::ostream & dumpOn( std::ostream & str, const Transaction & obj );
+
+    /** \relates Transaction */
+    bool operator==( const Transaction & lhs, const Transaction & rhs );
+
+    /** \relates Transaction */
+    inline bool operator!=( const Transaction & lhs, const Transaction & rhs )
+    { return !( lhs == rhs ); }
+
+
+    /** A single step within a \ref Transaction.
+     *
+     * \note After commit, when the @System repo (rpm database) is reread, all
+     * @System solvables within the transaction are invalidated (they got deleted).
+     * Thats why we internally store the NVRA, so you can access \ref ident
+     * (\see \ref sat::Solvable::ident), \ref edition, \ref arch of a deleted package,
+     * even if the \ref satSolvable itself is meanwhile invalid.
+     *
+     * \see \ref Transaction.
+     */
+    class Transaction::Step
+    {
+      friend std::ostream & operator<<( std::ostream & str, const Step & obj );
+
+      public:
+       Step();
+       Step( const RW_pointer<Impl> & pimpl_r, detail::IdType id_r )
+         : _solv( id_r )
+         , _pimpl( pimpl_r )
+       {}
+
+      public:
+       /** Type of action to perform in this step. */
+       StepType stepType() const;
+
+       /** Step action result. */
+       StepStage stepStage() const;
+
+       /** Set step action result. */
+       void stepStage( StepStage val_r );
+
+       /** Return the corresponding \ref Solvable.
+        * Returns \ref Solvable::noSolvable if the item is meanwhile deleted and
+        * was removed from the pool. \see Post mortem acccess to @System solvables.
+        */
+       Solvable satSolvable() const
+       { return _solv; }
+
+       /** \name Post mortem acccess to @System solvables
+        * \code
+        *   Transaction::Step step;
+        *   if ( step.satSolvable() )
+        *     std::cout << step.satSolvable() << endl;
+        *   else
+        *     std::cout << step.ident() << endl; // deleted @System solvable
+        * \endcode
+        */
+       //@{
+       /** \see \ref sat::Solvable::ident. */
+       IdString ident() const;
+
+       /** \see \ref sat::Solvable::edition. */
+       Edition edition() const;
+
+       /** \see \ref sat::Solvable::arch. */
+       Arch arch() const;
+       //@}
+
+       /** Implicit conversion to \ref Solvable */
+       operator const Solvable &() const { return _solv; }
+       /** \overload nonconst */
+       operator Solvable &() { return _solv; }
+
+      private:
+       Solvable _solv;
+       /** Pointer to implementation */
+       RW_pointer<Impl> _pimpl;
+    };
+
+    /** \relates Transaction::Step Stream output */
+    std::ostream & operator<<( std::ostream & str, const Transaction::Step & obj );
+
+    /** \relates Transaction::StepType Stream output */
+    std::ostream & operator<<( std::ostream & str, Transaction::StepType obj );
+
+    /** \relates Transaction::StepStage Stream output */
+    std::ostream & operator<<( std::ostream & str, Transaction::StepStage obj );
+
+   ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      /** \ref Transaction iterator.
+       */
+      class Transaction_iterator : public boost::iterator_adaptor<
+      Transaction_iterator             // Derived
+      , const detail::IdType *         // Base
+      , Transaction::Step              // Value
+      , boost::forward_traversal_tag   // CategoryOrTraversal
+      , Transaction::Step              // Reference
+      >
+      {
+       public:
+         Transaction_iterator();
+         Transaction_iterator( const RW_pointer<Transaction::Impl> & pimpl_r, base_type id_r )
+         : Transaction_iterator::iterator_adaptor_( id_r )
+         , _pimpl( pimpl_r )
+         {}
+
+       private:
+         friend class boost::iterator_core_access;
+
+         reference dereference() const
+         { return Transaction::Step( _pimpl, *base() ); }
+
+       private:
+         friend class Transaction_const_iterator;
+         /** Pointer to implementation */
+         RW_pointer<Transaction::Impl> _pimpl;
+      };
+
+     /** \ref Transaction const_iterator.
+       */
+      class Transaction_const_iterator : public boost::iterator_adaptor<
+      Transaction_const_iterator       // Derived
+      , const detail::IdType *         // Base
+      , const Transaction::Step                // Value
+      , boost::forward_traversal_tag   // CategoryOrTraversal
+      , const Transaction::Step                // Reference
+      >
+      {
+       public:
+         Transaction_const_iterator();
+         Transaction_const_iterator( const Transaction_iterator & iter_r );
+         Transaction_const_iterator( const RW_pointer<Transaction::Impl> & pimpl_r, base_type id_r )
+         : Transaction_const_iterator::iterator_adaptor_( id_r )
+         , _pimpl( pimpl_r )
+         {}
+
+       private:
+         friend class boost::iterator_core_access;
+
+         reference dereference() const
+         { return Transaction::Step( _pimpl, *base() ); }
+
+       private:
+         /** Pointer to implementation */
+         RW_pointer<Transaction::Impl> _pimpl;
+      };
+
+       /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+    inline Transaction::const_iterator Transaction::find( const ResObject::constPtr & resolvable_r ) const
+    { return( resolvable_r ? find( resolvable_r->satSolvable() ) : end() ); }
+
+    inline Transaction::iterator Transaction::find( const ResObject::constPtr & resolvable_r )
+    { return( resolvable_r ? find( resolvable_r->satSolvable() ) : end() ); }
+
+    inline Transaction::const_iterator Transaction::find( const PoolItem & pi_r ) const
+    { return find( pi_r.satSolvable() ); }
+
+    inline Transaction::iterator Transaction::find( const PoolItem & pi_r )
+    { return find( pi_r.satSolvable() ); }
+
+
+    struct  Transaction::FilterAction
+    {
+      FilterAction() {}
+      FilterAction( StepStages filter_r ) : _filter( filter_r ) {}
+
+      bool operator()( const Transaction::Step & step_r ) const
+      {
+       if ( step_r.stepType() == Transaction::TRANSACTION_IGNORE )
+         return false; // no action
+       return !_filter || _filter.testFlag( step_r.stepStage() );
+      }
+
+      StepStages _filter;
+    };
+
+    inline Transaction::action_iterator Transaction::actionBegin( StepStages filter_r ) const
+    { return make_filter_begin( FilterAction( filter_r ), *this ); }
+
+    inline Transaction::action_iterator Transaction::actionEnd() const
+    { return make_filter_end( FilterAction(), *this ); }
+
+    inline bool Transaction::actionEmpty( StepStages filter_r ) const
+    { return( actionBegin( filter_r ) == actionEnd() ); }
+
+    inline size_t Transaction::actionSize( StepStages filter_r ) const
+    {
+      size_t cnt = 0;
+      for_( it, actionBegin( filter_r ), actionEnd() )
+       ++cnt;
+      return cnt;
+    }
+
+     /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_TRANSACTION_H
diff --git a/zypp/sat/WhatObsoletes.cc b/zypp/sat/WhatObsoletes.cc
new file mode 100644 (file)
index 0000000..740e4b5
--- /dev/null
@@ -0,0 +1,172 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/WhatObsoletes.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Tr1hash.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/PoolItem.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    // Obsoletes may either match against provides, or names.
+    // Configuration depends on the behaviour of rpm.
+    bool obsoleteUsesProvides = false;
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      typedef std::tr1::unordered_set<detail::IdType> set_type;
+      typedef std::vector<sat::detail::IdType>        vector_type;
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    WhatObsoletes::WhatObsoletes( Solvable item_r )
+    : _begin( 0 )
+    {
+      ctorAdd( item_r );
+      ctorDone();
+    }
+
+    WhatObsoletes::WhatObsoletes( const PoolItem & item_r )
+    : _begin( 0 )
+    {
+      ctorAdd( item_r );
+      ctorDone();
+    }
+
+    WhatObsoletes::WhatObsoletes( const ResObject::constPtr item_r )
+    : _begin( 0 )
+    {
+      if ( item_r )
+      {
+        ctorAdd( item_r->satSolvable() );
+        ctorDone();
+      }
+    }
+
+    void WhatObsoletes::ctorAdd( const PoolItem & item_r )
+    { ctorAdd( item_r->satSolvable() ); }
+
+    void WhatObsoletes::ctorAdd( ResObject_constPtr item_r )
+    { if ( item_r ) ctorAdd( item_r->satSolvable() ); }
+
+
+    namespace
+    {
+      /** Add item to the set created on demand. */
+      inline void addToSet( Solvable item, set_type *& pdata, shared_ptr<void>& _private )
+      {
+        if ( ! pdata )
+        {
+          _private.reset( (pdata = new set_type) );
+        }
+        pdata->insert( item.id() );
+      }
+    }
+
+    void WhatObsoletes::ctorAdd( Solvable item_r )
+    {
+      if ( item_r.multiversionInstall() )
+        return; // multiversion (rpm -i) does not evaluate any obsoletes
+
+      if ( obsoleteUsesProvides )
+      {
+        WhatProvides obsoleted( item_r.obsoletes() );
+        if ( obsoleted.empty() )
+          return;
+
+        // use allocated private data to collect the results
+        set_type * pdata = ( _private ? reinterpret_cast<set_type*>( _private.get() ) : 0 );
+        for_( it, obsoleted.begin(), obsoleted.end() )
+        {
+          if ( it->isSystem() )
+            addToSet( *it, pdata, _private );
+        }
+      }
+      else // Obsoletes match names
+      {
+        Capabilities obsoletes( item_r.obsoletes() );
+        if ( obsoletes.empty() )
+          return;
+
+        // use allocated private data to collect the results
+        set_type * pdata = ( _private ? reinterpret_cast<set_type*>( _private.get() ) : 0 );
+        for_( it, obsoletes.begin(), obsoletes.end() )
+        {
+          // For each obsoletes find providers, but with the same name
+          IdString ident( it->detail().name() );
+          WhatProvides obsoleted( *it );
+          for_( iit, obsoleted.begin(), obsoleted.end() )
+          {
+            if ( iit->isSystem() && iit->ident() == ident )
+              addToSet( *iit, pdata, _private );
+          }
+        }
+      }
+    }
+
+    void WhatObsoletes::ctorDone()
+    {
+      if ( _private )
+      {
+        // copy set to vector and terminate _private
+        set_type * sdata = reinterpret_cast<set_type*>( _private.get() );
+
+        vector_type * pdata = new vector_type( sdata->begin(), sdata->end() );
+        pdata->push_back( sat::detail::noId );
+        _begin = &pdata->front();
+
+        _private.reset( pdata );
+      }
+    }
+
+    WhatObsoletes::size_type WhatObsoletes::size() const
+    {
+      if ( ! _begin )
+        return 0;
+
+      Capabilities::size_type ret = 0;
+      for ( const sat::detail::IdType * end = _begin; *end; ++end )
+      {
+        ++ret;
+      }
+      return ret;
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const WhatObsoletes & obj )
+    {
+      return dumpRange( str << "(" << obj.size() << ")", obj.begin(), obj.end() );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/WhatObsoletes.h b/zypp/sat/WhatObsoletes.h
new file mode 100644 (file)
index 0000000..f34efd7
--- /dev/null
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/WhatObsoletes.h
+ *
+*/
+#ifndef ZYPP_SAT_WHATOBSOLETES_H
+#define ZYPP_SAT_WHATOBSOLETES_H
+
+#include <iosfwd>
+#include <vector>
+
+#include "zypp/sat/WhatProvides.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : WhatObsoletes
+    //
+    /** Container of \b installed \ref Solvable which would be
+     *  obsoleted by the \ref Solvable passed to the ctor.
+     *
+     * \todo Publish obsoleteUsesProvides config option.
+     */
+    class WhatObsoletes : public SolvIterMixin<WhatObsoletes,detail::WhatProvidesIterator>,
+                          protected detail::PoolMember
+    {
+      public:
+        typedef Solvable  value_type;
+        typedef unsigned  size_type;
+
+      public:
+        /** Default ctor */
+        WhatObsoletes()
+        : _begin( 0 )
+        {}
+
+        /** Ctor from \ref Solvable. */
+        explicit
+        WhatObsoletes( Solvable item_r );
+
+        /** Ctor from \ref PoolItem. */
+        explicit
+        WhatObsoletes( const PoolItem & item_r );
+
+        /** Ctor from \ref ResObject::constPtr. */
+        explicit
+        WhatObsoletes( const ResObject_constPtr item_r );
+
+        /** Ctor from a range of \ref Solvable, \ref PoolItem or \ref ResObject::constPtr. */
+        template <class _Iterator>
+        WhatObsoletes( _Iterator begin, _Iterator end )
+        : _begin( 0 )
+        {
+          for_( it, begin, end )
+            ctorAdd( *it );
+          ctorDone();
+        }
+
+     public:
+        /** Whether the container is empty. */
+        bool empty() const
+        { return ! ( _begin && *_begin ); }
+
+        /** Number of solvables inside. */
+        size_type size() const;
+
+      public:
+        typedef detail::WhatProvidesIterator const_iterator;
+
+        /** Iterator pointing to the first \ref Solvable. */
+        const_iterator begin() const
+        { return const_iterator( _begin ); }
+
+        /** Iterator pointing behind the last \ref Solvable. */
+        const_iterator end() const
+        { return const_iterator(); }
+
+      private:
+        void ctorAdd( const PoolItem & item_r );
+        void ctorAdd( ResObject_constPtr item_r );
+        void ctorAdd( Solvable item_r );
+        void ctorDone();
+
+      private:
+        const sat::detail::IdType * _begin;
+        shared_ptr<void> _private;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates WhatObsoletes Stream output */
+    std::ostream & operator<<( std::ostream & str, const WhatObsoletes & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_WHATOBSOLETES_H
diff --git a/zypp/sat/WhatProvides.cc b/zypp/sat/WhatProvides.cc
new file mode 100644 (file)
index 0000000..4c3c75c
--- /dev/null
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/WhatProvides.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/sat/WhatProvides.h"
+#include "zypp/sat/detail/PoolImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : WhatProvides::Impl
+    //
+    /** WhatProvides implementation date.
+     * Stores the offset into a O terminated Id array. Per default
+     * the satsolvers whatprovidesdata, otherwise private data.
+     *
+     * As the satsolvers whatprovidesdata might be realocated
+     * while iterating a result, the iterator takes an
+     * <tt>const IdType *const*</tt>. Thats why we explicitly
+     * provide _private and pass its adress to the iterator,
+     * even if private data are not reallocated.
+     */
+    class WhatProvides::Impl : protected detail::PoolMember
+    {
+      public:
+        Impl()
+        : _offset( 0 ), _private( 0 )
+        {}
+
+        Impl( unsigned offset_r )
+        : _offset( offset_r ), _private( 0 )
+        {}
+
+        Impl( const std::tr1::unordered_set<detail::IdType> & ids_r )
+        : _offset( 0 ), _private( 0 )
+        {
+           // use private data to store the result (incl. trailing NULL)
+          _pdata.reserve( ids_r.size()+1 );
+          _pdata.insert( _pdata.begin(), ids_r.begin(), ids_r.end() );
+          _pdata.push_back( detail::noId );
+
+          _private = &_pdata.front(); // ptr to 1st element
+        }
+
+      public:
+        unsigned                         _offset;
+        const detail::IdType *           _private;
+
+      private:
+        std::vector<sat::detail::IdType> _pdata;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      /** WhatProvides ctor helper collecting providers from Capabilies. */
+      template <class Iterator>
+      void collectProviders( Iterator begin_r, Iterator end_r, std::tr1::unordered_set<detail::IdType> & collect_r )
+      {
+        for_( it, begin_r, end_r )
+        {
+          WhatProvides providers( *it );
+          for_( prv, providers.begin(), providers.end() )
+          {
+            collect_r.insert( prv->id() );
+          }
+        }
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } //namespace
+    ///////////////////////////////////////////////////////////////////
+
+    WhatProvides::WhatProvides()
+    {}
+
+    WhatProvides::WhatProvides( Capability cap_r )
+    {
+      unsigned res( myPool().whatProvides( cap_r ) );
+      if ( myPool().whatProvidesData( res ) )
+      {
+        _pimpl.reset( new Impl( res ) );
+      }
+      // else: no Impl for empty result.
+    }
+
+    WhatProvides::WhatProvides( Capabilities caps_r )
+    {
+      std::tr1::unordered_set<detail::IdType> ids;
+      collectProviders( caps_r.begin(), caps_r.end(), ids );
+      if ( ! ids.empty() )
+      {
+        _pimpl.reset( new Impl( ids ) );
+      }
+      // else: no Impl for empty result.
+   }
+
+    WhatProvides::WhatProvides( const CapabilitySet & caps_r )
+    {
+      std::tr1::unordered_set<detail::IdType> ids;
+      collectProviders( caps_r.begin(), caps_r.end(), ids );
+      if ( ! ids.empty() )
+      {
+        _pimpl.reset( new Impl( ids ) );
+      }
+      // else: no Impl for empty result.
+   }
+
+    bool WhatProvides::empty() const
+    {
+      return !_pimpl; // Ctor asserts no Impl for empty result.
+    }
+
+    WhatProvides::size_type WhatProvides::size() const
+    {
+      if ( !_pimpl )
+        return 0;
+
+      size_type count = 0;
+      for_( it, begin(), end() )
+        ++count;
+      return count;
+    }
+
+    WhatProvides::const_iterator WhatProvides::begin() const
+    {
+      if ( !_pimpl )
+        return const_iterator();
+
+      if ( _pimpl->_private )
+        return const_iterator( _pimpl->_private );
+
+      // for satsolvers index use one more indirection, as it might get relocated.
+      return const_iterator( &myPool().getPool()->whatprovidesdata, _pimpl->_offset );
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const WhatProvides & obj )
+    {
+      return dumpRange( str << "(" << obj.size() << ")", obj.begin(), obj.end() );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      std::ostream & operator<<( std::ostream & str, const WhatProvidesIterator & obj )
+      {
+        str << str::form( "[%5u]", obj._offset );
+        str << str::form( "<%p(%p)>", obj.base_reference(), &obj.base_reference() );
+        str << str::form( "<%p(%p)>", obj._baseRef, (obj._baseRef ? *obj._baseRef : 0) );
+        return str;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } //namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/WhatProvides.h b/zypp/sat/WhatProvides.h
new file mode 100644 (file)
index 0000000..c57f572
--- /dev/null
@@ -0,0 +1,235 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/WhatProvides.h
+ *
+*/
+#ifndef ZYPP_SAT_WHATPROVIDES_H
+#define ZYPP_SAT_WHATPROVIDES_H
+
+#include <iosfwd>
+#include <vector>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/SolvIterMixin.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      class WhatProvidesIterator;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : WhatProvides
+    //
+    /** Container of \ref Solvable providing a \ref Capability (read only).
+     *
+     * \code
+     * Capability cap("amarok < 1.13");
+     *
+     * WhatProvides q( cap );
+     * Solvable firstMatch;
+     *
+     * if ( ! q.empty() )
+     * {
+     *   cout << "Found " << q.size() << " matches for " << cap << ":" << endl;
+     *   firstMatch = *q.begin();
+     *
+     *   for_( it, q.begin(), q.end() )
+     *     cout << *it << endl;
+     * }
+     *
+     * if ( firstMatch )
+     * {
+     *   WhatProvides req( firstMatch.requires() );
+     *   if ( ! req.empty() )
+     *   {
+     *      cout << "Found " << req.size() << " items providing requirements of " << firstMatch << ":" << endl;
+     *   }
+     * }
+     * \endcode
+     *
+     * \note Note that there are capabilities which are not provided by any \ref Solvable,
+     * but are system properties. For example:
+     * \code
+     *   rpmlib(PayloadIsBzip2) <= 3.0.5-1
+     * \endcode
+     * In that case a \ref Solvable::noSolvable is returned, which has \c isSystem set \c true, although
+     * there should never be a \ref Solvable::noSolvable returned with \c isSystem set \c false. If so,
+     * please file a bugreport.
+     * \code
+     * WhatProvides q( Capability("rpmlib(PayloadIsBzip2) <= 3.0.5-1") );
+     * for_( it, q.begin(), q.end() )
+     * {
+     *   if ( *it )
+     *     cout << "Capability is provided by package " << *it << endl;
+     *   else if ( it->isSystem() )
+     *     cout << "Capability is a system property" << endl;
+     *   else
+     *     ; // never reaching this \c else
+     * }
+     * \endcode
+     */
+    class WhatProvides : public SolvIterMixin<WhatProvides,detail::WhatProvidesIterator>,
+                         protected detail::PoolMember
+    {
+      public:
+        typedef Solvable  value_type;
+        typedef unsigned  size_type;
+
+      public:
+        /** Default ctor */
+        WhatProvides();
+
+        /** Ctor from \ref Capability. */
+        explicit
+        WhatProvides( Capability cap_r );
+
+        /** Ctor collecting all providers of capabilities in \c caps_r. */
+        explicit
+        WhatProvides( Capabilities caps_r );
+
+        /** Ctor collecting all providers of capabilities in \c caps_r. */
+        explicit
+        WhatProvides( const CapabilitySet & caps_r );
+
+     public:
+        /** Whether the container is empty. */
+        bool empty() const;
+
+        /** Number of solvables inside. */
+        size_type size() const;
+
+      public:
+        typedef detail::WhatProvidesIterator const_iterator;
+
+        /** Iterator pointing to the first \ref Solvable. */
+        const_iterator begin() const;
+
+        /** Iterator pointing behind the last \ref Solvable. */
+        const_iterator end() const;
+
+      private:
+        struct Impl;
+        RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates WhatProvides Stream output */
+    std::ostream & operator<<( std::ostream & str, const WhatProvides & obj );
+
+    namespace detail
+    {
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : WhatProvides::const_iterator
+    //
+    /** \ref WhatProvides iterator.
+     * Iterate a NULL terminated sat::detail::IdType array. Ctor gets
+     * the adress of a pointer to the array, and offset into the array.
+     * This is needed in case the array gets reallocated.
+     */
+    class WhatProvidesIterator : public boost::iterator_adaptor<
+          WhatProvidesIterator         // Derived
+        , const detail::IdType *       // Base
+        , const Solvable               // Value
+        , boost::forward_traversal_tag // CategoryOrTraversal
+        , const Solvable               // Reference
+        >
+    {
+      friend std::ostream & operator<<( std::ostream & str, const WhatProvidesIterator & obj );
+      public:
+        WhatProvidesIterator()
+        : iterator_adaptor_( 0 ), _baseRef( 0 ), _offset( 0 )
+        {}
+
+        /** Ctor with pointer to 1st elemment of an array.
+         * Use otherwise unused base as pointer for _baseRef.
+         */
+        explicit WhatProvidesIterator( const detail::IdType *const base_r, unsigned offset_r = 0 )
+        : iterator_adaptor_( base_r ), _baseRef( base_r ? &base_reference() : 0 ), _offset( offset_r )
+        {}
+
+        /** Ctor with pointer to pointer to 1st elemment of an array.
+         * Required for arrays that might be relocated while iterating.
+         */
+        explicit WhatProvidesIterator( const detail::IdType *const* baseRef_r, unsigned offset_r )
+        : iterator_adaptor_( 0 ), _baseRef( baseRef_r ), _offset( offset_r )
+        {}
+
+        /** Copy-ctor required to keep _baseRef adjusted. */
+        WhatProvidesIterator( const WhatProvidesIterator & rhs )
+        : iterator_adaptor_( rhs.base_reference() )
+        , _baseRef( base_reference() ? &base_reference() : rhs._baseRef )
+        , _offset( rhs._offset )
+        {}
+
+        /** Assignment operator required to keep _baseRef adjusted. */
+        WhatProvidesIterator & operator=( const WhatProvidesIterator & rhs )
+        {
+          if ( this != &rhs ) // no self assign
+          {
+            base_reference() = rhs.base_reference();
+            _baseRef = ( base_reference() ? &base_reference() : rhs._baseRef );
+            _offset = rhs._offset;
+          }
+          return *this;
+        }
+
+      private:
+        friend class boost::iterator_core_access;
+
+        reference dereference() const
+        { return Solvable( getId() ); }
+#if 0
+        template <class OtherDerived, class OtherIterator, class V, class C, class R, class D>
+        bool equal( const boost::iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> & rhs ) const
+#endif
+        bool equal( const WhatProvidesIterator & rhs ) const
+        {
+          if ( ! ( getId() || rhs.getId() ) )
+            return true; // both @end
+          if ( _offset != rhs._offset )
+            return false;
+          if ( base_reference() )
+            return( base_reference() == rhs.base_reference() );
+          return( _baseRef == rhs._baseRef );
+        }
+
+        void increment()
+        { ++_offset; }
+
+        detail::IdType getId() const
+        { return _baseRef ? (*_baseRef)[_offset] : detail::noId; }
+
+      private:
+        const detail::IdType *const* _baseRef;
+        unsigned                     _offset;
+    };
+    ///////////////////////////////////////////////////////////////////
+    }
+
+    inline WhatProvides::const_iterator WhatProvides::end() const
+    { return const_iterator(); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_WHATPROVIDES_H
diff --git a/zypp/sat/detail/PoolImpl.cc b/zypp/sat/detail/PoolImpl.cc
new file mode 100644 (file)
index 0000000..79b2106
--- /dev/null
@@ -0,0 +1,610 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/detail/PoolImpl.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <boost/mpl/int.hpp>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/Measure.h"
+#include "zypp/base/WatchFile.h"
+#include "zypp/base/Sysconfig.h"
+#include "zypp/base/IOStream.h"
+
+#include "zypp/ZConfig.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/Capability.h"
+#include "zypp/Locale.h"
+#include "zypp/PoolItem.h"
+
+#include "zypp/target/modalias/Modalias.h"
+#include "zypp/media/MediaPriority.h"
+
+extern "C"
+{
+// Workaround satsolver project not providing a common include
+// directory. (the -devel package does, but the git repo doesn't).
+// #include <satsolver/repo_helix.h>
+void repo_add_helix( ::Repo *repo, FILE *fp, int flags );
+}
+
+using std::endl;
+
+#undef  ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::satpool"
+
+// ///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      // MPL checks for satlib constants we redefine to avoid
+      // includes and defines.
+      BOOST_MPL_ASSERT_RELATION( noId,                 ==, STRID_NULL );
+      BOOST_MPL_ASSERT_RELATION( emptyId,              ==, STRID_EMPTY );
+
+      BOOST_MPL_ASSERT_RELATION( noSolvableId,         ==, ID_NULL );
+      BOOST_MPL_ASSERT_RELATION( systemSolvableId,     ==, SYSTEMSOLVABLE );
+
+      BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
+      BOOST_MPL_ASSERT_RELATION( solvableFileMarker,   ==, SOLVABLE_FILEMARKER );
+
+      BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_AND,       ==, REL_AND );
+      BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_OR,        ==, REL_OR );
+      BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_WITH,      ==, REL_WITH );
+      BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_NAMESPACE, ==, REL_NAMESPACE );
+      BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_ARCH,      ==, REL_ARCH );
+
+     /////////////////////////////////////////////////////////////////
+
+      const std::string & PoolImpl::systemRepoAlias()
+      {
+        static const std::string _val( "@System" );
+        return _val;
+      }
+
+      /////////////////////////////////////////////////////////////////
+
+      static void logSat( struct _Pool *, void *data, int type, const char *logString )
+      {
+         if ( type & (SAT_FATAL|SAT_ERROR) ) {
+           _ERR("satsolver") << logString;
+         } else if ( type & SAT_DEBUG_STATS ) {
+           _DBG("satsolver") << logString;
+         } else {
+           _MIL("satsolver") << logString;
+         }
+      }
+
+      detail::IdType PoolImpl::nsCallback( struct _Pool *, void * data, detail::IdType lhs, detail::IdType rhs )
+      {
+        // lhs:    the namespace identifier, e.g. NAMESPACE:MODALIAS
+        // rhs:    the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
+        // return: 0 if not supportded
+        //         1 if supported by the system
+        //        -1  AFAIK it's also possible to return a list of solvables that support it, but don't know how.
+
+        static const detail::IdType RET_unsupported    = 0;
+        static const detail::IdType RET_systemProperty = 1;
+        switch ( lhs )
+        {
+          case NAMESPACE_LANGUAGE:
+          {
+            static IdString en( "en" );
+            const std::tr1::unordered_set<IdString> & locale2Solver( reinterpret_cast<PoolImpl*>(data)->_locale2Solver );
+            if ( locale2Solver.empty() )
+            {
+              return rhs == en.id() ? RET_systemProperty : RET_unsupported;
+            }
+            return locale2Solver.find( IdString(rhs) ) != locale2Solver.end() ? RET_systemProperty : RET_unsupported;
+          }
+          break;
+
+          case NAMESPACE_MODALIAS:
+          {
+            // modalias strings in capability may be hexencoded because rpm does not allow
+            // ',', ' ' or other special chars.
+            return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
+                ? RET_systemProperty
+              : RET_unsupported;
+          }
+          break;
+
+          case NAMESPACE_FILESYSTEM:
+          {
+            static const Pathname sysconfigStoragePath( "/etc/sysconfig/storage" );
+            static WatchFile      sysconfigFile( sysconfigStoragePath, WatchFile::NO_INIT );
+            static std::set<std::string> requiredFilesystems;
+            if ( sysconfigFile.hasChanged() )
+            {
+              requiredFilesystems.clear();
+              str::split( base::sysconfig::read( sysconfigStoragePath )["USED_FS_LIST"],
+                          std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
+            }
+            return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
+          }
+          break;
+
+          case NAMESPACE_PRODUCTBUDDY:
+          {
+            PoolItem pi( (Solvable(rhs)) );
+            return( pi ? pi.buddy().id() : noId );
+          }
+
+          break;
+        }
+
+        WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
+        return RET_unsupported;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : PoolMember::myPool
+      //       METHOD TYPE : PoolImpl
+      //
+      PoolImpl & PoolMember::myPool()
+      {
+        static PoolImpl _global;
+        return _global;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : PoolImpl::PoolImpl
+      //       METHOD TYPE : Ctor
+      //
+      PoolImpl::PoolImpl()
+      : _pool( ::pool_create() )
+      {
+        MIL << "Creating sat-pool." << endl;
+        if ( ! _pool )
+        {
+          ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
+        }
+        // initialialize logging
+       if ( getenv("ZYPP_LIBSAT_FULLLOG") )
+         ::pool_setdebuglevel( _pool, 4 );
+       else if ( getenv("ZYPP_FULLLOG") )
+         ::pool_setdebuglevel( _pool, 2 );
+       else
+         ::pool_setdebugmask(_pool, SAT_DEBUG_JOB|SAT_DEBUG_STATS);
+
+        ::pool_setdebugcallback( _pool, logSat, NULL );
+
+        // set namespace callback
+        _pool->nscallback = &nsCallback;
+        _pool->nscallbackdata = (void*)this;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       METHOD NAME : PoolImpl::~PoolImpl
+      //       METHOD TYPE : Dtor
+      //
+      PoolImpl::~PoolImpl()
+      {
+        ::pool_free( _pool );
+      }
+
+     ///////////////////////////////////////////////////////////////////
+
+      void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
+      {
+        if ( a1 )
+        {
+          if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
+          else if ( a2 ) MIL << a1 << " " << a2 << endl;
+          else           MIL << a1 << endl;
+        }
+        _serial.setDirty();           // pool content change
+        _availableLocalesPtr.reset(); // available locales may change
+        _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
+
+        // invaldate dependency/namespace related indices:
+        depSetDirty();
+      }
+
+      void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
+      {
+        if ( a1 )
+        {
+          if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
+          else if ( a2 ) MIL << a1 << " " << a2 << endl;
+          else           MIL << a1 << endl;
+        }
+        ::pool_freewhatprovides( _pool );
+      }
+
+      void PoolImpl::prepare() const
+      {
+        if ( _watcher.remember( _serial ) )
+        {
+          // After repo/solvable add/remove:
+          // set pool architecture
+          ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
+        }
+        if ( ! _pool->whatprovides )
+        {
+          MIL << "pool_createwhatprovides..." << endl;
+          ::pool_addfileprovides( _pool );
+          ::pool_createwhatprovides( _pool );
+        }
+        if ( ! _pool->languages )
+        {
+         // initial seting
+         const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
+        }
+      }
+
+      ///////////////////////////////////////////////////////////////////
+
+      ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
+      {
+        setDirty(__FUNCTION__, name_r.c_str() );
+        ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
+        if ( ret && name_r == systemRepoAlias() )
+          ::pool_set_installed( _pool, ret );
+        return ret;
+      }
+
+      void PoolImpl::_deleteRepo( ::_Repo * repo_r )
+      {
+        setDirty(__FUNCTION__, repo_r->name );
+        ::repo_free( repo_r, /*reuseids*/false );
+        eraseRepoInfo( repo_r );
+       if ( isSystemRepo( repo_r ) )
+       {
+         // systemRepo added
+         _onSystemByUserListPtr.reset(); // re-evaluate
+       }
+      }
+
+      int PoolImpl::_addSolv( ::_Repo * repo_r, FILE * file_r )
+      {
+        setDirty(__FUNCTION__, repo_r->name );
+        int ret = ::repo_add_solv( repo_r , file_r );
+        if ( ret == 0 )
+          _postRepoAdd( repo_r );
+        return ret;
+      }
+
+      int PoolImpl::_addHelix( ::_Repo * repo_r, FILE * file_r )
+      {
+        setDirty(__FUNCTION__, repo_r->name );
+        ::repo_add_helix( repo_r , file_r, 0 ); // unfortunately void
+        _postRepoAdd( repo_r );
+        return 0;
+      }
+
+      void PoolImpl::_postRepoAdd( ::_Repo * repo_r )
+      {
+        if ( ! isSystemRepo( repo_r ) )
+        {
+            // Filter out unwanted archs
+          std::set<detail::IdType> sysids;
+          {
+            Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
+            for_( it, sysarchs.begin(), sysarchs.end() )
+              sysids.insert( it->id() );
+
+              // unfortunately satsolver treats src/nosrc as architecture:
+            sysids.insert( ARCH_SRC );
+            sysids.insert( ARCH_NOSRC );
+          }
+
+          detail::IdType blockBegin = 0;
+          unsigned       blockSize  = 0;
+          for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
+          {
+              ::_Solvable * s( _pool->solvables + i );
+              if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
+              {
+                // Remember an unwanted arch entry:
+                if ( ! blockBegin )
+                  blockBegin = i;
+                ++blockSize;
+              }
+              else if ( blockSize )
+              {
+                // Free remembered entries
+                  ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
+                  blockBegin = blockSize = 0;
+              }
+          }
+          if ( blockSize )
+          {
+              // Free remembered entries
+              ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
+              blockBegin = blockSize = 0;
+          }
+        }
+        else
+       {
+         // systemRepo added
+         _onSystemByUserListPtr.reset(); // re-evaluate
+       }
+      }
+
+      detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
+      {
+        setDirty(__FUNCTION__, repo_r->name );
+        return ::repo_add_solvable_block( repo_r, count_r );
+      }
+
+      void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
+      {
+        ::_Repo * repo( getRepo( id_r ) );
+        if ( repo )
+        {
+          bool dirty = false;
+
+          // satsolver priority is based on '<', while yum's repoinfo
+          // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
+          if ( repo->priority != int(-info_r.priority()) )
+          {
+            repo->priority = -info_r.priority();
+            dirty = true;
+          }
+
+         // subpriority is used to e.g. prefer http over dvd iff
+         // both have same priority.
+          int mediaPriority( media::MediaPriority( info_r.url() ) );
+          if ( repo->subpriority != mediaPriority )
+          {
+            repo->subpriority = mediaPriority;
+            dirty = true;
+          }
+
+          if ( dirty )
+            setDirty(__FUNCTION__, info_r.alias().c_str() );
+        }
+        _repoinfos[id_r] = info_r;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+
+      // need on demand and id based Locale
+      void _locale_hack( const LocaleSet & locales_r,
+                         std::tr1::unordered_set<IdString> & locale2Solver )
+      {
+        std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
+        for_( it, locales_r.begin(),locales_r.end() )
+        {
+          for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
+            locale2Solver.insert( IdString( l.code() ) );
+        }
+        MIL << "New Solver Locales: " << locale2Solver << endl;
+      }
+
+      void PoolImpl::setTextLocale( const Locale & locale_r )
+      {
+       std::vector<std::string> fallbacklist;
+       for ( Locale l( locale_r ); l != Locale::noCode; l = l.fallback() )
+       {
+         fallbacklist.push_back( l.code() );
+       }
+       dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
+
+       std::vector<const char *> fallbacklist_cstr;
+       for_( it, fallbacklist.begin(), fallbacklist.end() )
+       {
+         fallbacklist_cstr.push_back( it->c_str() );
+       }
+       ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
+      }
+
+      void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
+      {
+        depSetDirty( "setRequestedLocales" );
+        _requestedLocales = locales_r;
+        MIL << "New RequestedLocales: " << locales_r << endl;
+        _locale_hack( _requestedLocales, _locale2Solver );
+      }
+
+      bool PoolImpl::addRequestedLocale( const Locale & locale_r )
+      {
+        if ( _requestedLocales.insert( locale_r ).second )
+        {
+          depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
+          _locale_hack( _requestedLocales, _locale2Solver );
+          return true;
+        }
+        return false;
+      }
+
+      bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
+      {
+        if ( _requestedLocales.erase( locale_r ) )
+        {
+          depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
+          _locale_hack( _requestedLocales, _locale2Solver );
+          return true;
+        }
+        return false;
+      }
+
+      static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
+      {
+        // Collect locales from any 'namespace:language(lang)' dependency
+        CapDetail detail( cap_r );
+        if ( detail.kind() == CapDetail::EXPRESSION )
+        {
+          switch ( detail.capRel() )
+          {
+            case CapDetail::CAP_AND:
+            case CapDetail::CAP_OR:
+              // expand
+              _getLocaleDeps( detail.lhs(), store_r );
+              _getLocaleDeps( detail.rhs(), store_r );
+              break;
+
+            case CapDetail::CAP_NAMESPACE:
+              if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
+              {
+                store_r.insert( detail.rhs().id() );
+              }
+              break;
+
+            case CapDetail::REL_NONE:
+            case CapDetail::CAP_WITH:
+            case CapDetail::CAP_ARCH:
+              break; // unwanted
+          }
+        }
+      }
+
+      const LocaleSet & PoolImpl::getAvailableLocales() const
+      {
+        if ( !_availableLocalesPtr )
+        {
+          // Collect any 'namespace:language(ja)' dependencies
+          std::tr1::unordered_set<sat::detail::IdType> tmp;
+          Pool pool( Pool::instance() );
+          for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
+          {
+            Capabilities cap( it->supplements() );
+            for_( cit, cap.begin(), cap.end() )
+            {
+              _getLocaleDeps( *cit, tmp );
+            }
+          }
+#warning immediately build LocaleSet as soon as Loale is an Id based type
+          _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
+          for_( it, tmp.begin(), tmp.end() )
+          {
+            _availableLocalesPtr->insert( Locale( IdString(*it) ) );
+          }
+        }
+        return *_availableLocalesPtr;
+      }
+
+      void PoolImpl::multiversionListInit() const
+      {
+        _multiversionListPtr.reset( new MultiversionList );
+        MultiversionList & multiversionList( *_multiversionListPtr );
+
+        const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
+        for_( it, multiversionSpec.begin(), multiversionSpec.end() )
+        {
+          static const std::string prefix( "provides:" );
+          if ( str::hasPrefix( *it, prefix ) )
+          {
+            WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
+            if ( provides.empty() )
+            {
+              MIL << "Multiversion install not provided (" << *it << ")" << endl;
+            }
+            else
+            {
+              for_( pit, provides.begin(), provides.end() )
+              {
+                if ( multiversionList.insert( pit->ident() ).second )
+                  MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
+              }
+            }
+          }
+          else
+          {
+            MIL << "Multiversion install " << *it << endl;
+            multiversionList.insert( IdString( *it ) );
+          }
+        }
+      }
+
+      void PoolImpl::onSystemByUserListInit() const
+      {
+       _onSystemByUserListPtr.reset( new OnSystemByUserList );
+       OnSystemByUserList & onSystemByUserList( *_onSystemByUserListPtr );
+
+       Pathname root( ZConfig::instance().systemRoot() );
+       if ( root.empty() )
+       {
+         MIL << "Target not initialized." << endl;
+         return;
+       }
+       PathInfo pi( root / ZConfig::instance().historyLogFile() );
+       MIL << "onSystemByUserList from history: " << pi << endl;
+       if ( ! pi.isFile() )
+         return;
+
+       // go and parse it: 'who' must constain an '@', then it was installed by user request.
+       // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
+       std::ifstream infile( pi.path().c_str() );
+       for( iostr::EachLine in( infile ); in; in.next() )
+       {
+         const char * ch( (*in).c_str() );
+         // start with year
+         if ( *ch < '1' || '9' < *ch )
+           continue;
+         const char * sep1 = ::strchr( ch, '|' );      // | after date
+         if ( !sep1 )
+           continue;
+         ++sep1;
+         // if logs an install or delete
+         bool installs = true;
+         if ( ::strncmp( sep1, "install|", 8 ) )
+         {
+           if ( ::strncmp( sep1, "remove |", 8 ) )
+             continue; // no install and no remove
+             else
+               installs = false; // remove
+         }
+         sep1 += 8;                                    // | after what
+         // get the package name
+         const char * sep2 = ::strchr( sep1, '|' );    // | after name
+         if ( !sep2 || sep1 == sep2 )
+           continue;
+         (*in)[sep2-ch] = '\0';
+         IdString pkg( sep1 );
+         // we're done, if a delete
+         if ( !installs )
+         {
+           onSystemByUserList.erase( pkg );
+           continue;
+         }
+         // now guess whether user installed or not (3rd next field contains 'user@host')
+         if ( (sep1 = ::strchr( sep2+1, '|' ))         // | after version
+           && (sep1 = ::strchr( sep1+1, '|' ))         // | after arch
+           && (sep2 = ::strchr( sep1+1, '|' )) )       // | after who
+         {
+           (*in)[sep2-ch] = '\0';
+           if ( ::strchr( sep1+1, '@' ) )
+           {
+             // by user
+             onSystemByUserList.insert( pkg );
+             continue;
+           }
+         }
+       }
+       MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/detail/PoolImpl.h b/zypp/sat/detail/PoolImpl.h
new file mode 100644 (file)
index 0000000..09ea430
--- /dev/null
@@ -0,0 +1,299 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/detail/PoolImpl.h
+ *
+*/
+#ifndef ZYPP_SAT_DETAIL_POOLIMPL_H
+#define ZYPP_SAT_DETAIL_POOLIMPL_H
+extern "C"
+{
+#include <satsolver/pool.h>
+#include <satsolver/repo.h>
+#include <satsolver/solvable.h>
+#include <satsolver/poolarch.h>
+#include <satsolver/repo_solv.h>
+}
+#include <iosfwd>
+
+#include "zypp/base/Tr1hash.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/SerialNumber.h"
+#include "zypp/sat/detail/PoolMember.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/Locale.h"
+#include "zypp/Capability.h"
+#include "zypp/IdString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : PoolImpl
+      //
+      /** */
+      class PoolImpl : private base::NonCopyable
+      {
+        public:
+          /** Default ctor */
+          PoolImpl();
+
+          /** Dtor */
+          ~PoolImpl();
+
+          /** Pointer style access forwarded to sat-pool. */
+          ::_Pool * operator->()
+          { return _pool; }
+
+        public:
+          /** Serial number changing whenever the content changes. */
+          const SerialNumber & serial() const
+          { return _serial; }
+
+          /** Update housekeeping data (e.g. whatprovides).
+           * \todo actually requires a watcher.
+           */
+          void prepare() const;
+
+        private:
+          /** Invalidate housekeeping data (e.g. whatprovides) if the
+           *  pools content changed.
+           */
+          void setDirty( const char * a1 = 0, const char * a2 = 0, const char * a3 = 0 );
+
+          /** Invalidate housekeeping data (e.g. whatprovides) if dependencies changed.
+           */
+          void depSetDirty( const char * a1 = 0, const char * a2 = 0, const char * a3 = 0 );
+
+          /** Callback to resolve namespace dependencies (language, modalias, filesystem, etc.). */
+          static detail::IdType nsCallback( ::_Pool *, void * data, detail::IdType lhs, detail::IdType rhs );
+
+        public:
+          /** Reserved system repository alias \c @System. */
+          static const std::string & systemRepoAlias();
+
+          bool isSystemRepo( ::_Repo * repo_r ) const
+          { return repo_r && _pool->installed == repo_r; }
+
+          ::_Repo * systemRepo() const
+          { return _pool->installed; }
+
+        public:
+          /** \name Actions invalidating housekeeping data.
+           *
+           * All methods expect valid arguments being passed.
+           */
+          //@{
+          /** Creating a new repo named \a name_r. */
+          ::_Repo * _createRepo( const std::string & name_r );
+
+          /** Creating a new repo named \a name_r. */
+          void _deleteRepo( ::_Repo * repo_r );
+
+          /** Adding solv file to a repo.
+           * Except for \c isSystemRepo_r, solvables of incompatible architecture
+           * are filtered out.
+          */
+          int _addSolv( ::_Repo * repo_r, FILE * file_r );
+
+          /** Adding helix file to a repo.
+           * Except for \c isSystemRepo_r, solvables of incompatible architecture
+           * are filtered out.
+          */
+          int _addHelix( ::_Repo * repo_r, FILE * file_r );
+
+          /** Adding Solvables to a repo. */
+          detail::SolvableIdType _addSolvables( ::_Repo * repo_r, unsigned count_r );
+          //@}
+
+          /** Helper postprocessing the repo after adding solv or helix files. */
+          void _postRepoAdd( ::_Repo * repo_r );
+
+        public:
+          /** a \c valid \ref Solvable has a non NULL repo pointer. */
+          bool validSolvable( const ::_Solvable & slv_r ) const
+          { return slv_r.repo; }
+          /** \overload Check also for id_r being in range of _pool->solvables. */
+          bool validSolvable( SolvableIdType id_r ) const
+          { return id_r < unsigned(_pool->nsolvables) && validSolvable( _pool->solvables[id_r] ); }
+          /** \overload Check also for slv_r being in range of _pool->solvables. */
+          bool validSolvable( const ::_Solvable * slv_r ) const
+          { return _pool->solvables <= slv_r && slv_r <= _pool->solvables+_pool->nsolvables && validSolvable( *slv_r ); }
+
+        public:
+          ::_Pool * getPool() const
+          { return _pool; }
+
+          /** \todo a quick check whether the repo was meanwhile deleted. */
+          ::_Repo * getRepo( RepoIdType id_r ) const
+          { return id_r; }
+
+          /** Return pointer to the sat-solvable or NULL if it is not valid.
+           * \see \ref validSolvable.
+           */
+          ::_Solvable * getSolvable( SolvableIdType id_r ) const
+          {
+            if ( validSolvable( id_r ) )
+              return &_pool->solvables[id_r];
+            return 0;
+          }
+
+        public:
+          /** Get id of the first valid \ref Solvable.
+           * This is the next valid after the system solvable.
+           */
+          SolvableIdType getFirstId()  const
+          { return getNextId( 1 ); }
+
+          /** Get id of the next valid \ref Solvable.
+           * This goes round robbin. At the end it returns \ref noSolvableId.
+           * Passing \ref noSolvableId it returns the 1st valid  \ref Solvable.
+           * \see \ref validSolvable.
+           */
+          SolvableIdType getNextId( SolvableIdType id_r ) const
+          {
+            for( ++id_r; id_r < unsigned(_pool->nsolvables); ++id_r )
+            {
+              if ( validSolvable( _pool->solvables[id_r] ) )
+                return id_r;
+            }
+            return noSolvableId;
+          }
+
+        public:
+          /** */
+          const RepoInfo & repoInfo( RepoIdType id_r )
+          { return _repoinfos[id_r]; }
+          /** Also adjust repo priority and subpriority accordingly. */
+          void setRepoInfo( RepoIdType id_r, const RepoInfo & info_r );
+          /** */
+          void eraseRepoInfo( RepoIdType id_r )
+          { _repoinfos.erase( id_r ); }
+
+        public:
+          /** Returns the id stored at \c offset_r in the internal
+           * whatprovidesdata array.
+          */
+          const sat::detail::IdType whatProvidesData( unsigned offset_r )
+          { return _pool->whatprovidesdata[offset_r]; }
+
+          /** Returns offset into the internal whatprovidesdata array.
+           * Use \ref whatProvidesData to get the stored Id.
+          */
+          unsigned whatProvides( Capability cap_r )
+          { prepare(); return ::pool_whatprovides( _pool, cap_r.id() ); }
+
+        public:
+          /** \name Requested locales. */
+          //@{
+         void setTextLocale( const Locale & locale_r );
+          void setRequestedLocales( const LocaleSet & locales_r );
+          bool addRequestedLocale( const Locale & locale_r );
+          bool eraseRequestedLocale( const Locale & locale_r );
+
+          const LocaleSet & getRequestedLocales() const
+          { return _requestedLocales; }
+
+          bool isRequestedLocale( const Locale & locale_r ) const
+          {
+            LocaleSet::const_iterator it( _requestedLocales.find( locale_r ) );
+            return it != _requestedLocales.end();
+          }
+
+          const LocaleSet & getAvailableLocales() const;
+
+          bool isAvailableLocale( const Locale & locale_r ) const
+          {
+            const LocaleSet & avl( getAvailableLocales() );
+            LocaleSet::const_iterator it( avl.find( locale_r ) );
+            return it != avl.end();
+          }
+          //@}
+
+        public:
+          /** \name Multiversion install. */
+          //@{
+          typedef IdStringSet MultiversionList;
+
+          const MultiversionList & multiversionList() const
+          {
+            if ( ! _multiversionListPtr )
+              multiversionListInit();
+            return *_multiversionListPtr;
+          }
+
+          bool isMultiversion( IdString ident_r ) const
+          {
+            const MultiversionList & l( multiversionList() );
+            return l.find( ident_r ) != l.end();
+          }
+          //@}
+
+        public:
+          /** \name Installed on behalf of a user request hint. */
+          //@{
+          typedef IdStringSet OnSystemByUserList;
+
+          const OnSystemByUserList & onSystemByUserList() const
+          {
+            if ( ! _onSystemByUserListPtr )
+             onSystemByUserListInit();
+           return *_onSystemByUserListPtr;
+          }
+
+          bool isOnSystemByUser( IdString ident_r ) const
+          {
+            const OnSystemByUserList & l( onSystemByUserList() );
+            return l.find( ident_r ) != l.end();
+          }
+          //@}
+
+        private:
+          /** sat-pool. */
+          ::_Pool * _pool;
+          /** Serial number. */
+          SerialNumber _serial;
+          /** Watch serial number. */
+          SerialNumberWatcher _watcher;
+          /** Additional \ref RepoInfo. */
+          std::map<RepoIdType,RepoInfo> _repoinfos;
+
+          /**  */
+          LocaleSet _requestedLocales;
+          mutable scoped_ptr<LocaleSet> _availableLocalesPtr;
+          mutable std::tr1::unordered_set<IdString> _locale2Solver;
+
+          /**  */
+          void multiversionListInit() const;
+          mutable scoped_ptr<MultiversionList> _multiversionListPtr;
+
+          /**  */
+          void onSystemByUserListInit() const;
+          mutable scoped_ptr<OnSystemByUserList> _onSystemByUserListPtr;
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#define POOL_SETDIRTY
+#endif // ZYPP_SAT_DETAIL_POOLIMPL_H
diff --git a/zypp/sat/detail/PoolMember.h b/zypp/sat/detail/PoolMember.h
new file mode 100644 (file)
index 0000000..0cd0fdc
--- /dev/null
@@ -0,0 +1,131 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/detail/PoolMember.h
+ *
+*/
+#ifndef ZYPP_SAT_DETAIL_POOLMEMBER_H
+#define ZYPP_SAT_DETAIL_POOLMEMBER_H
+
+#include "zypp/base/Tr1hash.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Easy.h"
+
+extern "C"
+{
+struct _Solvable;
+struct _Repo;
+struct _Pool;
+}
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  class IdString;
+  class Capability;
+  class Capabilities;
+  class Repository;
+  class RepoInfo;
+
+  ///////////////////////////////////////////////////////////////////
+  namespace detail
+  {
+    class RepoIterator;
+    class ByRepository;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    class Pool;
+    class Solvable;
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      class PoolImpl;
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : PoolMember
+      //
+      /** Backlink to the associated \ref PoolImpl.
+       * Simple as we currently use one global PoolImpl. If we change our
+       * minds this is where we'd store and do the \c Id to \ref PoolImpl
+       * mapping.
+       */
+      struct PoolMember
+      {
+        static PoolImpl & myPool();
+      };
+      ///////////////////////////////////////////////////////////////////
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      /** Generic Id type. */
+      typedef int IdType;
+      static const IdType noId( 0 );
+      static const IdType emptyId( 1 );
+
+      /** Internal ids satlib includes in dependencies.
+       * MPL check in PoolImpl.cc
+      */
+      static const IdType solvablePrereqMarker( 15 );
+      static const IdType solvableFileMarker  ( 16 );
+      /** Test for internal ids satlib includes in dependencies. */
+      inline bool isDepMarkerId( IdType id_r )
+      { return( id_r == solvablePrereqMarker || id_r == solvableFileMarker ); }
+
+      /** Id type to connect \ref Solvable and sat-solvable.
+       * Indext into solvable array.
+      */
+      typedef unsigned SolvableIdType;
+      typedef SolvableIdType size_type;
+      /** Id to denote \ref Solvable::noSolvable. */
+      static const SolvableIdType noSolvableId( 0 );
+      /** Id to denote the usually hidden \ref Solvable::systemSolvable. */
+      static const SolvableIdType systemSolvableId( 1 );
+
+      /** Id type to connect \ref Repo and sat-repo. */
+      typedef ::_Repo * RepoIdType;
+      /** Id to denote \ref Repo::noRepository. */
+      static const RepoIdType noRepoId( 0 );
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    { /////////////////////////////////////////////////////////////////
+
+      class SolvableIterator;
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_DETAIL_POOLMEMBER_H
diff --git a/zypp/solver/detail/Helper.cc b/zypp/solver/detail/Helper.cc
new file mode 100644 (file)
index 0000000..37a20da
--- /dev/null
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Helper.cc
+ *
+ * Static helpers
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <sstream>
+
+#include "zypp/solver/detail/Helper.h"
+#include "zypp/Capabilities.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/VendorAttr.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/RepoInfo.h"
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+ostream &
+operator<< (ostream & os, const PoolItemList & itemlist)
+{
+    for (PoolItemList::const_iterator iter = itemlist.begin(); iter != itemlist.end(); ++iter) {
+       if (iter != itemlist.begin())
+           os << ", ";
+       os << *iter;
+    }
+    return os;
+}
+
+
+class LookFor : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    PoolItem item;
+
+    bool operator()( PoolItem provider )
+    {
+       item = provider;
+       return false;                           // stop here, we found it
+    }
+};
+
+
+// just find installed item with same kind/name as item
+
+template<class _Iter>
+static PoolItem findInstalledByNameAndKind ( _Iter begin, _Iter end, const string & name, const Resolvable::Kind & kind)
+{
+    LookFor info;
+
+    invokeOnEach(begin, end,
+                 resfilter::ByInstalled (),                                    // ByInstalled
+                 functor::functorRef<bool,PoolItem> (info) );
+
+    _XDEBUG("Helper::findInstalledByNameAndKind (" << name << ", " << kind << ") => " << info.item);
+    return info.item;
+
+}
+
+PoolItem Helper::findInstalledByNameAndKind (const ResPool & pool, const string & name, const Resolvable::Kind & kind)
+{ return detail::findInstalledByNameAndKind( pool.byIdentBegin( kind, name ), pool.byIdentEnd( kind, name ), name, kind ); }
+
+PoolItem Helper::findInstalledItem (const ResPool & pool, PoolItem item)
+{ return findInstalledByNameAndKind(pool, item->name(), item->kind() ); }
+
+PoolItem Helper::findInstalledItem( const std::vector<PoolItem> & pool, PoolItem item )
+{ return detail::findInstalledByNameAndKind( pool.begin(), pool.end(), item->name(), item->kind() ); }
+
+
+// just find uninstalled item with same kind/name as item
+
+PoolItem
+Helper::findUninstalledByNameAndKind (const ResPool & pool, const string & name, const Resolvable::Kind & kind)
+{
+    LookFor info;
+
+    invokeOnEach( pool.byIdentBegin( kind, name ),
+                 pool.byIdentEnd( kind, name ),
+                 resfilter::ByUninstalled(),                                   // ByUninstalled
+                 functor::functorRef<bool,PoolItem> (info) );
+
+    _XDEBUG("Helper::findUninstalledByNameAndKind (" << name << ", " << kind << ") => " << info.item);
+    return info.item;
+}
+
+
+//----------------------------------------------------------------------------
+
+class LookForUpdate : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    PoolItem uninstalled;
+    PoolItem installed;
+
+    bool operator()( PoolItem provider )
+    {
+        // is valid
+        if ( ! provider.resolvable() )
+        {
+          WAR << "Warning: '" << provider << "' not valid" << endl;
+          return true;
+        }
+
+        if ( installed.resolvable() )
+        {
+          if ( !VendorAttr::instance().equivalent( installed, provider ) )
+          {
+            MIL << "Discarding '" << provider << "' from vendor '"
+                << provider->vendor() << "' different to uninstalled '"
+                << installed->vendor() << "' vendor." << endl;
+            return true;
+          }
+        }
+
+       if ((!uninstalled                                                       // none yet
+           || (uninstalled->edition().compare( provider->edition() ) < 0)      // or a better edition
+           || (uninstalled->arch().compare( provider->arch() ) < 0) ) // or a better architecture
+           && !provider.status().isLocked() )                                  // is not locked
+       {
+           uninstalled = provider;                                             // store
+       }
+       return true;
+    }
+};
+
+
+// just find best (according to edition) uninstalled item with same kind/name as item
+// *DOES* check edition
+
+template<class _Iter>
+static PoolItem findUpdateItem( _Iter begin, _Iter end, PoolItem item )
+{
+    LookForUpdate info;
+    info.installed = item;
+
+    invokeOnEach( begin, end,
+                 functor::chain (resfilter::ByUninstalled (),                                          // ByUninstalled
+                                 resfilter::byEdition<CompareByGT<Edition> >( item->edition() )),      // only look at better editions
+                 functor::functorRef<bool,PoolItem> (info) );
+
+    _XDEBUG("Helper::findUpdateItem(" << item << ") => " << info.uninstalled);
+    return info.uninstalled;
+}
+
+PoolItem Helper::findUpdateItem (const ResPool & pool, PoolItem item)
+{ return detail::findUpdateItem( pool.byIdentBegin( item ), pool.byIdentEnd( item ), item ); }
+
+PoolItem Helper::findUpdateItem (const std::vector<PoolItem> & pool, PoolItem item)
+{ return detail::findUpdateItem( pool.begin(), pool.end(), item ); }
+
+
+//----------------------------------------------------------------------------
+
+class LookForReinstall : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    PoolItem uninstalled;
+
+    bool operator()( PoolItem provider )
+    {
+       if (provider.status().isLocked()) {
+           return true; // search next
+       } else {
+           uninstalled = provider;
+           return false;                               // stop here, we found it
+       }
+    }
+};
+
+
+PoolItem
+Helper::findReinstallItem (const ResPool & pool, PoolItem item)
+{
+    LookForReinstall info;
+
+    invokeOnEach( pool.byIdentBegin( item ),
+                 pool.byIdentEnd( item ),
+                 functor::chain (resfilter::ByUninstalled (),                                          // ByUninstalled
+                                 resfilter::byEdition<CompareByEQ<Edition> >( item->edition() )),
+                 functor::functorRef<bool,PoolItem> (info) );
+
+    _XDEBUG("Helper::findReinstallItem(" << item << ") => " << info.uninstalled);
+    return info.uninstalled;
+}
+
+//----------------------------------------------------------------------------
+
+class CheckIfBest : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    PoolItem _item;
+    bool is_best;
+
+    CheckIfBest( PoolItem item )
+       : _item( item )
+       , is_best( true )               // assume we already have the best
+    {}
+
+    // check if provider is better. If yes, end the search.
+
+    bool operator()( PoolItem provider )
+    {
+       int archcmp = _item->arch().compare( provider->arch() );
+       if (((archcmp < 0)                                                      // provider has a better architecture
+            || ((archcmp == 0)
+                && (_item->edition().compare( provider->edition() ) < 0)))     // or a better edition
+           && !provider.status().isLocked())                                   // and is not locked
+       {
+           is_best = false;
+           return false;
+       }
+       return true;
+    }
+};
+
+
+// check if the given item is the best one of the pool
+
+bool
+Helper::isBestUninstalledItem (const ResPool & pool, PoolItem item)
+{
+    CheckIfBest info( item );
+
+    invokeOnEach( pool.byIdentBegin( item ),
+                 pool.byIdentEnd( item ),
+                 resfilter::ByUninstalled(),                   // ByUninstalled
+                 functor::functorRef<bool,PoolItem>( info ) );
+
+    _XDEBUG("Helper::isBestUninstalledItem(" << item << ") => " << info.is_best);
+    return info.is_best;
+}
+
+std::string
+Helper::itemToString (PoolItem item, bool shortVersion)
+{
+    ostringstream os;
+    if (!item) return "";
+
+    if (item->kind() != ResKind::package)
+       os << item->kind() << ':';
+    os  << item->name();
+    if (!shortVersion) {
+       os << '-' << item->edition();
+       if (item->arch() != "") {
+           os << '.' << item->arch();
+       }
+
+       string alias = item->repoInfo().alias();
+       if (!alias.empty()
+           && alias != "@System")
+       {
+           os << '[' << alias << ']';
+       }
+    }
+    return os.str();
+}
+
+std::string
+Helper::capToString (const Capability & capability)
+{
+    ostringstream os;
+    os << capability.asString();
+    return os.str();
+}
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
diff --git a/zypp/solver/detail/Helper.h b/zypp/solver/detail/Helper.h
new file mode 100644 (file)
index 0000000..0204a13
--- /dev/null
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Helper.h
+ *
+ * Static helpers
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_HELPER_H
+#define ZYPP_SOLVER_DETAIL_HELPER_H
+
+#include <iosfwd>
+
+#include "zypp/ResPool.h"
+#include "zypp/PoolItem.h"
+#include "zypp/Capabilities.h"
+#include "zypp/base/String.h"
+#include "zypp/solver/detail/Types.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Helper
+
+class Helper {
+  public:
+
+    // for name, find installed item which has same name
+    // does *NOT* check edition
+    //  FIXME: should probably take provides/obsoletes into account for
+    //        renamed upgrades
+    static PoolItem findInstalledByNameAndKind (const ResPool & pool, const std::string & name, const Resolvable::Kind & kind);
+
+    // for name, find uninstalled item which has same name
+    static PoolItem findUninstalledByNameAndKind (const ResPool & pool, const std::string & name, const Resolvable::Kind & kind);
+
+    // for item, find installed item which has same name -> calls findInstalledByNameAndKind()
+    // does *NOT* check edition
+    //  FIXME: should probably take provides/obsoletes into account for
+    //        renamed upgrades
+    static PoolItem findInstalledItem (const ResPool & pool, PoolItem item);
+    /** \overload Using ident cache entry. */
+    static PoolItem findInstalledItem (const std::vector<PoolItem> & pool, PoolItem item);
+
+    // for item, find uninstalled item which has same name and higher edition
+    static PoolItem findUninstalledItem (const ResPool & pool, PoolItem item);
+
+    // for item, find uninstalled item which has same name and equal edition
+    static PoolItem findReinstallItem (const ResPool & pool, PoolItem item);
+
+    static PoolItem findUpdateItem (const ResPool & pool, PoolItem item);
+    /** \overload Using ident cache entry. */
+    static PoolItem findUpdateItem (const std::vector<PoolItem> & pool, PoolItem item);
+
+    // for item, check if this is the 'best' uninstalled (best arch, best version) item
+    static bool isBestUninstalledItem (const ResPool & pool, PoolItem item);
+
+    // Human readable item
+    static std::string itemToString (PoolItem item, bool shortVersion=false);
+    static std::string capToString (const Capability & capability);
+
+    friend std::ostream& operator<<(std::ostream&, const PoolItemList & itemlist);
+
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_HELPER_H
diff --git a/zypp/solver/detail/InstallOrder.cc b/zypp/solver/detail/InstallOrder.cc
new file mode 100644 (file)
index 0000000..d1cdbf1
--- /dev/null
@@ -0,0 +1,376 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* InstallOrder.cc
+ *
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+// stolen from yast2-packagemanager
+/*
+   File:       InstallOrder.h
+   Purpose:    Determine order for installing packages
+   Author:     Ludwig Nussel <lnussel@suse.de>
+   Maintainer: Ludwig Nussel <lnussel@suse.de>
+
+/-*/
+
+#include "zypp/solver/detail/InstallOrder.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Algorithm.h"
+
+#include "zypp/solver/detail/SATResolver.h"
+
+#include "zypp/ResFilters.h"
+#include "zypp/ResStatus.h"
+#include "zypp/sat/Pool.h"
+#include <zypp/sat/WhatObsoletes.h>
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+using namespace zypp;
+
+#define ITEMNAME(item) (item)->name()
+//-----------------------------------------------------------------------------
+
+InstallOrder::InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed )
+    : _pool( pool )
+    , _toinstall( toinstall )
+    , _installed( installed )
+    , _dirty (true)
+    , _numrun (0)
+{
+    _DEBUG("InstallOrder::InstallOrder(_toinstall " << _toinstall.size() << " items, _installed " << _installed.size() << " items)");
+}
+
+
+//-----------------------------------------------------------------------------
+
+void
+InstallOrder::printAdj (std::ostream& os, bool reversed) const
+{
+    const Graph& g = (reversed ? _rgraph : _graph);
+    os << "digraph pkgdeps {" << endl;
+    for (Graph::const_iterator gcit = g.begin(); gcit != g.end(); ++gcit)
+    {
+       Nodes::const_iterator niit = _nodes.find(gcit->first);
+       int order = niit->second.order;
+       string name = gcit->first->name();
+       os << "\"" << name << "\"" << "[label=\"" << name << "\\n" << order << "\"";
+       os << "] " << endl;
+       for (PoolItemList::const_iterator scit = gcit->second.begin(); scit != gcit->second.end(); ++scit)
+       {
+           os << "\"" << name << "\" -> \"" << (*scit)->name() << "\"" << endl;
+       }
+    }
+    os << "}" << endl;
+}
+
+
+//-----------------------------------------------------------------------------
+
+// yea, that stuff is suboptimal. there should be a heap sorted by order
+PoolItemList
+InstallOrder::computeNextSet()
+{
+    PoolItemList newlist;
+
+    if (_dirty) startrdfs();
+
+    for (Nodes::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
+    {
+       if (it->second.order == 0
+           && it->second.item)                 // the default Nodes constructor leaves this empty
+       {
+           XXX << "InstallOrder::computeNextSet found " << ITEMNAME(it->second.item) << endl;
+
+           newlist.push_back(it->second.item);
+       }
+    }
+
+    return newlist;
+}
+
+
+// decrease order of every adjacent node
+void
+InstallOrder::setInstalled(PoolItem item )
+{
+    _dirty = true;
+
+    PoolItemList adj = _rgraph[item];
+
+    XXX << "InstallOrder::setInstalled " << ITEMNAME(item) << endl;
+
+    // order will be < 0
+    _nodes[item].order--;
+    _installed.insert( item );
+    _toinstall.erase( item );
+
+    for (PoolItemList::iterator it = adj.begin(); it != adj.end(); ++it)
+    {
+       NodeInfo& info = _nodes[*it];
+       info.order--;
+       if (info.order < 0)
+       {
+           WAR << "order of node " << (*it) << " is < 0" << endl;
+       }
+    }
+}
+
+
+void
+InstallOrder::setInstalled( const PoolItemList & rl )
+{
+    for (PoolItemList::const_iterator it = rl.begin(); it != rl.end(); ++it)
+    {
+       setInstalled(*it);
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+bool
+InstallOrder::doesProvide( const Capability requirement, PoolItem item ) const
+{
+    Capabilities::const_iterator pend = item->dep( Dep::PROVIDES ).end();
+    for( Capabilities::const_iterator pit = item->dep( Dep::PROVIDES ).begin(); pit != pend; ++pit) {
+       if( pit->matches( requirement ) == CapMatch::yes ) {
+           return item;
+       }
+    }
+    return PoolItem();
+}
+
+
+PoolItem
+InstallOrder::findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const
+{
+    for( PoolItemSet::const_iterator citer = candidates.begin(); citer != candidates.end(); citer++) {
+       if( doesProvide( requirement, *citer ) ) {
+           return *citer;
+       }
+    }
+
+    return PoolItem();
+}
+
+//-----------------------------------------------------------------------------
+
+
+void
+InstallOrder::rdfsvisit (const PoolItem item)
+{
+    typedef list<Capability> CapList;
+    CapList requires;
+
+    XXX << "InstallOrder::rdfsvisit, visiting " << ITEMNAME(item) << endl;
+
+    NodeInfo& nodeinfo = _nodes[item];
+
+    nodeinfo.visited = true;
+    nodeinfo.begintime = _rdfstime;
+    _rdfstime++;
+
+    // items prereq
+    CapabilitySet prq( item->dep(Dep::PREREQUIRES).begin(), item->dep(Dep::PREREQUIRES).end() );
+    // an installed items prereq (in case they are reqired for uninstall scripts)
+    ui::Selectable::Ptr sel( ui::Selectable::get( item ) );
+    for_( it, sel->installedBegin(), sel->installedEnd() )
+    {
+      Capabilities p( it->satSolvable().prerequires() );
+      prq.insert( p.begin(), p.end() );
+    }
+    // any obsoleted items prereq (in case they are reqired for uninstall scripts)
+    sat::WhatObsoletes obs( item );
+    for_( it, obs.begin(), obs.end() )
+    {
+      Capabilities p( it->prerequires() );
+      prq.insert( p.begin(), p.end() );
+    }
+    // put prerequires first and requires last on list to ensure
+    // that prerequires are processed first
+    for (CapabilitySet::const_iterator it = prq.begin(); it != prq.end(); ++it)
+    {
+       requires.push_back(*it);
+    }
+
+    // Product requirements are ignored to assert Product gets installed
+    // as early as possible. Some stuff depends on it (e.g. registration).
+    if ( ! isKind<Product>( item.resolvable() ) )
+      {
+        for (Capabilities::const_iterator it = item->dep (Dep::REQUIRES).begin(); it != item->dep (Dep::REQUIRES).end(); ++it)
+          {
+            requires.push_back(*it);
+          }
+      }
+
+    for (CapList::const_iterator iter = requires.begin(); iter != requires.end(); ++iter)
+    {
+        bool goBack = false;
+       const Capability requirement = *iter;
+        PoolItemList providers;
+
+       XXX << "check requirement " << requirement << " of " << ITEMNAME(item) << endl;
+        SATResolver satResolver(_pool, sat::Pool::instance().get());
+       PoolItemList tovisit;
+        sat::WhatProvides possibleProviders(requirement);
+
+       // first, look in _installed
+        for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
+            PoolItem provider = ResPool::instance().find( *iter );
+            if ( provider == item )
+            {
+              goBack = true;
+              break;
+            }
+            if (_installed.find( provider ) != _installed.end())       // and is not installed
+            {
+                XXX << "tovisit " << ITEMNAME(provider) << endl;
+                providers.push_back (provider);
+            }
+        }
+
+        if ( goBack )
+          continue;
+
+       // if not found in _installed, look in _toinstall
+
+       if (providers.empty()) {
+            for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
+                PoolItem provider = ResPool::instance().find( *iter );
+                if ((provider.resolvable() != item.resolvable())               // resolvable could provide its own requirement
+                    && (_toinstall.find( provider ) != _toinstall.end()))      // and is not to be installed
+                {
+                    XXX << "tovisit " << ITEMNAME(provider) << endl;
+                    tovisit.push_back (provider);
+                }
+            }
+       }
+
+       for (PoolItemList::iterator it = tovisit.begin(); it != tovisit.end(); ++it)
+       {
+           const PoolItem must_visit = *it;
+           if (_nodes[must_visit].visited == false)
+           {
+               nodeinfo.order++;
+               _rgraph[must_visit].push_back( item );
+               _graph[item].push_back( must_visit );
+               rdfsvisit( must_visit );
+           }
+           else if (_nodes[must_visit].endtime == 0)
+           {
+               if (must_visit != item)
+               {
+                 // log only the 1st occurrence.
+                 std::string lstr( ITEMNAME(item) );
+                 lstr += " -> ";
+                 lstr += ITEMNAME(must_visit);
+                 if ( _logset.insert( lstr ).second )
+                 {
+                   WAR << "** dependency loop: " << lstr << endl;
+                 }
+               }
+           }
+           else
+           {
+               // filter multiple depends on same item (cosmetic)
+               PoolItemList & lrg = _rgraph[must_visit];
+               if( find( lrg.begin(), lrg.end(), item) == lrg.end() )
+               {
+                   nodeinfo.order++;
+                   lrg.push_back( item );
+
+                   PoolItemList & lg = _graph[item];
+                   if( find( lg.begin(), lg.end(), must_visit ) == lg.end() )
+                       lg.push_back( must_visit );
+               }
+           }
+       }
+    }
+    _topsorted.push_back(item);
+    _nodes[item].endtime = _rdfstime;
+    _rdfstime++;
+
+    XXX << ITEMNAME(item) << " done" << endl;
+}
+
+
+void
+InstallOrder::startrdfs()
+{
+    _nodes.clear();
+    _rgraph.clear();
+    _graph.clear();
+
+    _rdfstime = 1;
+
+    _topsorted.clear();
+
+    _numrun++;
+    XXX << "run #" << _numrun << endl;
+
+    // initialize all nodes
+    for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
+    {
+       PoolItem item = *it;
+       _nodes[item] = NodeInfo (item);
+       _rgraph[item] = PoolItemList();
+       _graph[item] = PoolItemList();
+    }
+
+    // visit all nodes
+    for (PoolItemSet::iterator it = _toinstall.begin(); it != _toinstall.end(); ++it)
+    {
+       const PoolItem item = *it;
+       if (_nodes[item].visited == false)
+       {
+           XXX << "start recursion on " << ITEMNAME(item) << endl;
+           rdfsvisit (item);
+       }
+    }
+
+    _dirty = false;
+}
+
+
+//-----------------------------------------------------------------------------
+
+const PoolItemList
+InstallOrder::getTopSorted() const
+{
+    return _topsorted;
+}
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/InstallOrder.h b/zypp/solver/detail/InstallOrder.h
new file mode 100644 (file)
index 0000000..ce9fa8e
--- /dev/null
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* InstallOrder.h
+ *
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+// stolen from yast2-packagemanager
+/*
+   File:       InstallOrder.h
+   Purpose:    Determine order for installing packages
+   Author:     Ludwig Nussel <lnussel@suse.de>
+   Maintainer: Ludwig Nussel <lnussel@suse.de>
+
+/-*/
+
+#ifndef ZYPP_SOLVER_DETAIL_INSTALLORDER_H
+#define ZYPP_SOLVER_DETAIL_INSTALLORDER_H
+
+#include <string>
+#include <list>
+#include <map>
+#include <set>
+
+#include "zypp/PoolItem.h"
+#include "zypp/ResPool.h"
+#include "zypp/Capabilities.h"
+
+#include "zypp/solver/detail/Types.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+/**
+ * compute Installation order.<br>
+ *
+ * There are two Interfaces:<br>
+ * - getTopSorted: return flat list of packages in proper order<br>
+ * - computeNextSet: return only packages without requirements, see comment below<br>
+ * */
+
+class InstallOrder
+{
+    private:
+       const ResPool & _pool;
+       PoolItemSet _toinstall;
+       PoolItemSet _installed;
+
+       /** adjacency list type */
+       typedef std::map<PoolItem, PoolItemList> Graph;
+
+       /** adjacency list, package -> requirements */
+       Graph _graph;
+
+       /** reversed graph, package -> referers */
+       Graph _rgraph;
+
+       struct NodeInfo
+       {
+           unsigned begintime;
+           unsigned endtime;
+           bool visited;
+           int order; // number of incoming edges in reverse graph
+
+           PoolItem item;
+
+           NodeInfo() : begintime(0), endtime(0), visited(false), order(0) {}
+           NodeInfo(PoolItem item) : begintime(0), endtime(0), visited(false), order(0), item(item) {}
+       };
+
+       typedef std::map<PoolItem, NodeInfo> Nodes;
+
+       Nodes _nodes;
+
+       unsigned _rdfstime;
+
+       PoolItemList _topsorted;
+
+       bool _dirty;
+
+       unsigned _numrun;
+
+       std::set<std::string> _logset;
+
+    private:
+       void rdfsvisit (PoolItem item);
+
+       PoolItem findProviderInSet( const Capability requirement, const PoolItemSet & candidates ) const;
+       bool doesProvide( const Capability requirement, PoolItem item ) const;
+
+    public:
+
+       /**
+        * Constructor
+        *
+        * @param toinstall Set of ResItems that have to be installed
+        * @param installed Set of ResItems that are already installed
+        * */
+       InstallOrder( const ResPool & pool, const PoolItemSet & toinstall, const PoolItemSet & installed);
+
+       /**
+        * Compute a list of ResItems which have no requirements and can be
+        * installed in parallel without conflicts. Use setInstalled to make
+        * computation of a different set possible */
+       PoolItemList computeNextSet();
+
+       /**
+        * set a Solvable as installed, computeNextSet is able to compute a new
+        * set then
+        * */
+       void setInstalled( PoolItem item );
+
+       /**
+        * like above, for convenience
+        * */
+       void setInstalled( const PoolItemList & list );
+
+       /**
+        * recoursive depth first search, build internal trees
+        * */
+       void startrdfs();
+
+       /**
+        * Initialize data structures. Must be called before any other
+        * function.
+        * */
+       void init() { startrdfs(); }
+
+       /**
+        * compute topological sorted list
+        *
+        * @return list of resolvables in an installable order
+        * */
+       const PoolItemList getTopSorted() const;
+
+       void printAdj (std::ostream & os, bool reversed = false) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_INSTALLORDER_H
diff --git a/zypp/solver/detail/ProblemSolutionCombi.cc b/zypp/solver/detail/ProblemSolutionCombi.cc
new file mode 100644 (file)
index 0000000..5b9880c
--- /dev/null
@@ -0,0 +1,122 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* ProblemSolution.cc
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <sstream>
+
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+
+#include "zypp/solver/detail/ProblemSolutionCombi.h"
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(ProblemSolutionCombi);
+
+//---------------------------------------------------------------------------
+
+ProblemSolutionCombi::ProblemSolutionCombi( ResolverProblem_Ptr parent)
+    : ProblemSolution (parent, "", "")
+      , actNumber(0)
+{
+    _description = "";
+    _details = "";
+}
+
+void ProblemSolutionCombi::addSingleAction( Capability capability, const TransactionKind action)
+{
+    addAction (new TransactionSolutionAction(capability, action));
+    actNumber++;
+}
+
+void ProblemSolutionCombi::addSingleAction( PoolItem item, const TransactionKind action)
+{
+    addAction (new TransactionSolutionAction(item, action));
+    actNumber++;
+}
+
+void ProblemSolutionCombi::addSingleAction( SolverQueueItem_Ptr item, const TransactionKind action)
+{
+    addAction (new TransactionSolutionAction(item, action));
+    actNumber++;
+}
+
+void ProblemSolutionCombi::addDescription( const std::string description)
+{
+    if ( _description.size() == 0
+        && _details.size() == 0) {
+        // first entry
+       _description = description;
+    } else {
+       if ( _description.size() > 0
+            && _details.size() == 0) {
+           // second entry
+           _details = _description;
+           _description = _("Following actions will be done:");
+       }
+       // all other
+       _details += "\n";
+       _details += description;
+    }
+}
+
+void ProblemSolutionCombi::addFrontDescription( const std::string & description )
+{
+    if ( _description.size() == 0
+        && _details.size() == 0) {
+        // first entry
+       _description = description;
+    } else {
+       if ( _description.size() > 0
+            && _details.size() == 0) {
+           // second entry
+           _details = _description;
+           _description = _("Following actions will be done:");
+       }
+       // all other
+        std::string tmp( _details );
+       _details = description;
+       _details += "\n";
+        _details += tmp;
+    }
+}
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/ProblemSolutionCombi.h b/zypp/solver/detail/ProblemSolutionCombi.h
new file mode 100644 (file)
index 0000000..1e9875e
--- /dev/null
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Resolver_problems.cc
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONINSTALL_H
+#define ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONINSTALL_H
+
+#include <string>
+#include "zypp/ProblemSolution.h"
+#include "zypp/solver/detail/Types.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Class representing one possible solution to one problem found during resolving
+        * This problem solution is a combination of different actions.
+        * e.G. install, delete, keep different resolvables.
+        *
+        **/
+       class ProblemSolutionCombi : public ProblemSolution
+       {
+        protected:
+           int actNumber; // number of actions
+       public:
+
+           /**
+            * Constructor.
+            **/
+           ProblemSolutionCombi( ResolverProblem_Ptr parent );
+           /**
+            * Add a single action of an item
+            */
+           void addSingleAction( PoolItem item, const TransactionKind action);
+
+           /**
+            * Add a single action of a capability
+            */
+           void addSingleAction( Capability capability, const TransactionKind action);
+
+           /**
+            * Add a single action of a SolverQueueItem
+            */
+           void addSingleAction( SolverQueueItem_Ptr item, const TransactionKind action);
+
+           /**
+            * returns the number of actions
+            */
+           int actionCount() { return actNumber;}
+
+           /**
+            * Set description text (append)
+            */
+           void addDescription( const std::string description);
+
+           /**
+            * Set description text (prepend)
+            */
+           void addFrontDescription( const std::string & description );
+       };
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONAINSTALL_H
+
diff --git a/zypp/solver/detail/ProblemSolutionIgnore.cc b/zypp/solver/detail/ProblemSolutionIgnore.cc
new file mode 100644 (file)
index 0000000..cecdd43
--- /dev/null
@@ -0,0 +1,76 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* ProblemSolution.cc
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/ProblemSolutionIgnore.h"
+#include "zypp/solver/detail/Helper.h"
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+IMPL_PTR_TYPE(ProblemSolutionIgnore);
+
+//---------------------------------------------------------------------------
+
+ProblemSolutionIgnore::ProblemSolutionIgnore( ResolverProblem_Ptr parent,
+                                             PoolItem item )
+    : ProblemSolution (parent, "", "")
+{
+    // TranslatorExplanation %s = name of package, patch, selection ...
+    _description = str::form (_("break %s by ignoring some of its dependencies"), item->name().c_str() );
+
+    addAction ( new InjectSolutionAction (item, WEAK));
+}
+
+ProblemSolutionIgnore::ProblemSolutionIgnore( ResolverProblem_Ptr parent,
+                                             PoolItemList itemList )
+    : ProblemSolution (parent, "", "")
+{
+       _description = _("generally ignore of some dependecies");
+       for (PoolItemList::const_iterator iter = itemList.begin();
+            iter != itemList.end(); iter++) {
+           addAction ( new InjectSolutionAction (*iter, WEAK));
+       }
+}
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/ProblemSolutionIgnore.h b/zypp/solver/detail/ProblemSolutionIgnore.h
new file mode 100644 (file)
index 0000000..3765023
--- /dev/null
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Resolver_problems.cc
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONIGNORE_H
+#define ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONIGNORE_H
+
+#include "zypp/solver/detail/Types.h"
+#include "zypp/ProblemSolution.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Class representing one possible solution to one problem found during resolving
+        * This problem solution ignores one or more items by setting his dependencies
+        * to weak
+        **/
+       class ProblemSolutionIgnore : public ProblemSolution
+       {
+       public:
+
+           /**
+            * Constructor.
+            **/
+           ProblemSolutionIgnore( ResolverProblem_Ptr parent,
+                                  PoolItem item );
+           ProblemSolutionIgnore( ResolverProblem_Ptr parent,
+                                  PoolItemList itemList);          
+       };
+
+       
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_PROBLEMSOLUTIONIGNORE_H
+
diff --git a/zypp/solver/detail/Resolver.cc b/zypp/solver/detail/Resolver.cc
new file mode 100644 (file)
index 0000000..11b5993
--- /dev/null
@@ -0,0 +1,600 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Resolver.cc
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <boost/static_assert.hpp>
+
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/Helper.h"
+#include "zypp/solver/detail/Testcase.h"
+#include "zypp/solver/detail/SATResolver.h"
+
+#include "zypp/Capabilities.h"
+#include "zypp/ZConfig.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/sat/Transaction.h"
+
+#define MAXSOLVERRUNS 5
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(Resolver);
+
+
+//---------------------------------------------------------------------------
+
+
+std::ostream & Resolver::dumpOn( std::ostream & os ) const
+{
+  os << "<resolver>" << endl;
+  #define OUTS(t) os << "  " << #t << ":\t" << t << endl;
+  OUTS( _forceResolve );
+  OUTS( _upgradeMode );
+  OUTS( _updateMode );
+  OUTS( _verifying );
+  OUTS( _onlyRequires );
+  OUTS( _allowVendorChange );
+  OUTS( _solveSrcPackages );
+  OUTS( _cleandepsOnRemove );
+  OUTS( _ignoreAlreadyRecommended );
+  #undef OUT
+  return os << "<resolver/>";
+}
+
+
+//---------------------------------------------------------------------------
+
+Resolver::Resolver (const ResPool & pool)
+    : _pool(pool)
+    , _satResolver(NULL)
+    , _poolchanged(_pool.serial() )
+    , _forceResolve            (false)
+    , _upgradeMode             (false)
+    , _updateMode              (false)
+    , _verifying               (false)
+    , _onlyRequires            ( ZConfig::instance().solver_onlyRequires() )
+    , _allowVendorChange       ( ZConfig::instance().solver_allowVendorChange() )
+    , _solveSrcPackages                ( false )
+    , _cleandepsOnRemove       ( ZConfig::instance().solver_cleandepsOnRemove() )
+    , _ignoreAlreadyRecommended        ( false )
+
+{
+    sat::Pool satPool( sat::Pool::instance() );
+    _satResolver = new SATResolver(_pool, satPool.get());
+}
+
+
+Resolver::~Resolver()
+{
+  delete _satResolver;
+}
+
+//---------------------------------------------------------------------------
+
+void Resolver::setAllowVendorChange( TriBool state_r )
+{
+  _allowVendorChange = indeterminate(state_r) ? ZConfig::instance().solver_allowVendorChange() : bool(state_r);
+}
+
+void Resolver::setOnlyRequires( TriBool state_r )
+{
+  _onlyRequires = indeterminate(state_r) ? ZConfig::instance().solver_onlyRequires() : bool(state_r);
+}
+
+void Resolver::setCleandepsOnRemove( TriBool state_r )
+{
+  _cleandepsOnRemove = indeterminate(state_r) ? ZConfig::instance().solver_cleandepsOnRemove() : bool(state_r);
+}
+
+//---------------------------------------------------------------------------
+
+ResPool Resolver::pool() const
+{ return _pool; }
+
+void Resolver::reset( bool keepExtras )
+{
+    _verifying = false;
+
+    if (!keepExtras) {
+      _extra_requires.clear();
+      _extra_conflicts.clear();
+    }
+
+    _isInstalledBy.clear();
+    _installs.clear();
+    _satifiedByInstalled.clear();
+    _installedSatisfied.clear();
+}
+
+void Resolver::doUpdate()
+{
+    _updateMode = true;
+    return _satResolver->doUpdate();
+}
+
+PoolItemList Resolver::problematicUpdateItems() const
+{ return _satResolver->problematicUpdateItems(); }
+
+void Resolver::addExtraRequire( const Capability & capability )
+{ _extra_requires.insert (capability); }
+
+void Resolver::removeExtraRequire( const Capability & capability )
+{ _extra_requires.erase (capability); }
+
+void Resolver::addExtraConflict( const Capability & capability )
+{ _extra_conflicts.insert (capability); }
+
+void Resolver::removeExtraConflict( const Capability & capability )
+{ _extra_conflicts.erase (capability); }
+
+void Resolver::removeQueueItem( SolverQueueItem_Ptr item )
+{
+    bool found = false;
+    for (SolverQueueItemList::const_iterator iter = _added_queue_items.begin();
+        iter != _added_queue_items.end(); iter++) {
+       if (*iter == item) {
+           _added_queue_items.remove(*iter);
+           found = true;
+           break;
+       }
+    }
+    if (!found) {
+       _removed_queue_items.push_back (item);
+       _removed_queue_items.unique ();
+    }
+}
+
+void Resolver::addQueueItem( SolverQueueItem_Ptr item )
+{
+    bool found = false;
+    for (SolverQueueItemList::const_iterator iter = _removed_queue_items.begin();
+        iter != _removed_queue_items.end(); iter++) {
+       if (*iter == item) {
+           _removed_queue_items.remove(*iter);
+           found = true;
+           break;
+       }
+    }
+    if (!found) {
+       _added_queue_items.push_back (item);
+       _added_queue_items.unique ();
+    }
+}
+
+void Resolver::addWeak( const PoolItem & item )
+{ _addWeak.push_back( item ); }
+
+//---------------------------------------------------------------------------
+
+struct UndoTransact : public resfilter::PoolItemFilterFunctor
+{
+    ResStatus::TransactByValue resStatus;
+    UndoTransact ( const ResStatus::TransactByValue &status)
+       :resStatus(status)
+    { }
+
+    bool operator()( PoolItem item )           // only transacts() items go here
+    {
+       item.status().resetTransact( resStatus );// clear any solver/establish transactions
+       return true;
+    }
+};
+
+
+struct DoTransact : public resfilter::PoolItemFilterFunctor
+{
+    ResStatus::TransactByValue resStatus;
+    DoTransact ( const ResStatus::TransactByValue &status)
+       :resStatus(status)
+    { }
+
+    bool operator()( PoolItem item )           // only transacts() items go here
+    {
+       item.status().setTransact( true, resStatus );
+       return true;
+    }
+};
+
+
+bool Resolver::verifySystem()
+{
+    UndoTransact resetting (ResStatus::APPL_HIGH);
+
+    _DEBUG ("Resolver::verifySystem() ");
+
+    _verifying = true;
+
+    invokeOnEach ( _pool.begin(), _pool.end(),
+                  resfilter::ByTransact( ),                    // Resetting all transcations
+                  functor::functorRef<bool,PoolItem>(resetting) );
+
+    return resolvePool();
+}
+
+
+//----------------------------------------------------------------------------
+// undo
+
+void Resolver::undo()
+{
+    UndoTransact info(ResStatus::APPL_LOW);
+    MIL << "*** undo ***" << endl;
+    invokeOnEach ( _pool.begin(), _pool.end(),
+                  resfilter::ByTransact( ),                    // collect transacts from Pool to resolver queue
+                  functor::functorRef<bool,PoolItem>(info) );
+    //  Regard dependencies of the item weak onl
+    _addWeak.clear();
+
+    // Additional QueueItems which has to be regarded by the solver
+    _removed_queue_items.clear();
+    _added_queue_items.clear();
+
+    return;
+}
+
+void Resolver::solverInit()
+{
+    // Solving with the satsolver
+    static bool poolDumped = false;
+    MIL << "-------------- Calling SAT Solver -------------------" << endl;
+    if ( getenv("ZYPP_FULLLOG") ) {
+       Testcase testcase("/var/log/YaST2/autoTestcase");
+       if (!poolDumped) {
+           testcase.createTestcase (*this, true, false); // dump pool
+           poolDumped = true;
+       } else {
+           testcase.createTestcase (*this, false, false); // write control file only
+       }
+    }
+
+    _satResolver->setFixsystem                 ( isVerifyingMode() );
+    _satResolver->setIgnorealreadyrecommended  ( ignoreAlreadyRecommended() );
+    _satResolver->setOnlyRequires              ( onlyRequires() );
+    _satResolver->setAllowdowngrade            (false);
+    _satResolver->setAllowarchchange           (false);
+    _satResolver->setAllowvendorchange         ( allowVendorChange() );
+    _satResolver->setAllowuninstall            ( forceResolve() );
+    _satResolver->setUpdatesystem              (false);
+    _satResolver->setNoupdateprovide           (false);
+    _satResolver->setDosplitprovides           (false);
+    _satResolver->setSolveSrcPackages          ( solveSrcPackages() );
+    _satResolver->setCleandepsOnRemove         ( cleandepsOnRemove() );
+
+    if (_upgradeMode) {
+      // may overwrite some settings
+      _satResolver->setDistupgrade                     (true);
+      _satResolver->setDistupgrade_removeunsupported   (false);
+      _satResolver->setUpdatesystem                    (true);
+      _satResolver->setAllowdowngrade                  (true);
+      _satResolver->setAllowarchchange                 (true);
+      _satResolver->setAllowvendorchange               (true);
+      _satResolver->setDosplitprovides                 (true);
+    }
+
+    // Resetting additional solver information
+    _isInstalledBy.clear();
+    _installs.clear();
+    _satifiedByInstalled.clear();
+    _installedSatisfied.clear();
+}
+
+bool Resolver::resolvePool()
+{
+    solverInit();
+    return _satResolver->resolvePool(_extra_requires, _extra_conflicts, _addWeak, _upgradeRepos );
+}
+
+bool Resolver::resolveQueue( solver::detail::SolverQueueItemList & queue )
+{
+    solverInit();
+
+    // add/remove additional SolverQueueItems
+    for (SolverQueueItemList::const_iterator iter = _removed_queue_items.begin();
+        iter != _removed_queue_items.end(); iter++) {
+       for (SolverQueueItemList::const_iterator iterQueue = queue.begin(); iterQueue != queue.end(); iterQueue++) {
+           if ( (*iterQueue)->cmp(*iter) == 0) {
+               MIL << "remove from queue" << *iter;
+               queue.remove(*iterQueue);
+               break;
+           }
+       }
+    }
+
+    for (SolverQueueItemList::const_iterator iter = _added_queue_items.begin();
+        iter != _added_queue_items.end(); iter++) {
+       bool found = false;
+       for (SolverQueueItemList::const_iterator iterQueue = queue.begin(); iterQueue != queue.end(); iterQueue++) {
+           if ( (*iterQueue)->cmp(*iter) == 0) {
+               found = true;
+               break;
+           }
+       }
+       if (!found) {
+           MIL << "add to queue" << *iter;
+           queue.push_back(*iter);
+       }
+    }
+
+    // The application has to take care to write these solutions back to e.g. selectables in order
+    // give the user a chance for changing these decisions again.
+    _removed_queue_items.clear();
+    _added_queue_items.clear();
+
+    return _satResolver->resolveQueue(queue, _addWeak);
+}
+
+sat::Transaction Resolver::getTransaction()
+{ return _satResolver->getTransaction(); }
+
+//----------------------------------------------------------------------------
+// Getting more information about the solve results
+
+
+void Resolver::collectResolverInfo()
+{
+    if ( _satResolver
+        && _isInstalledBy.empty()
+        && _installs.empty()) {
+
+       // generating new
+       PoolItemList itemsToInstall = _satResolver->resultItemsToInstall();
+
+       for (PoolItemList::const_iterator instIter = itemsToInstall.begin();
+            instIter != itemsToInstall.end(); instIter++) {
+           // Requires
+           for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::REQUIRES).begin(); capIt != (*instIter)->dep (Dep::REQUIRES).end(); ++capIt)
+           {
+               sat::WhatProvides possibleProviders(*capIt);
+               for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
+                   PoolItem provider = ResPool::instance().find( *iter );
+
+                   // searching if this provider will already be installed
+                   bool found = false;
+                   bool alreadySetForInstallation = false;
+                   ItemCapKindMap::const_iterator pos = _isInstalledBy.find(provider);
+                   while (pos != _isInstalledBy.end()
+                          && pos->first == provider
+                          && !found) {
+                       alreadySetForInstallation = true;
+                       ItemCapKind capKind = pos->second;
+                       if (capKind.item == *instIter)  found = true;
+                       pos++;
+                   }
+
+                   if (!found
+                       && provider.status().isToBeInstalled()) {
+                       if (provider.status().isBySolver()) {
+                           ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::REQUIRES, !alreadySetForInstallation );
+                           _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
+                       } else {
+                           // no initial installation cause it has been set be e.g. user
+                           ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::REQUIRES, false );
+                           _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
+                       }
+                       ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::REQUIRES, !alreadySetForInstallation );
+                       _installs.insert (make_pair( *instIter, capKindisInstalledBy));
+                   }
+
+                   if (provider.status().staysInstalled()) { // Is already satisfied by an item which is installed
+                       ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::REQUIRES, false );
+                       _satifiedByInstalled.insert (make_pair( *instIter, capKindisInstalledBy));
+
+                       ItemCapKind installedSatisfied( *instIter, *capIt, Dep::REQUIRES, false );
+                       _installedSatisfied.insert (make_pair( provider, installedSatisfied));
+                   }
+               }
+           }
+
+           if (!(_satResolver->onlyRequires())) {
+               //Recommends
+               for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::RECOMMENDS).begin(); capIt != (*instIter)->dep (Dep::RECOMMENDS).end(); ++capIt)
+               {
+                   sat::WhatProvides possibleProviders(*capIt);
+                   for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
+                       PoolItem provider = ResPool::instance().find( *iter );
+
+                       // searching if this provider will already be installed
+                       bool found = false;
+                       bool alreadySetForInstallation = false;
+                       ItemCapKindMap::const_iterator pos = _isInstalledBy.find(provider);
+                       while (pos != _isInstalledBy.end()
+                              && pos->first == provider
+                              && !found) {
+                           alreadySetForInstallation = true;
+                           ItemCapKind capKind = pos->second;
+                           if (capKind.item == *instIter)  found = true;
+                           pos++;
+                       }
+
+                       if (!found
+                           && provider.status().isToBeInstalled()) {
+                           if (provider.status().isBySolver()) {
+                               ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::RECOMMENDS, !alreadySetForInstallation );
+                               _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
+                           } else {
+                               // no initial installation cause it has been set be e.g. user
+                               ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::RECOMMENDS, false );
+                               _isInstalledBy.insert (make_pair( provider, capKindisInstalledBy));
+                           }
+                           ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::RECOMMENDS, !alreadySetForInstallation );
+                           _installs.insert (make_pair( *instIter, capKindisInstalledBy));
+                       }
+
+                       if (provider.status().staysInstalled()) { // Is already satisfied by an item which is installed
+                           ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::RECOMMENDS, false );
+                           _satifiedByInstalled.insert (make_pair( *instIter, capKindisInstalledBy));
+
+                           ItemCapKind installedSatisfied( *instIter, *capIt, Dep::RECOMMENDS, false );
+                           _installedSatisfied.insert (make_pair( provider, installedSatisfied));
+                       }
+                   }
+               }
+
+               //Supplements
+               for (Capabilities::const_iterator capIt = (*instIter)->dep (Dep::SUPPLEMENTS).begin(); capIt != (*instIter)->dep (Dep::SUPPLEMENTS).end(); ++capIt)
+               {
+                   sat::WhatProvides possibleProviders(*capIt);
+                   for_( iter, possibleProviders.begin(), possibleProviders.end() ) {
+                       PoolItem provider = ResPool::instance().find( *iter );
+                       // searching if this item will already be installed
+                       bool found = false;
+                       bool alreadySetForInstallation = false;
+                       ItemCapKindMap::const_iterator pos = _isInstalledBy.find(*instIter);
+                       while (pos != _isInstalledBy.end()
+                              && pos->first == *instIter
+                              && !found) {
+                           alreadySetForInstallation = true;
+                           ItemCapKind capKind = pos->second;
+                           if (capKind.item == provider)  found = true;
+                           pos++;
+                       }
+
+                       if (!found
+                           && instIter->status().isToBeInstalled()) {
+                           if (instIter->status().isBySolver()) {
+                               ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
+                               _isInstalledBy.insert (make_pair( *instIter, capKindisInstalledBy));
+                           } else {
+                               // no initial installation cause it has been set be e.g. user
+                               ItemCapKind capKindisInstalledBy( provider, *capIt, Dep::SUPPLEMENTS, false );
+                               _isInstalledBy.insert (make_pair( *instIter, capKindisInstalledBy));
+                           }
+                           ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
+                           _installs.insert (make_pair( provider, capKindisInstalledBy));
+                       }
+
+                       if (instIter->status().staysInstalled()) { // Is already satisfied by an item which is installed
+                           ItemCapKind capKindisInstalledBy( *instIter, *capIt, Dep::SUPPLEMENTS, !alreadySetForInstallation );
+                           _satifiedByInstalled.insert (make_pair( provider, capKindisInstalledBy));
+
+                           ItemCapKind installedSatisfied( provider, *capIt, Dep::SUPPLEMENTS, false );
+                           _installedSatisfied.insert (make_pair( *instIter, installedSatisfied));
+                       }
+                   }
+               }
+           }
+       }
+    }
+}
+
+
+ItemCapKindList Resolver::isInstalledBy( const PoolItem & item )
+{
+    ItemCapKindList ret;
+    collectResolverInfo();
+
+    for (ItemCapKindMap::const_iterator iter = _isInstalledBy.find(item); iter != _isInstalledBy.end();) {
+       ItemCapKind info = iter->second;
+       PoolItem iterItem = iter->first;
+       if (iterItem == item) {
+           ret.push_back(info);
+           iter++;
+       } else {
+           // exit
+           iter = _isInstalledBy.end();
+       }
+    }
+    return ret;
+}
+
+ItemCapKindList Resolver::installs( const PoolItem & item )
+{
+    ItemCapKindList ret;
+    collectResolverInfo();
+
+    for (ItemCapKindMap::const_iterator iter = _installs.find(item); iter != _installs.end();) {
+       ItemCapKind info = iter->second;
+       PoolItem iterItem = iter->first;
+       if (iterItem == item) {
+           ret.push_back(info);
+           iter++;
+       } else {
+           // exit
+           iter = _installs.end();
+       }
+    }
+    return ret;
+}
+
+ItemCapKindList Resolver::satifiedByInstalled( const PoolItem & item )
+{
+    ItemCapKindList ret;
+    collectResolverInfo();
+
+    for (ItemCapKindMap::const_iterator iter = _satifiedByInstalled.find(item); iter != _satifiedByInstalled.end();) {
+       ItemCapKind info = iter->second;
+       PoolItem iterItem = iter->first;
+       if (iterItem == item) {
+           ret.push_back(info);
+           iter++;
+       } else {
+           // exit
+           iter = _satifiedByInstalled.end();
+       }
+    }
+    return ret;
+}
+
+ItemCapKindList Resolver::installedSatisfied( const PoolItem & item )
+{
+    ItemCapKindList ret;
+    collectResolverInfo();
+
+    for (ItemCapKindMap::const_iterator iter = _installedSatisfied.find(item); iter != _installedSatisfied.end();) {
+       ItemCapKind info = iter->second;
+       PoolItem iterItem = iter->first;
+       if (iterItem == item) {
+           ret.push_back(info);
+           iter++;
+       } else {
+           // exit
+           iter = _installedSatisfied.end();
+       }
+    }
+    return ret;
+}
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
diff --git a/zypp/solver/detail/Resolver.h b/zypp/solver/detail/Resolver.h
new file mode 100644 (file)
index 0000000..0db4c2a
--- /dev/null
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Resolver.h
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_RESOLVER_H
+#define ZYPP_SOLVER_DETAIL_RESOLVER_H
+
+#include <iosfwd>
+#include <list>
+#include <map>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/ResPool.h"
+#include "zypp/TriBool.h"
+#include "zypp/base/SerialNumber.h"
+
+#include "zypp/solver/detail/Types.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+#include "zypp/ProblemTypes.h"
+#include "zypp/ResolverProblem.h"
+#include "zypp/ProblemSolution.h"
+#include "zypp/Capabilities.h"
+#include "zypp/Capability.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+  namespace sat
+  {
+    class Transaction;
+  }
+
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+    class SATResolver;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ItemCapKind
+    //
+    /** */
+    struct ItemCapKind
+    {
+       public:
+       Capability cap; //Capability which has triggerd this selection
+       Dep capKind; //Kind of that capability
+       PoolItem item; //Item which has triggered this selection
+       bool initialInstallation; //This item has triggered the installation
+                                 //Not already fullfilled requierement only.
+
+    ItemCapKind() : capKind(Dep::PROVIDES) {}
+           ItemCapKind( PoolItem i, Capability c, Dep k, bool initial)
+               : cap( c )
+               , capKind( k )
+               , item( i )
+               , initialInstallation( initial )
+           { }
+    };
+    typedef std::multimap<PoolItem,ItemCapKind> ItemCapKindMap;
+    typedef std::list<ItemCapKind> ItemCapKindList;
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Resolver
+
+class Resolver : public base::ReferenceCounted, private base::NonCopyable {
+
+  private:
+    ResPool _pool;
+    SATResolver *_satResolver;
+    SerialNumberWatcher _poolchanged;
+
+    CapabilitySet _extra_requires;
+    CapabilitySet _extra_conflicts;
+    std::set<Repository> _upgradeRepos;
+
+    // Regard dependencies of the item weak onl
+    PoolItemList _addWeak;
+
+    /** \name Solver flags */
+    //@{
+    bool _forceResolve;           // remove items which are conflicts with others or
+                                  // have unfulfilled requirements.
+                                  // This behaviour is favourited by ZMD
+    bool _upgradeMode;            // Resolver has been called with doUpgrade
+    bool _updateMode;            // Resolver has been called with doUpdate
+    bool _verifying;              // The system will be checked
+    bool _onlyRequires;          // do install required resolvables only
+                                  // no recommended resolvables, language
+                                  // packages, hardware packages (modalias)
+    bool _allowVendorChange;   // whether the solver should allow or disallow vendor changes.
+    bool _solveSrcPackages;    // whether to generate solver jobs for selected source packges.
+    bool _cleandepsOnRemove;   // whether removing a package should also remove no longer needed requirements
+
+    bool _ignoreAlreadyRecommended;   //ignore recommended packages that have already been recommended by the installed packages
+    //@}
+
+    // Additional QueueItems which has to be regarded by the solver
+    // This will be used e.g. by solution actions
+    solver::detail::SolverQueueItemList _removed_queue_items;
+    solver::detail::SolverQueueItemList _added_queue_items;
+
+    // Additional information about the solverrun
+    ItemCapKindMap _isInstalledBy;
+    ItemCapKindMap _installs;
+    ItemCapKindMap _satifiedByInstalled;
+    ItemCapKindMap _installedSatisfied;
+
+    // helpers
+    void collectResolverInfo();
+
+    // Unmaintained packages which does not fit to the updated system
+    // (broken dependencies) will be deleted.
+    // returns true if solving was successful
+    bool checkUnmaintainedItems ();
+
+    void solverInit();
+
+  public:
+
+    Resolver( const ResPool & pool );
+    virtual ~Resolver();
+
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+    friend std::ostream& operator<<( std::ostream& str, const Resolver & obj )
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- methods
+
+    ResPool pool() const;
+    void setPool( const ResPool & pool ) { _pool = pool; }
+
+    void addUpgradeRepo( Repository repo_r )           { if ( repo_r && ! repo_r.isSystemRepo() ) _upgradeRepos.insert( repo_r ); }
+    bool upgradingRepo( Repository repo_r ) const      { return( _upgradeRepos.find( repo_r ) != _upgradeRepos.end() ); }
+    void removeUpgradeRepo( Repository repo_r )                { _upgradeRepos.erase( repo_r ); }
+    void removeUpgradeRepos()                          { _upgradeRepos.clear(); }
+    const std::set<Repository> & upgradeRepos() const  { return _upgradeRepos; }
+
+    void addExtraRequire( const Capability & capability );
+    void removeExtraRequire( const Capability & capability );
+    void addExtraConflict( const Capability & capability );
+    void removeExtraConflict( const Capability & capability );
+
+    void removeQueueItem( SolverQueueItem_Ptr item );
+    void addQueueItem( SolverQueueItem_Ptr item );
+
+    CapabilitySet extraRequires() const                { return _extra_requires; }
+    CapabilitySet extraConflicts() const       { return _extra_conflicts; }
+
+    void addWeak( const PoolItem & item );
+
+    bool verifySystem();
+    bool resolvePool();
+    bool resolveQueue( SolverQueueItemList & queue );
+    void doUpdate();
+
+    bool doUpgrade();
+    PoolItemList problematicUpdateItems() const;
+
+    /** \name Solver flags */
+    //@{
+    bool ignoreAlreadyRecommended() const      { return _ignoreAlreadyRecommended; }
+    void setIgnoreAlreadyRecommended( bool yesno_r ) { _ignoreAlreadyRecommended = yesno_r; }
+
+    bool onlyRequires () const                 { return _onlyRequires; }
+    void setOnlyRequires( TriBool state_r );
+
+    bool forceResolve()        const                   { return _forceResolve; }
+    void setForceResolve( TriBool state_r )    { _forceResolve = indeterminate(state_r) ? false : bool(state_r); }
+
+    bool isUpgradeMode() const                         { return _upgradeMode; }// Resolver has been called with doUpgrade
+
+    bool isUpdateMode() const                  { return _updateMode; } // Resolver has been called with doUpdate
+
+    bool isVerifyingMode() const               { return _verifying; }  // The system will be checked
+    void setVerifyingMode( TriBool state_r )   { _verifying = indeterminate(state_r) ? false : bool(state_r); }
+
+    bool allowVendorChange() const             { return _allowVendorChange; }
+    void setAllowVendorChange( TriBool state_r );
+
+    bool solveSrcPackages() const              { return _solveSrcPackages; }
+    void setSolveSrcPackages( TriBool state_r )        { _solveSrcPackages = indeterminate(state_r) ? false : bool(state_r); }
+
+    bool cleandepsOnRemove() const             { return _cleandepsOnRemove; }
+    void setCleandepsOnRemove( TriBool state_r );
+    //@}
+
+    ResolverProblemList problems() const;
+    void applySolutions( const ProblemSolutionList & solutions );
+
+    // Return the Transaction computed by the last solver run.
+    sat::Transaction getTransaction();
+
+    // reset all SOLVER transaction in pool
+    void undo();
+
+    void reset( bool keepExtras = false );
+
+    // Get more information about the solverrun
+    // Which item will be installed by another item or triggers an item for
+    // installation
+    ItemCapKindList isInstalledBy( const PoolItem & item );
+    ItemCapKindList installs( const PoolItem & item );
+    ItemCapKindList satifiedByInstalled (const PoolItem & item );
+    ItemCapKindList installedSatisfied( const PoolItem & item );
+
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_RESOLVER_H
diff --git a/zypp/solver/detail/ResolverUpgrade.cc b/zypp/solver/detail/ResolverUpgrade.cc
new file mode 100644 (file)
index 0000000..d914654
--- /dev/null
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/* ResolverUpgrade.cc
+ *
+ * Implements the distribution upgrade algorithm.
+ *
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Exception.h"
+#include "zypp/ResPool.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/Testcase.h"
+#include "zypp/solver/detail/SATResolver.h"
+#include "zypp/Target.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+using namespace zypp;
+
+//-----------------------------------------------------------------------------
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Resolver::doUpgrade
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION : creates a testcase in /var/log/updateTestcase
+//                   and set the SAT solver in update mode
+//
+bool
+Resolver::doUpgrade()
+{
+  Target_Ptr target( getZYpp()->getTarget() );
+  if ( ! target )
+  {
+    WAR << "Huh, no target ?" << endl;
+  }
+
+  // Setting Resolver to upgrade mode. SAT solver will do the update
+  _upgradeMode = true;
+  return resolvePool();
+}
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+
diff --git a/zypp/solver/detail/Resolver_problems.cc b/zypp/solver/detail/Resolver_problems.cc
new file mode 100644 (file)
index 0000000..1d61e68
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Resolver_problems.cc
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <map>
+#include <sstream>
+
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/SATResolver.h"
+#include "zypp/Resolver.h"
+#include "zypp/ResolverProblem.h"
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Gettext.h"
+
+#include "zypp/base/Algorithm.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+       
+ResolverProblemList
+Resolver::problems () const
+{
+    MIL << "Resolver::problems()" << endl;    
+    return _satResolver->problems();
+}
+
+void
+Resolver::applySolutions (const ProblemSolutionList & solutions)
+{
+    for (ProblemSolutionList::const_iterator iter = solutions.begin();
+        iter != solutions.end(); ++iter) {
+       ProblemSolution_Ptr solution = *iter;
+       if (!solution->apply (*this))
+           break;
+    }    
+}
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
diff --git a/zypp/solver/detail/SATResolver.cc b/zypp/solver/detail/SATResolver.cc
new file mode 100644 (file)
index 0000000..a2629bb
--- /dev/null
@@ -0,0 +1,1456 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SATResolver.cc
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/repo_solv.h>
+#include <satsolver/poolarch.h>
+#include <satsolver/evr.h>
+#include <satsolver/poolvendor.h>
+#include <satsolver/policy.h>
+#include <satsolver/bitmap.h>
+#include <satsolver/queue.h>
+}
+
+#include "zypp/solver/detail/Helper.h"
+#include "zypp/base/String.h"
+#include "zypp/Product.h"
+#include "zypp/Capability.h"
+#include "zypp/ResStatus.h"
+#include "zypp/VendorAttr.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Algorithm.h"
+#include "zypp/ResPool.h"
+#include "zypp/ResFilters.h"
+#include "zypp/ZConfig.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/WhatProvides.h"
+#include "zypp/sat/WhatObsoletes.h"
+#include "zypp/solver/detail/SATResolver.h"
+#include "zypp/solver/detail/ProblemSolutionCombi.h"
+#include "zypp/solver/detail/ProblemSolutionIgnore.h"
+#include "zypp/solver/detail/SolverQueueItemInstall.h"
+#include "zypp/solver/detail/SolverQueueItemDelete.h"
+#include "zypp/solver/detail/SystemCheck.h"
+#include "zypp/sat/Transaction.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SATResolver);
+
+#define MAYBE_CLEANDEPS (cleandepsOnRemove()?SOLVER_CLEANDEPS:0)
+
+//---------------------------------------------------------------------------
+// Callbacks for SAT policies
+//---------------------------------------------------------------------------
+
+int vendorCheck( Pool *pool, Solvable *solvable1, Solvable *solvable2 )
+{
+  return VendorAttr::instance().equivalent( IdString(solvable1->vendor),
+                                            IdString(solvable2->vendor) ) ? 0 : 1;
+}
+
+
+inline std::string itemToString( const PoolItem & item )
+{
+  if ( !item )
+    return std::string();
+
+  sat::Solvable slv( item.satSolvable() );
+  std::string ret( slv.asString() ); // n-v-r.a
+  if ( ! slv.isSystem() )
+  {
+    ret += "[";
+    ret += slv.repository().alias();
+    ret += "]";
+  }
+  return ret;
+}
+
+inline PoolItem getPoolItem( Id id_r )
+{
+  PoolItem ret( (sat::Solvable( id_r )) );
+  if ( !ret && id_r )
+    INT << "id " << id_r << " not found in ZYPP pool." << endl;
+  return ret;
+}
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SATResolver::dumpOn( std::ostream & os ) const
+{
+    os << "<resolver>" << endl;
+    if (_solv) {
+       os << "  fixsystem = " << _solv->fixsystem << endl;
+       os << "  allowdowngrade = " << _solv->allowdowngrade << endl;
+       os << "  allowarchchange = " << _solv->allowarchchange << endl;
+       os << "  allowvendorchange = " <<  _solv->allowvendorchange << endl;
+       os << "  allowuninstall = " << _solv->allowuninstall << endl;
+       os << "  updatesystem = " << _solv->updatesystem << endl;
+       os << "  noupdateprovide = " << _solv->noupdateprovide << endl;
+       os << "  dosplitprovides = " << _solv->dosplitprovides << endl;
+       os << "  onlyRequires = " << _solv->dontinstallrecommended << endl;
+       os << "  ignorealreadyrecommended = " << _solv->ignorealreadyrecommended << endl;
+       os << "  distupgrade = " << _distupgrade << endl;
+        os << "  distupgrade_removeunsupported = " << _distupgrade_removeunsupported << endl;
+       os << "  solveSrcPackages = " << _solveSrcPackages << endl;
+       os << "  cleandepsOnRemove = " << _cleandepsOnRemove << endl;
+    } else {
+       os << "<NULL>";
+    }
+    return os << "<resolver/>" << endl;
+}
+
+//---------------------------------------------------------------------------
+
+SATResolver::SATResolver (const ResPool & pool, Pool *SATPool)
+    : _pool (pool)
+    , _SATPool (SATPool)
+    , _solv(NULL)
+    , _fixsystem(false)
+    , _allowdowngrade(false)
+    , _allowarchchange(false)
+    , _allowvendorchange(ZConfig::instance().solver_allowVendorChange())
+    , _allowuninstall(false)
+    , _updatesystem(false)
+    , _noupdateprovide(false)
+    , _dosplitprovides(false)
+    , _onlyRequires(ZConfig::instance().solver_onlyRequires())
+    , _ignorealreadyrecommended(false)
+    , _distupgrade(false)
+    , _distupgrade_removeunsupported(false)
+    , _solveSrcPackages(false)
+    , _cleandepsOnRemove(ZConfig::instance().solver_cleandepsOnRemove())
+{
+}
+
+
+SATResolver::~SATResolver()
+{
+  solverEnd();
+}
+
+//---------------------------------------------------------------------------
+
+sat::Transaction SATResolver::getTransaction()
+{
+  if ( !_solv )
+    return sat::Transaction();
+  return sat::Transaction( _solv->trans );
+}
+
+ResPool
+SATResolver::pool (void) const
+{
+    return _pool;
+}
+
+void
+SATResolver::resetItemTransaction (PoolItem item)
+{
+    bool found = false;
+    for (PoolItemList::const_iterator iter = _items_to_remove.begin();
+        iter != _items_to_remove.end(); ++iter) {
+       if (*iter == item) {
+           _items_to_remove.remove(*iter);
+           found = true;
+           break;
+       }
+    }
+    if (!found) {
+       for (PoolItemList::const_iterator iter = _items_to_install.begin();
+            iter != _items_to_install.end(); ++iter) {
+           if (*iter == item) {
+               _items_to_install.remove(*iter);
+               found = true;
+               break;
+           }
+       }
+    }
+    if (!found) {
+       for (PoolItemList::const_iterator iter = _items_to_keep.begin();
+            iter != _items_to_keep.end(); ++iter) {
+           if (*iter == item) {
+               _items_to_keep.remove(*iter);
+               found = true;
+               break;
+           }
+       }
+    }
+    if (!found) {
+       for (PoolItemList::const_iterator iter = _items_to_lock.begin();
+            iter != _items_to_lock.end(); ++iter) {
+           if (*iter == item) {
+               _items_to_lock.remove(*iter);
+               found = true;
+               break;
+           }
+       }
+    }
+}
+
+
+void
+SATResolver::addPoolItemToInstall (PoolItem item)
+{
+    resetItemTransaction (item);
+    _items_to_install.push_back (item);
+    _items_to_install.unique ();
+}
+
+
+void
+SATResolver::addPoolItemsToInstallFromList (PoolItemList & rl)
+{
+    for (PoolItemList::const_iterator iter = rl.begin(); iter != rl.end(); iter++) {
+       addPoolItemToInstall (*iter);
+    }
+}
+
+
+void
+SATResolver::addPoolItemToRemove (PoolItem item)
+{
+    resetItemTransaction (item);
+    _items_to_remove.push_back (item);
+    _items_to_remove.unique ();
+}
+
+
+void
+SATResolver::addPoolItemsToRemoveFromList (PoolItemList & rl)
+{
+    for (PoolItemList::const_iterator iter = rl.begin(); iter != rl.end(); iter++) {
+       addPoolItemToRemove (*iter);
+    }
+}
+
+void
+SATResolver::addPoolItemToLock (PoolItem item)
+{
+    resetItemTransaction (item);
+    _items_to_lock.push_back (item);
+    _items_to_lock.unique ();
+}
+
+void
+SATResolver::addPoolItemToKeep (PoolItem item)
+{
+    resetItemTransaction (item);
+    _items_to_keep.push_back (item);
+    _items_to_keep.unique ();
+}
+
+//---------------------------------------------------------------------------
+
+// copy marked item from solution back to pool
+// if data != NULL, set as APPL_LOW (from establishPool())
+
+static void
+SATSolutionToPool (PoolItem item, const ResStatus & status, const ResStatus::TransactByValue causer)
+{
+    // resetting
+    item.status().resetTransact (causer);
+    item.status().resetWeak ();
+
+    bool r;
+
+    // installation/deletion
+    if (status.isToBeInstalled()) {
+       r = item.status().setToBeInstalled (causer);
+       _XDEBUG("SATSolutionToPool install returns " << item << ", " << r);
+    }
+    else if (status.isToBeUninstalledDueToUpgrade()) {
+       r = item.status().setToBeUninstalledDueToUpgrade (causer);
+       _XDEBUG("SATSolutionToPool upgrade returns " << item << ", " <<  r);
+    }
+    else if (status.isToBeUninstalled()) {
+       r = item.status().setToBeUninstalled (causer);
+       _XDEBUG("SATSolutionToPool remove returns " << item << ", " <<  r);
+    }
+
+    return;
+}
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+// resolvePool
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Helper functions for the ZYPP-Pool
+//----------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------------------------------------
+//  This function loops over the pool and grabs all items
+//  It clears all previous bySolver() states also
+//
+//  Every toBeInstalled is passed to zypp::solver:detail::Resolver.addPoolItemToInstall()
+//  Every toBeUninstalled is passed to zypp::solver:detail::Resolver.addPoolItemToRemove()
+//
+//  Solver results must be written back to the pool.
+//------------------------------------------------------------------------------------------------------------
+
+
+struct SATCollectTransact : public resfilter::PoolItemFilterFunctor
+{
+    SATResolver & resolver;
+
+    SATCollectTransact (SATResolver & r)
+       : resolver (r)
+    { }
+
+    bool operator()( PoolItem item )           // only transacts() items go here
+    {
+       ResStatus status = item.status();
+       bool by_solver = (status.isBySolver() || status.isByApplLow());
+
+       if (by_solver) {
+           item.status().resetTransact( ResStatus::APPL_LOW );// clear any solver/establish transactions
+           return true;                                // back out here, dont re-queue former solver result
+       }
+
+       if ( item.satSolvable().isKind<SrcPackage>() && ! resolver.solveSrcPackages() )
+       {
+         // Later we may continue on a per source package base.
+         return true; // dont process this source package.
+       }
+
+       if (status.isToBeInstalled()) {
+           resolver.addPoolItemToInstall(item);        // -> install!
+       }
+       else if (status.isToBeUninstalled()) {
+           resolver.addPoolItemToRemove(item);         // -> remove !
+       }
+        else if (status.isLocked()
+                && !by_solver) {
+           resolver.addPoolItemToLock (item);
+        }
+        else if (status.isKept()
+                && !by_solver) {
+           resolver.addPoolItemToKeep (item);
+        }
+
+       return true;
+    }
+};
+
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+// solving.....
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+
+class CheckIfUpdate : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    bool is_updated;
+    bool multiversion;
+    sat::Solvable _installed;
+
+    CheckIfUpdate( sat::Solvable installed_r )
+       : is_updated( false )
+        , multiversion( installed_r.multiversionInstall() )
+        , _installed( installed_r )
+    {}
+
+    // check this item will be updated
+
+    bool operator()( PoolItem item )
+    {
+       if ( item.status().isToBeInstalled() )
+        {
+          if ( ! multiversion || sameNVRA( _installed, item ) )
+          {
+            is_updated = true;
+            return false;
+          }
+       }
+       return true;
+    }
+};
+
+
+class CollectPseudoInstalled : public resfilter::PoolItemFilterFunctor
+{
+  public:
+    Queue *solvableQueue;
+
+    CollectPseudoInstalled( Queue *queue )
+       :solvableQueue (queue)
+    {}
+
+    // collecting PseudoInstalled items
+    bool operator()( PoolItem item )
+    {
+      if ( traits::isPseudoInstalled( item.satSolvable().kind() ) )
+        queue_push( solvableQueue, item.satSolvable().id() );
+      return true;
+    }
+};
+
+bool
+SATResolver::solving(const CapabilitySet & requires_caps,
+                    const CapabilitySet & conflict_caps)
+{
+    _solv = solver_create( _SATPool );
+    _solv->vendorCheckCb = &vendorCheck;
+    _solv->fixsystem = _fixsystem;
+    _solv->ignorealreadyrecommended = _ignorealreadyrecommended;
+    _solv->updatesystem = _updatesystem;
+    _solv->allowdowngrade = _allowdowngrade;
+    _solv->allowuninstall = _allowuninstall;
+    _solv->allowarchchange = _allowarchchange;
+    _solv->allowvendorchange = _allowvendorchange;
+    _solv->dosplitprovides = _dosplitprovides;
+    _solv->noupdateprovide = _noupdateprovide;
+    _solv->dontinstallrecommended = _onlyRequires;
+    _solv->distupgrade = _distupgrade;
+    _solv->distupgrade_removeunsupported = _distupgrade_removeunsupported;
+
+    sat::Pool::instance().prepare();
+
+    // Solve !
+    MIL << "Starting solving...." << endl;
+    MIL << *this;
+    solver_solve( _solv, &(_jobQueue) );
+    MIL << "....Solver end" << endl;
+
+    // copying solution back to zypp pool
+    //-----------------------------------------
+    _result_items_to_install.clear();
+    _result_items_to_remove.clear();
+
+    /*  solvables to be installed */
+    for ( int i = 0; i < _solv->decisionq.count; ++i )
+    {
+      sat::Solvable slv( _solv->decisionq.elements[i] );
+      if ( !slv || slv.isSystem() )
+       continue;
+
+      PoolItem poolItem( slv );
+      SATSolutionToPool (poolItem, ResStatus::toBeInstalled, ResStatus::SOLVER);
+      _result_items_to_install.push_back (poolItem);
+    }
+
+    /* solvables to be erased */
+    Repository systemRepo( sat::Pool::instance().findSystemRepo() ); // don't create if it does not exist
+    if ( systemRepo && ! systemRepo.solvablesEmpty() )
+    {
+      bool mustCheckObsoletes = false;
+      for_( it, systemRepo.solvablesBegin(), systemRepo.solvablesEnd() )
+      {
+       if (_solv->decisionmap[it->id()] > 0)
+         continue;
+
+       // Check if this is an update
+       CheckIfUpdate info( *it );
+       PoolItem poolItem( *it );
+       invokeOnEach( _pool.byIdentBegin( poolItem ),
+                     _pool.byIdentEnd( poolItem ),
+                     resfilter::ByUninstalled(),                       // ByUninstalled
+                     functor::functorRef<bool,PoolItem> (info) );
+
+       if (info.is_updated) {
+         SATSolutionToPool( poolItem, ResStatus::toBeUninstalledDueToUpgrade, ResStatus::SOLVER );
+       } else {
+         SATSolutionToPool( poolItem, ResStatus::toBeUninstalled, ResStatus::SOLVER );
+         if ( ! mustCheckObsoletes )
+           mustCheckObsoletes = true; // lazy check for UninstalledDueToObsolete
+       }
+       _result_items_to_remove.push_back (poolItem);
+      }
+      if ( mustCheckObsoletes )
+      {
+       sat::WhatObsoletes obsoleted( _result_items_to_install.begin(), _result_items_to_install.end() );
+       for_( it, obsoleted.poolItemBegin(), obsoleted.poolItemEnd() )
+       {
+         ResStatus & status( it->status() );
+         // WhatObsoletes contains installed items only!
+         if ( status.transacts() && ! status.isToBeUninstalledDueToUpgrade() )
+           status.setToBeUninstalledDueToObsolete();
+       }
+      }
+    }
+
+    /*  solvables which are recommended */
+    for ( int i = 0; i < _solv->recommendations.count; ++i )
+    {
+      PoolItem poolItem( getPoolItem( _solv->recommendations.elements[i] ) );
+      poolItem.status().setRecommended( true );
+    }
+
+    /*  solvables which are suggested */
+    for ( int i = 0; i < _solv->suggestions.count; ++i )
+    {
+      PoolItem poolItem( getPoolItem( _solv->suggestions.elements[i] ) );
+      poolItem.status().setSuggested( true );
+    }
+
+    _problem_items.clear();
+    /*  solvables which are orphaned */
+    for ( int i = 0; i < _solv->orphaned.count; ++i )
+    {
+      PoolItem poolItem( getPoolItem( _solv->orphaned.elements[i] ) );
+      poolItem.status().setOrphaned( true );
+      _problem_items.push_back( poolItem );
+    }
+
+    /* Write validation state back to pool */
+    Queue flags, solvableQueue;
+
+    queue_init(&flags);
+    queue_init(&solvableQueue);
+
+    CollectPseudoInstalled collectPseudoInstalled(&solvableQueue);
+    invokeOnEach( _pool.begin(),
+                 _pool.end(),
+                 functor::functorRef<bool,PoolItem> (collectPseudoInstalled) );
+    solver_trivial_installable(_solv, &solvableQueue, &flags );
+    for (int i = 0; i < solvableQueue.count; i++) {
+       PoolItem item = _pool.find (sat::Solvable(solvableQueue.elements[i]));
+       item.status().setUndetermined();
+
+       if (flags.elements[i] == -1) {
+           item.status().setNonRelevant();
+           _XDEBUG("SATSolutionToPool(" << item << " ) nonRelevant !");
+       } else if (flags.elements[i] == 1) {
+           item.status().setSatisfied();
+           _XDEBUG("SATSolutionToPool(" << item << " ) satisfied !");
+       } else if (flags.elements[i] == 0) {
+           item.status().setBroken();
+           _XDEBUG("SATSolutionToPool(" << item << " ) broken !");
+       }
+    }
+
+    // Solvables which were selected due requirements which have been made by the user will
+    // be selected by APPL_LOW. We can't use any higher level, because this setting must
+    // not serve as a request for the next solver run. APPL_LOW is reset before solving.
+    for (CapabilitySet::const_iterator iter = requires_caps.begin(); iter != requires_caps.end(); iter++) {
+       sat::WhatProvides rpmProviders(*iter);
+       for_( iter2, rpmProviders.begin(), rpmProviders.end() ) {
+           PoolItem poolItem(*iter2);
+           if (poolItem.status().isToBeInstalled()) {
+               MIL << "User requirement " << *iter << " sets " << poolItem << endl;
+               poolItem.status().setTransactByValue (ResStatus::APPL_LOW);
+           }
+       }
+    }
+    for (CapabilitySet::const_iterator iter = conflict_caps.begin(); iter != conflict_caps.end(); iter++) {
+       sat::WhatProvides rpmProviders(*iter);
+       for_( iter2, rpmProviders.begin(), rpmProviders.end() ) {
+           PoolItem poolItem(*iter2);
+           if (poolItem.status().isToBeUninstalled()) {
+               MIL << "User conflict " << *iter << " sets " << poolItem << endl;
+               poolItem.status().setTransactByValue (ResStatus::APPL_LOW);
+           }
+       }
+    }
+
+    if (_solv->problems.count > 0 )
+    {
+       ERR << "Solverrun finished with an ERROR" << endl;
+       return false;
+    }
+
+    queue_free(&(solvableQueue));
+    queue_free(&flags);
+
+    return true;
+}
+
+
+void
+SATResolver::solverInit(const PoolItemList & weakItems)
+{
+    SATCollectTransact info (*this);
+
+    MIL << "SATResolver::solverInit()" << endl;
+
+    // remove old stuff
+    solverEnd();
+
+    queue_init( &_jobQueue );
+    _items_to_install.clear();
+    _items_to_remove.clear();
+    _items_to_lock.clear();
+    _items_to_keep.clear();
+
+    invokeOnEach ( _pool.begin(), _pool.end(),
+                  functor::functorRef<bool,PoolItem>(info) );
+
+    for (PoolItemList::const_iterator iter = weakItems.begin(); iter != weakItems.end(); iter++) {
+       Id id = (*iter)->satSolvable().id();
+       if (id == ID_NULL) {
+           ERR << "Weaken: " << *iter << " not found" << endl;
+       }
+       MIL << "Weaken dependencies of " << *iter << endl;
+       queue_push( &(_jobQueue), SOLVER_WEAKENDEPS | SOLVER_SOLVABLE );
+        queue_push( &(_jobQueue), id );
+    }
+
+    // Add rules for parallel installable resolvables with different versions
+    for_( it, sat::Pool::instance().multiversionBegin(), sat::Pool::instance().multiversionEnd() )
+    {
+      queue_push( &(_jobQueue), SOLVER_NOOBSOLETES | SOLVABLE_NAME );
+      queue_push( &(_jobQueue), it->id() );
+    }
+
+    if ( cleandepsOnRemove() )
+    {
+      // Add all items known to be installed by user request (not solver selected).
+      for_( it, sat::Pool::instance().onSystemByUserBegin(), sat::Pool::instance().onSystemByUserEnd() )
+      {
+       queue_push( &(_jobQueue), SOLVER_USERINSTALLED | SOLVER_SOLVABLE_NAME );
+       queue_push( &(_jobQueue), it->id() );
+      }
+    }
+
+    if ( _distupgrade )
+    {
+      if ( ZConfig::instance().solverUpgradeRemoveDroppedPackages() )
+      {
+        MIL << "Checking droplists ..." << endl;
+        // Dropped packages: look for 'weakremover()' provides
+        // in dup candidates of installed products.
+        ResPoolProxy proxy( ResPool::instance().proxy() );
+        for_( it, proxy.byKindBegin<Product>(), proxy.byKindEnd<Product>() )
+        {
+          if ( (*it)->onSystem() ) // (to install) or (not to delete)
+          {
+            Product::constPtr prodCand( (*it)->candidateAsKind<Product>() );
+            if ( ! prodCand || (*it)->identicalInstalledCandidate() )
+              continue; // product no longer available or unchanged
+
+            CapabilitySet droplist( prodCand->droplist() );
+            dumpRangeLine( MIL << "Droplist for " << (*it)->candidateObj() << ": " << droplist.size() << " ", droplist.begin(), droplist.end() ) << endl;
+            for_( cap, droplist.begin(), droplist.end() )
+            {
+              queue_push( &_jobQueue, SOLVER_DROP_ORPHANED | SOLVER_SOLVABLE_NAME );
+              queue_push( &_jobQueue, cap->id() );
+            }
+          }
+        }
+      }
+      else
+      {
+        MIL << "Droplist processing is disabled." << endl;
+      }
+    }
+}
+
+void
+SATResolver::solverEnd()
+{
+  // cleanup
+  if ( _solv )
+  {
+    solver_free(_solv);
+    _solv = NULL;
+    queue_free( &(_jobQueue) );
+  }
+}
+
+
+bool
+SATResolver::resolvePool(const CapabilitySet & requires_caps,
+                        const CapabilitySet & conflict_caps,
+                        const PoolItemList & weakItems,
+                         const std::set<Repository> & upgradeRepos)
+{
+    MIL << "SATResolver::resolvePool()" << endl;
+
+    // initialize
+    solverInit(weakItems);
+
+    for (PoolItemList::const_iterator iter = _items_to_install.begin(); iter != _items_to_install.end(); iter++) {
+       Id id = (*iter)->satSolvable().id();
+       if (id == ID_NULL) {
+           ERR << "Install: " << *iter << " not found" << endl;
+       } else {
+           MIL << "Install " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_INSTALL_SOLVABLE );
+           queue_push( &(_jobQueue), id );
+       }
+    }
+
+    for (PoolItemList::const_iterator iter = _items_to_remove.begin(); iter != _items_to_remove.end(); iter++) {
+       Id id = (*iter)->satSolvable().id();
+       if (id == ID_NULL) {
+           ERR << "Delete: " << *iter << " not found" << endl;
+       } else {
+           MIL << "Delete " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_ERASE_SOLVABLE | MAYBE_CLEANDEPS );
+           queue_push( &(_jobQueue), id);
+       }
+    }
+
+    for_( iter, upgradeRepos.begin(), upgradeRepos.end() )
+    {
+       queue_push( &(_jobQueue), SOLVER_DISTUPGRADE | SOLVER_SOLVABLE_REPO );
+       queue_push( &(_jobQueue), iter->get()->repoid );
+        MIL << "Upgrade repo " << *iter << endl;
+    }
+
+    for (CapabilitySet::const_iterator iter = requires_caps.begin(); iter != requires_caps.end(); iter++) {
+       queue_push( &(_jobQueue), SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES );
+       queue_push( &(_jobQueue), iter->id() );
+       MIL << "Requires " << *iter << endl;
+    }
+
+    for (CapabilitySet::const_iterator iter = conflict_caps.begin(); iter != conflict_caps.end(); iter++) {
+       queue_push( &(_jobQueue), SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES | MAYBE_CLEANDEPS );
+       queue_push( &(_jobQueue), iter->id() );
+       MIL << "Conflicts " << *iter << endl;
+    }
+
+    // set requirements for a running system
+    setSystemRequirements();
+
+    // set locks for the solver
+    setLocks();
+
+    // solving
+    bool ret = solving(requires_caps, conflict_caps);
+
+    (ret?MIL:WAR) << "SATResolver::resolvePool() done. Ret:" << ret <<  endl;
+    return ret;
+}
+
+
+bool
+SATResolver::resolveQueue(const SolverQueueItemList &requestQueue,
+                         const PoolItemList & weakItems)
+{
+    MIL << "SATResolver::resolvQueue()" << endl;
+
+    // initialize
+    solverInit(weakItems);
+
+    // generate solver queue
+    for (SolverQueueItemList::const_iterator iter = requestQueue.begin(); iter != requestQueue.end(); iter++) {
+       (*iter)->addRule(_jobQueue);
+    }
+
+    // Add addition item status to the resolve-queue cause these can be set by problem resolutions
+    for (PoolItemList::const_iterator iter = _items_to_install.begin(); iter != _items_to_install.end(); iter++) {
+       Id id = (*iter)->satSolvable().id();
+       if (id == ID_NULL) {
+           ERR << "Install: " << *iter << " not found" << endl;
+       } else {
+           MIL << "Install " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_INSTALL_SOLVABLE );
+           queue_push( &(_jobQueue), id );
+       }
+    }
+    for (PoolItemList::const_iterator iter = _items_to_remove.begin(); iter != _items_to_remove.end(); iter++) {
+        sat::detail::IdType ident( (*iter)->satSolvable().ident().id() );
+       MIL << "Delete " << *iter << ident << endl;
+       queue_push( &(_jobQueue), SOLVER_ERASE | SOLVER_SOLVABLE_NAME | MAYBE_CLEANDEPS );
+       queue_push( &(_jobQueue), ident);
+    }
+
+    // set requirements for a running system
+    setSystemRequirements();
+
+    // set locks for the solver
+    setLocks();
+
+    // solving
+    bool ret = solving();
+
+    MIL << "SATResolver::resolveQueue() done. Ret:" << ret <<  endl;
+    return ret;
+}
+
+/** \todo duplicate code to be joined with \ref solving. */
+void SATResolver::doUpdate()
+{
+    MIL << "SATResolver::doUpdate()" << endl;
+
+    // initialize
+    solverInit(PoolItemList());
+
+    // set requirements for a running system
+    setSystemRequirements();
+
+    // set locks for the solver
+    setLocks();
+
+    _solv = solver_create( _SATPool );
+    _solv->vendorCheckCb = &vendorCheck;
+    _solv->fixsystem = _fixsystem;
+    _solv->ignorealreadyrecommended = _ignorealreadyrecommended;
+    _solv->updatesystem = true;
+    _solv->allowdowngrade = _allowdowngrade;
+    _solv->allowuninstall = _allowuninstall;
+    _solv->allowarchchange = _allowarchchange;
+    _solv->allowvendorchange = _allowvendorchange;
+    _solv->dosplitprovides = true;
+    _solv->noupdateprovide = _noupdateprovide;
+    _solv->dontinstallrecommended = _onlyRequires;
+    _solv->distupgrade = _distupgrade;
+    _solv->distupgrade_removeunsupported = _distupgrade_removeunsupported;
+
+    sat::Pool::instance().prepare();
+
+    // Solve !
+    MIL << "Starting solving for update...." << endl;
+    MIL << *this;
+    solver_solve( _solv, &(_jobQueue) );
+    MIL << "....Solver end" << endl;
+
+    // copying solution back to zypp pool
+    //-----------------------------------------
+
+    /*  solvables to be installed */
+    for (int i = 0; i < _solv->decisionq.count; i++)
+    {
+      Id p;
+      p = _solv->decisionq.elements[i];
+      if (p < 0 || !sat::Solvable(p))
+       continue;
+      if (sat::Solvable(p).repository().get() == _solv->installed)
+       continue;
+
+      PoolItem poolItem = _pool.find (sat::Solvable(p));
+      if (poolItem) {
+         SATSolutionToPool (poolItem, ResStatus::toBeInstalled, ResStatus::SOLVER);
+      } else {
+         ERR << "id " << p << " not found in ZYPP pool." << endl;
+      }
+    }
+
+    /* solvables to be erased */
+    for (int i = _solv->installed->start; i < _solv->installed->start + _solv->installed->nsolvables; i++)
+    {
+      if (_solv->decisionmap[i] > 0)
+       continue;
+
+      PoolItem poolItem( _pool.find( sat::Solvable(i) ) );
+      if (poolItem) {
+         // Check if this is an update
+         CheckIfUpdate info( (sat::Solvable(i)) );
+         invokeOnEach( _pool.byIdentBegin( poolItem ),
+                       _pool.byIdentEnd( poolItem ),
+                       resfilter::ByUninstalled(),                     // ByUninstalled
+                       functor::functorRef<bool,PoolItem> (info) );
+
+         if (info.is_updated) {
+             SATSolutionToPool (poolItem, ResStatus::toBeUninstalledDueToUpgrade , ResStatus::SOLVER);
+         } else {
+             SATSolutionToPool (poolItem, ResStatus::toBeUninstalled, ResStatus::SOLVER);
+         }
+      } else {
+         ERR << "id " << i << " not found in ZYPP pool." << endl;
+      }
+    }
+    MIL << "SATResolver::doUpdate() done" << endl;
+}
+
+
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+// error handling
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// helper function
+//----------------------------------------------------------------------------
+
+struct FindPackage : public resfilter::ResObjectFilterFunctor
+{
+    ProblemSolutionCombi *problemSolution;
+    TransactionKind action;
+    FindPackage (ProblemSolutionCombi *p, const TransactionKind act)
+       : problemSolution (p)
+       , action (act)
+       {
+       }
+
+    bool operator()( PoolItem p)
+   {
+       problemSolution->addSingleAction (p, action);
+       return true;
+   }
+};
+
+
+//----------------------------------------------------------------------------
+// Checking if this solvable/item has a buddy which reflect the real
+// user visible description of an item
+// e.g. The release package has a buddy to the concerning product item.
+// This user want's the message "Product foo conflicts with product bar" and
+// NOT "package release-foo conflicts with package release-bar"
+//----------------------------------------------------------------------------
+
+
+PoolItem SATResolver::mapItem (const PoolItem &item)
+{
+    sat::Solvable buddy = item.buddy();
+    if (buddy != sat::Solvable())
+    {
+       return _pool.find (buddy);
+    }
+    else
+    {
+       return item;
+    }
+}
+
+sat::Solvable SATResolver::mapSolvable (const Id &id)
+{
+    PoolItem item = _pool.find (sat::Solvable(id));
+    return mapItem(item).satSolvable();
+}
+
+string SATResolver::SATprobleminfoString(Id problem, string &detail, Id &ignoreId)
+{
+  string ret;
+  Pool *pool = _solv->pool;
+  Id probr;
+  Id dep, source, target;
+  sat::Solvable s, s2;
+
+  ignoreId = 0;
+  probr = solver_findproblemrule(_solv, problem);
+  switch (solver_ruleinfo(_solv, probr, &source, &target, &dep))
+  {
+      case SOLVER_RULE_DISTUPGRADE:
+         s = mapSolvable (source);
+         ret = str::form (_("%s does not belong to a distupgrade repository"), s.asString().c_str());
+         break;
+      case SOLVER_RULE_INFARCH:
+         s = mapSolvable (source);
+         ret = str::form (_("%s has inferior architecture"), s.asString().c_str());
+         break;
+      case SOLVER_RULE_UPDATE:
+         s = mapSolvable (source);
+         ret = str::form (_("problem with installed package %s"), s.asString().c_str());
+         break;
+      case SOLVER_RULE_JOB:
+         ret = _("conflicting requests");
+         break;
+      case SOLVER_RULE_RPM:
+         ret = _("some dependency problem");
+         break;
+      case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+         ret = str::form (_("nothing provides requested %s"), dep2str(pool, dep));
+         detail += _("Have you enabled all requested repositories?");
+         break;
+      case SOLVER_RULE_RPM_NOT_INSTALLABLE:
+         s = mapSolvable (source);
+         ret = str::form (_("%s is not installable"), s.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
+         ignoreId = source; // for setting weak dependencies
+         s = mapSolvable (source);
+         ret = str::form (_("nothing provides %s needed by %s"), dep2str(pool, dep), s.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_SAME_NAME:
+         s = mapSolvable (source);
+         s2 = mapSolvable (target);
+         ret = str::form (_("cannot install both %s and %s"), s.asString().c_str(), s2.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
+         s = mapSolvable (source);
+         s2 = mapSolvable (target);
+         ret = str::form (_("%s conflicts with %s provided by %s"), s.asString().c_str(), dep2str(pool, dep), s2.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
+         s = mapSolvable (source);
+         s2 = mapSolvable (target);
+         ret = str::form (_("%s obsoletes %s provided by %s"), s.asString().c_str(), dep2str(pool, dep), s2.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES:
+         s = mapSolvable (source);
+         s2 = mapSolvable (target);
+         ret = str::form (_("installed %s obsoletes %s provided by %s"), s.asString().c_str(), dep2str(pool, dep), s2.asString().c_str());
+         break;
+      case SOLVER_RULE_RPM_SELF_CONFLICT:
+         s = mapSolvable (source);
+         ret = str::form (_("solvable %s conflicts with %s provided by itself"), s.asString().c_str(), dep2str(pool, dep));
+          break;
+      case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
+         ignoreId = source; // for setting weak dependencies
+         s = mapSolvable (source);
+         Capability cap(dep);
+         sat::WhatProvides possibleProviders(cap);
+
+         // check, if a provider will be deleted
+         typedef list<PoolItem> ProviderList;
+         ProviderList providerlistInstalled, providerlistUninstalled;
+         for_( iter1, possibleProviders.begin(), possibleProviders.end() ) {
+             PoolItem provider1 = ResPool::instance().find( *iter1 );
+             // find pair of an installed/uninstalled item with the same NVR
+             bool found = false;
+             for_( iter2, possibleProviders.begin(), possibleProviders.end() ) {
+                 PoolItem provider2 = ResPool::instance().find( *iter2 );
+                 if (compareByNVR (provider1.resolvable(),provider2.resolvable()) == 0
+                     && ( (provider1.status().isInstalled() && provider2.status().isUninstalled())
+                         || (provider2.status().isInstalled() && provider1.status().isUninstalled()) ))  {
+                     found = true;
+                     break;
+                 }
+             }
+             if (!found) {
+                 if (provider1.status().isInstalled())
+                     providerlistInstalled.push_back(provider1);
+                 else
+                     providerlistUninstalled.push_back(provider1);
+             }
+         }
+
+         ret = str::form (_("%s requires %s, but this requirement cannot be provided"), s.asString().c_str(), dep2str(pool, dep));
+         if (providerlistInstalled.size() > 0) {
+             detail += _("deleted providers: ");
+             for (ProviderList::const_iterator iter = providerlistInstalled.begin(); iter != providerlistInstalled.end(); iter++) {
+                 if (iter == providerlistInstalled.begin())
+                     detail += itemToString( *iter );
+                 else
+                     detail += "\n                   " + itemToString( mapItem(*iter) );
+             }
+         }
+         if (providerlistUninstalled.size() > 0) {
+             if (detail.size() > 0)
+                 detail += _("\nuninstallable providers: ");
+             else
+                 detail = _("uninstallable providers: ");
+             for (ProviderList::const_iterator iter = providerlistUninstalled.begin(); iter != providerlistUninstalled.end(); iter++) {
+                 if (iter == providerlistUninstalled.begin())
+                     detail += itemToString( *iter );
+                 else
+                     detail += "\n                   " + itemToString( mapItem(*iter) );
+             }
+         }
+         break;
+  }
+
+  return ret;
+}
+
+ResolverProblemList
+SATResolver::problems ()
+{
+    ResolverProblemList resolverProblems;
+    if (_solv && _solv->problems.count) {
+       Pool *pool = _solv->pool;
+       int pcnt;
+       Id p, rp, what;
+       Id problem, solution, element;
+       sat::Solvable s, sd;
+
+       CapabilitySet system_requires = SystemCheck::instance().requiredSystemCap();
+       CapabilitySet system_conflicts = SystemCheck::instance().conflictSystemCap();
+
+       MIL << "Encountered problems! Here are the solutions:\n" << endl;
+       pcnt = 1;
+       problem = 0;
+       while ((problem = solver_next_problem(_solv, problem)) != 0) {
+           MIL << "Problem " <<  pcnt++ << ":" << endl;
+           MIL << "====================================" << endl;
+           string detail;
+           Id ignoreId;
+           string whatString = SATprobleminfoString (problem,detail,ignoreId);
+           MIL << whatString << endl;
+           MIL << "------------------------------------" << endl;
+           ResolverProblem_Ptr resolverProblem = new ResolverProblem (whatString, detail);
+
+           solution = 0;
+           while ((solution = solver_next_solution(_solv, problem, solution)) != 0) {
+               element = 0;
+               ProblemSolutionCombi *problemSolution = new ProblemSolutionCombi(resolverProblem);
+               while ((element = solver_next_solutionelement(_solv, problem, solution, element, &p, &rp)) != 0) {
+                   if (p == SOLVER_SOLUTION_JOB) {
+                       /* job, rp is index into job queue */
+                       what = _jobQueue.elements[rp];
+                       switch (_jobQueue.elements[rp-1]&(SOLVER_SELECTMASK|SOLVER_JOBMASK))
+                       {
+                           case SOLVER_INSTALL | SOLVER_SOLVABLE: {
+                               s = mapSolvable (what);
+                               PoolItem poolItem = _pool.find (s);
+                               if (poolItem) {
+                                   if (_solv->installed && s.get()->repo == _solv->installed) {
+                                       problemSolution->addSingleAction (poolItem, REMOVE);
+                                       string description = str::form (_("do not keep %s installed"),  s.asString().c_str() );
+                                       MIL << description << endl;
+                                       problemSolution->addDescription (description);
+                                   } else {
+                                       problemSolution->addSingleAction (poolItem, KEEP);
+                                       string description = str::form (_("do not install %s"), s.asString().c_str());
+                                       MIL << description << endl;
+                                       problemSolution->addDescription (description);
+                                   }
+                               } else {
+                                   ERR << "SOLVER_INSTALL_SOLVABLE: No item found for " << s.asString() << endl;
+                               }
+                           }
+                               break;
+                           case SOLVER_ERASE | SOLVER_SOLVABLE: {
+                               s = mapSolvable (what);
+                               PoolItem poolItem = _pool.find (s);
+                               if (poolItem) {
+                                   if (_solv->installed && s.get()->repo == _solv->installed) {
+                                       problemSolution->addSingleAction (poolItem, KEEP);
+                                       string description = str::form (_("keep %s"), s.asString().c_str());
+                                       MIL << description << endl;
+                                       problemSolution->addDescription (description);
+                                   } else {
+                                       problemSolution->addSingleAction (poolItem, UNLOCK);
+                                       string description = str::form (_("do not forbid installation of %s"), itemToString( poolItem ).c_str());
+                                       MIL << description << endl;
+                                       problemSolution->addDescription (description);
+                                   }
+                               } else {
+                                   ERR << "SOLVER_ERASE_SOLVABLE: No item found for " << s.asString() << endl;
+                               }
+                           }
+                               break;
+                           case SOLVER_INSTALL | SOLVER_SOLVABLE_NAME:
+                               {
+                               IdString ident( what );
+                               SolverQueueItemInstall_Ptr install =
+                                   new SolverQueueItemInstall(_pool, ident.asString(), false );
+                               problemSolution->addSingleAction (install, REMOVE_SOLVE_QUEUE_ITEM);
+
+                               string description = str::form (_("do not install %s"), ident.c_str() );
+                               MIL << description << endl;
+                               problemSolution->addDescription (description);
+                               }
+                               break;
+                           case SOLVER_ERASE | SOLVER_SOLVABLE_NAME:
+                               {
+                               // As we do not know, if this request has come from resolvePool or
+                               // resolveQueue we will have to take care for both cases.
+                                IdString ident( what );
+                               FindPackage info (problemSolution, KEEP);
+                               invokeOnEach( _pool.byIdentBegin( ident ),
+                                             _pool.byIdentEnd( ident ),
+                                             functor::chain (resfilter::ByInstalled (),                        // ByInstalled
+                                                             resfilter::ByTransact ()),                        // will be deinstalled
+                                             functor::functorRef<bool,PoolItem> (info) );
+
+                               SolverQueueItemDelete_Ptr del =
+                                   new SolverQueueItemDelete(_pool, ident.asString(), false );
+                               problemSolution->addSingleAction (del, REMOVE_SOLVE_QUEUE_ITEM);
+
+                               string description = str::form (_("keep %s"), ident.c_str());
+                               MIL << description << endl;
+                               problemSolution->addDescription (description);
+                               }
+                               break;
+                           case SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES:
+                               {
+                               problemSolution->addSingleAction (Capability(what), REMOVE_EXTRA_REQUIRE);
+                               string description = "";
+
+                               // Checking if this problem solution would break your system
+                               if (system_requires.find(Capability(what)) != system_requires.end()) {
+                                   // Show a better warning
+                                   resolverProblem->setDetails( resolverProblem->description() + "\n" + resolverProblem->details() );
+                                   resolverProblem->setDescription(_("This request will break your system!"));
+                                   description = _("ignore the warning of a broken system");
+                                    description += string(" (requires:")+dep2str(pool, what)+")";
+                                    MIL << description << endl;
+                                    problemSolution->addFrontDescription (description);
+                               } else {
+                                   description = str::form (_("do not ask to install a solvable providing %s"), dep2str(pool, what));
+                                    MIL << description << endl;
+                                    problemSolution->addDescription (description);
+                               }
+                               }
+                               break;
+                           case SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES:
+                               {
+                               problemSolution->addSingleAction (Capability(what), REMOVE_EXTRA_CONFLICT);
+                               string description = "";
+
+                               // Checking if this problem solution would break your system
+                               if (system_conflicts.find(Capability(what)) != system_conflicts.end()) {
+                                   // Show a better warning
+                                   resolverProblem->setDetails( resolverProblem->description() + "\n" + resolverProblem->details() );
+                                   resolverProblem->setDescription(_("This request will break your system!"));
+                                   description = _("ignore the warning of a broken system");
+                                    description += string(" (conflicts:")+dep2str(pool, what)+")";
+                                    MIL << description << endl;
+                                    problemSolution->addFrontDescription (description);
+
+                               } else {
+                                   description = str::form (_("do not ask to delete all solvables providing %s"), dep2str(pool, what));
+                                    MIL << description << endl;
+                                    problemSolution->addDescription (description);
+                               }
+                               }
+                               break;
+                           case SOLVER_UPDATE | SOLVER_SOLVABLE:
+                               {
+                               s = mapSolvable (what);
+                               PoolItem poolItem = _pool.find (s);
+                               if (poolItem) {
+                                   if (_solv->installed && s.get()->repo == _solv->installed) {
+                                       problemSolution->addSingleAction (poolItem, KEEP);
+                                       string description = str::form (_("do not install most recent version of %s"), s.asString().c_str());
+                                       MIL << description << endl;
+                                       problemSolution->addDescription (description);
+                                   } else {
+                                       ERR << "SOLVER_INSTALL_SOLVABLE_UPDATE " << poolItem << " is not selected for installation" << endl;
+                                   }
+                               } else {
+                                   ERR << "SOLVER_INSTALL_SOLVABLE_UPDATE: No item found for " << s.asString() << endl;
+                               }
+                               }
+                               break;
+                           default:
+                               MIL << "- do something different" << endl;
+                               ERR << "No valid solution available" << endl;
+                               break;
+                       }
+                   } else if (p == SOLVER_SOLUTION_INFARCH) {
+                       s = mapSolvable (rp);
+                       PoolItem poolItem = _pool.find (s);
+                       if (_solv->installed && s.get()->repo == _solv->installed) {
+                           problemSolution->addSingleAction (poolItem, LOCK);
+                           string description = str::form (_("keep %s despite the inferior architecture"), s.asString().c_str());
+                           MIL << description << endl;
+                           problemSolution->addDescription (description);
+                       } else {
+                           problemSolution->addSingleAction (poolItem, INSTALL);
+                           string description = str::form (_("install %s despite the inferior architecture"), s.asString().c_str());
+                           MIL << description << endl;
+                           problemSolution->addDescription (description);
+                       }
+                   } else if (p == SOLVER_SOLUTION_DISTUPGRADE) {
+                       s = mapSolvable (rp);
+                       PoolItem poolItem = _pool.find (s);
+                       if (_solv->installed && s.get()->repo == _solv->installed) {
+                           problemSolution->addSingleAction (poolItem, LOCK);
+                           string description = str::form (_("keep obsolete %s"), s.asString().c_str());
+                           MIL << description << endl;
+                           problemSolution->addDescription (description);
+                       } else {
+                           problemSolution->addSingleAction (poolItem, INSTALL);
+                           string description = str::form (_("install %s from excluded repository"), s.asString().c_str());
+                           MIL << description << endl;
+                           problemSolution->addDescription (description);
+                       }
+                   } else {
+                       /* policy, replace p with rp */
+                       s = mapSolvable (p);
+                       if (rp)
+                           sd = mapSolvable (rp);
+
+                       PoolItem itemFrom = _pool.find (s);
+                       if (s == sd && _solv->distupgrade)
+                       {
+                           PoolItem poolItem = _pool.find (s);
+                           if (poolItem) {
+                               problemSolution->addSingleAction (poolItem, LOCK); // for solver reason: NOT weak lock.
+                               string description = str::form (_("keep obsolete %s"), s.asString().c_str());
+                               MIL << description << endl;
+                               problemSolution->addDescription (description);
+                           } else {
+                               ERR << "SOLVER_INSTALL_SOLVABLE: No item found for " << s.asString() << endl;
+                           }
+                       }
+                       else if (rp)
+                       {
+                           int gotone = 0;
+
+                           PoolItem itemTo = _pool.find (sd);
+                           if (itemFrom && itemTo) {
+                               problemSolution->addSingleAction (itemTo, INSTALL);
+
+                               if (evrcmp(pool, s.get()->evr, sd.get()->evr, EVRCMP_COMPARE ) > 0)
+                               {
+                                   string description = str::form (_("downgrade of %s to %s"), s.asString().c_str(), sd.asString().c_str());
+                                   MIL << description << endl;
+                                   problemSolution->addDescription (description);
+                                   gotone = 1;
+                               }
+                               if (!_solv->allowarchchange && s.get()->name == sd.get()->name && s.get()->arch != sd.get()->arch
+                                   && policy_illegal_archchange(_solv, s.get(), sd.get()))
+                               {
+                                   string description = str::form (_("architecture change of %s to %s"), s.asString().c_str(), sd.asString().c_str());
+                                   MIL << description << endl;
+                                   problemSolution->addDescription (description);
+                                   gotone = 1;
+                               }
+                               if (!_solv->allowvendorchange && s.get()->name == sd.get()->name && s.get()->vendor != sd.get()->vendor
+                                   && policy_illegal_vendorchange(_solv, s.get(), sd.get()))
+                               {
+                                    IdString s_vendor( s.vendor() );
+                                    IdString sd_vendor( sd.vendor() );
+                                   string description = str::form (_("install %s (with vendor change)\n  %s  -->  %s") ,
+                                                                   sd.asString().c_str(),
+                                                                    ( s_vendor ? s_vendor.c_str() : " (no vendor) " ),
+                                                                    ( sd_vendor ? sd_vendor.c_str() : " (no vendor) " ) );
+                                   MIL << description << endl;
+                                   problemSolution->addDescription (description);
+                                   gotone = 1;
+                               }
+                               if (!gotone) {
+                                   string description = str::form (_("replacement of %s with %s"), s.asString().c_str(), sd.asString().c_str());
+                                   MIL << description << endl;
+                                   problemSolution->addDescription (description);
+                               }
+                           } else {
+                               ERR << s.asString() << " or "  << sd.asString() << " not found" << endl;
+                           }
+                       }
+                       else
+                       {
+                           if (itemFrom) {
+                               string description = str::form (_("deinstallation of %s"), s.asString().c_str());
+                               MIL << description << endl;
+                               problemSolution->addDescription (description);
+                               problemSolution->addSingleAction (itemFrom, REMOVE);
+                           }
+                       }
+                   }
+               }
+               resolverProblem->addSolution (problemSolution,
+                                             problemSolution->actionCount() > 1 ? true : false); // Solutions with more than 1 action will be shown first.
+               MIL << "------------------------------------" << endl;
+           }
+
+           if (ignoreId > 0) {
+               // There is a possibility to ignore this error by setting weak dependencies
+               PoolItem item = _pool.find (sat::Solvable(ignoreId));
+               ProblemSolutionIgnore *problemSolution = new ProblemSolutionIgnore(resolverProblem, item);
+               resolverProblem->addSolution (problemSolution,
+                                             false); // Solutions will be shown at the end
+               MIL << "ignore some dependencies of " << item << endl;
+               MIL << "------------------------------------" << endl;
+           }
+
+           // save problem
+           resolverProblems.push_back (resolverProblem);
+       }
+    }
+    return resolverProblems;
+}
+
+void
+SATResolver::applySolutions (const ProblemSolutionList & solutions)
+{
+    for (ProblemSolutionList::const_iterator iter = solutions.begin();
+        iter != solutions.end(); ++iter) {
+       ProblemSolution_Ptr solution = *iter;
+       Resolver dummyResolver(_pool);
+       if (!solution->apply (dummyResolver))
+           break;
+    }
+}
+
+void SATResolver::setLocks()
+{
+    for (PoolItemList::const_iterator iter = _items_to_lock.begin(); iter != _items_to_lock.end(); iter++) {
+        sat::detail::SolvableIdType ident( (*iter)->satSolvable().id() );
+       if (iter->status().isInstalled()) {
+           MIL << "Lock installed item " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_INSTALL_SOLVABLE );
+           queue_push( &(_jobQueue), ident );
+       } else {
+           MIL << "Lock NOT installed item " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_ERASE_SOLVABLE | MAYBE_CLEANDEPS );
+           queue_push( &(_jobQueue), ident );
+       }
+    }
+
+    for (PoolItemList::const_iterator iter = _items_to_keep.begin(); iter != _items_to_keep.end(); iter++) {
+        sat::detail::SolvableIdType ident( (*iter)->satSolvable().id() );
+       if (iter->status().isInstalled()) {
+           MIL << "Keep installed item " << *iter << endl;
+           queue_push( &(_jobQueue), SOLVER_INSTALL_SOLVABLE | SOLVER_WEAK);
+           queue_push( &(_jobQueue), ident );
+       } else {
+           MIL << "Keep NOT installed item " << *iter << ident << endl;
+           queue_push( &(_jobQueue), SOLVER_ERASE_SOLVABLE | SOLVER_WEAK | MAYBE_CLEANDEPS );
+           queue_push( &(_jobQueue), ident );
+       }
+    }
+}
+
+void SATResolver::setSystemRequirements()
+{
+    CapabilitySet system_requires = SystemCheck::instance().requiredSystemCap();
+    CapabilitySet system_conflicts = SystemCheck::instance().conflictSystemCap();
+
+    for (CapabilitySet::const_iterator iter = system_requires.begin(); iter != system_requires.end(); iter++) {
+       queue_push( &(_jobQueue), SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES );
+       queue_push( &(_jobQueue), iter->id() );
+       MIL << "SYSTEM Requires " << *iter << endl;
+    }
+
+    for (CapabilitySet::const_iterator iter = system_conflicts.begin(); iter != system_conflicts.end(); iter++) {
+       queue_push( &(_jobQueue), SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES | MAYBE_CLEANDEPS );
+       queue_push( &(_jobQueue), iter->id() );
+       MIL << "SYSTEM Conflicts " << *iter << endl;
+    }
+
+    // Lock the architecture of the running systems rpm
+    // package on distupgrade.
+    if ( _distupgrade && ZConfig::instance().systemRoot() == "/" )
+    {
+      ResPool pool( ResPool::instance() );
+      IdString rpm( "rpm" );
+      for_( it, pool.byIdentBegin(rpm), pool.byIdentEnd(rpm) )
+      {
+        if ( (*it)->isSystem() )
+        {
+          Capability archrule( (*it)->arch(), rpm.c_str(), Capability::PARSED );
+          queue_push( &(_jobQueue), SOLVER_INSTALL | SOLVABLE_NAME | SOLVER_ESSENTIAL );
+          queue_push( &(_jobQueue), archrule.id() );
+
+        }
+      }
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////
+};// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
diff --git a/zypp/solver/detail/SATResolver.h b/zypp/solver/detail/SATResolver.h
new file mode 100644 (file)
index 0000000..31b5054
--- /dev/null
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SATResolver.h
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_SAT_RESOLVER_H
+#define ZYPP_SOLVER_DETAIL_SAT_RESOLVER_H
+
+extern "C"
+{
+#include <satsolver/solver.h>
+#include <satsolver/pool.h>
+}
+
+#include <iosfwd>
+#include <list>
+#include <map>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ResPool.h"
+#include "zypp/base/SerialNumber.h"
+#include "zypp/ProblemTypes.h"
+#include "zypp/ResolverProblem.h"
+#include "zypp/ProblemSolution.h"
+#include "zypp/Capability.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+
+  namespace sat
+  {
+    class Transaction;
+  }
+
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SATResolver
+/**
+ * \todo The way solver options are passed as individual booleans from Resolver
+ * via solver::detail::Resolver to SATResolver is pedestrian and error prone.
+ * Introdce a dedicated solver option structure which is passed down as a whole.
+*/
+class SATResolver : public base::ReferenceCounted, private base::NonCopyable {
+
+  private:
+    ResPool _pool;
+    Pool *_SATPool;
+    Solver *_solv;
+    Queue _jobQueue;
+
+    // list of problematic items (orphaned)
+    PoolItemList _problem_items;
+
+    // list populated by calls to addPoolItemTo*()
+    PoolItemList _items_to_install;
+    PoolItemList _items_to_remove;
+    PoolItemList _items_to_lock;
+    PoolItemList _items_to_keep;
+
+    // solve results
+    PoolItemList _result_items_to_install;
+    PoolItemList _result_items_to_remove;
+
+    bool _fixsystem;                   // repair errors in rpm dependency graph
+    bool _allowdowngrade;              // allow to downgrade installed solvable
+    bool _allowarchchange;             // allow to change architecture of installed solvables
+    bool _allowvendorchange;           // allow to change vendor of installed solvables
+    bool _allowuninstall;              // allow removal of installed solvables
+    bool _updatesystem;                        // update
+    bool _noupdateprovide;             // true: update packages needs not to provide old package
+    bool _dosplitprovides;             // true: consider legacy split provides
+    bool _onlyRequires;                        // true: consider required packages only
+    bool _ignorealreadyrecommended;    // true: ignore recommended packages that were already recommended by the installed packages
+    bool _distupgrade;
+    bool _distupgrade_removeunsupported;
+    bool _solveSrcPackages;            // false: generate no job rule for source packages selected in the pool
+    bool _cleandepsOnRemove;           // whether removing a package should also remove no longer needed requirements
+
+    // ---------------------------------- methods
+    std::string SATprobleminfoString (Id problem, std::string &detail, Id &ignoreId);
+    void resetItemTransaction (PoolItem item);
+
+    // Create a SAT solver and reset solver selection in the pool (Collecting
+    void solverInit(const PoolItemList & weakItems);
+    // common solver run with the _jobQueue; Save results back to pool
+    bool solving(const CapabilitySet & requires_caps = CapabilitySet(),
+                const CapabilitySet & conflict_caps = CapabilitySet());
+    // cleanup solver
+    void solverEnd();
+    // set locks for the solver
+    void setLocks();
+    // set requirements for a running system
+    void setSystemRequirements();
+
+   // Checking if this solvable/item has a buddy which reflect the real
+   // user visible description of an item
+   // e.g. The release package has a buddy to the concerning product item.
+   // This user want's the message "Product foo conflicts with product bar" and
+   // NOT "package release-foo conflicts with package release-bar"
+   // So these functions return the concerning buddy (e.g. product item)
+    sat::Solvable mapSolvable (const Id &id);
+    PoolItem mapItem (const PoolItem &item);
+
+  public:
+
+    SATResolver (const ResPool & pool, Pool *SATPool);
+    virtual ~SATResolver();
+
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+    friend std::ostream& operator<<(std::ostream& str, const SATResolver & obj)
+    { return obj.dumpOn (str); }
+
+    ResPool pool (void) const;
+    void setPool (const ResPool & pool) { _pool = pool; }
+
+    // solver run with pool selected items
+    bool resolvePool(const CapabilitySet & requires_caps,
+                    const CapabilitySet & conflict_caps,
+                    const PoolItemList & weakItems,
+                    const std::set<Repository> & upgradeRepos
+                    );
+    // solver run with the given request queue
+    bool resolveQueue(const SolverQueueItemList &requestQueue,
+                     const PoolItemList & weakItems
+                     );
+    // searching for new packages
+    void doUpdate();
+
+    ResolverProblemList problems ();
+    void applySolutions (const ProblemSolutionList &solutions);
+
+    // Return the Transaction computed by the last solver run.
+    sat::Transaction getTransaction();
+
+    void addPoolItemToInstall (PoolItem item);
+    void addPoolItemsToInstallFromList (PoolItemList & rl);
+
+    void addPoolItemToLock (PoolItem item);
+    void addPoolItemToKeep (PoolItem item);
+
+    void addPoolItemToRemove (PoolItem item);
+    void addPoolItemsToRemoveFromList (PoolItemList & rl);
+
+    bool fixsystem () const {return _fixsystem;}
+    void setFixsystem ( const bool fixsystem) { _fixsystem = fixsystem;}
+
+    bool ignorealreadyrecommended () const {return _ignorealreadyrecommended;}
+    void setIgnorealreadyrecommended ( const bool ignorealreadyrecommended) { _ignorealreadyrecommended = ignorealreadyrecommended;}
+
+    bool distupgrade () const {return _distupgrade;}
+    void setDistupgrade ( const bool distupgrade) { _distupgrade = distupgrade;}
+
+    bool distupgrade_removeunsupported () const {return _distupgrade_removeunsupported;}
+    void setDistupgrade_removeunsupported ( const bool distupgrade_removeunsupported) { _distupgrade_removeunsupported = distupgrade_removeunsupported;}
+
+    bool allowdowngrade () const {return _allowdowngrade;}
+    void setAllowdowngrade ( const bool allowdowngrade) { _allowdowngrade = allowdowngrade;}
+
+    bool allowarchchange () const {return _allowarchchange;}
+    void setAllowarchchange ( const bool allowarchchange) { _allowarchchange = allowarchchange;}
+
+    bool allowvendorchange () const {return _allowvendorchange;}
+    void setAllowvendorchange ( const bool allowvendorchange) { _allowvendorchange = allowvendorchange;}
+
+    bool allowuninstall () const {return _allowuninstall;}
+    void setAllowuninstall ( const bool allowuninstall) { _allowuninstall = allowuninstall;}
+
+    bool updatesystem () const {return _updatesystem;}
+    void setUpdatesystem ( const bool updatesystem) { _updatesystem = updatesystem;}
+
+    bool noupdateprovide () const {return _noupdateprovide;}
+    void setNoupdateprovide ( const bool noupdateprovide) { _noupdateprovide = noupdateprovide;}
+
+    bool dosplitprovides () const {return _dosplitprovides;}
+    void setDosplitprovides ( const bool dosplitprovides) { _dosplitprovides = dosplitprovides;}
+
+    bool onlyRequires () const {return _onlyRequires;}
+    void setOnlyRequires ( const bool onlyRequires) { _onlyRequires = onlyRequires;}
+
+    bool solveSrcPackages() const              { return _solveSrcPackages; }
+    void setSolveSrcPackages( bool state_r )   { _solveSrcPackages = state_r; }
+
+    bool cleandepsOnRemove() const             { return _cleandepsOnRemove; }
+    void setCleandepsOnRemove( bool state_r )  { _cleandepsOnRemove = state_r; }
+
+    PoolItemList problematicUpdateItems( void ) const { return _problem_items; }
+
+    PoolItemList resultItemsToInstall () { return _result_items_to_install; }
+    PoolItemList resultItemsToRemove () { return _result_items_to_remove; }
+    PoolItemList problematicUpdateItems() { return _problem_items; }
+
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_SAT_RESOLVER_H
diff --git a/zypp/solver/detail/SolutionAction.cc b/zypp/solver/detail/SolutionAction.cc
new file mode 100644 (file)
index 0000000..c2498ea
--- /dev/null
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolutionAction.cc
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/SolutionAction.h"
+#include "zypp/Capabilities.h"
+#include "zypp/base/Logger.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolutionAction);
+IMPL_PTR_TYPE(TransactionSolutionAction);
+IMPL_PTR_TYPE(InjectSolutionAction);
+
+//---------------------------------------------------------------------------
+
+SolutionAction::SolutionAction()
+{
+}
+
+
+SolutionAction::~SolutionAction()
+{
+}
+
+
+//---------------------------------------------------------------------------
+
+ostream &
+TransactionSolutionAction::dumpOn( ostream& os) const
+{
+    os << "TransactionSolutionAction: ";
+    switch (_action) {
+       case KEEP:                      os << "Keep " << _item; break;
+       case INSTALL:                   os << "Install " << _item; break;
+       case REMOVE:                    os << "Remove " << _item; break;
+       case UNLOCK:                    os << "Unlock " << _item; break;
+       case LOCK:                      os << "Lock " << _item; break;
+       case REMOVE_EXTRA_REQUIRE:      os << "Remove require " << _capability; break;
+       case REMOVE_EXTRA_CONFLICT:     os << "Remove conflict " << _capability; break;
+       case ADD_SOLVE_QUEUE_ITEM:      os << "Add SolveQueueItem " <<  _solverQueueItem; break;
+       case REMOVE_SOLVE_QUEUE_ITEM:   os << "Remove SolveQueueItem " <<  _solverQueueItem; break;
+    }
+
+    os << endl;
+    return os;
+}
+
+
+ostream&
+operator<<( ostream& os, const SolutionActionList & actionlist)
+{
+    for (SolutionActionList::const_iterator iter = actionlist.begin(); iter != actionlist.end(); ++iter) {
+       os << *(*iter);
+       os << endl;
+    }
+    return os;
+}
+
+
+ostream&
+operator<<( ostream& os, const CSolutionActionList & actionlist)
+{
+    for (CSolutionActionList::const_iterator iter = actionlist.begin(); iter != actionlist.end(); ++iter) {
+       os << *(*iter);
+       os << endl;
+    }
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+ostream &
+InjectSolutionAction::dumpOn( ostream& os ) const
+{
+    os << "InjectSolutionAction: ";
+    switch (_kind) {
+       case WEAK:      os << "Weak"; break;
+       default: os << "Wrong kind"; break;
+    }
+    os << " ";
+    os << _item;
+    os << endl;
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+
+ostream &
+SolutionAction::dumpOn( std::ostream & os ) const
+{
+    os << "SolutionAction<";
+    os << "not specified";
+    os << "> ";
+    return os;
+}
+
+
+bool
+TransactionSolutionAction::execute(Resolver & resolver) const
+{
+    bool ret = true;
+    switch (action()) {
+       case KEEP:
+           _item.status().resetTransact (ResStatus::USER);
+           ret = _item.status().setTransact (false, ResStatus::APPL_HIGH); // APPL_HIGH: Locking should not be saved permanently
+           break;
+       case INSTALL:
+           if (_item.status().isToBeUninstalled())
+               ret = _item.status().setTransact (false, ResStatus::USER);
+           else
+               _item.status().setToBeInstalled (ResStatus::USER);
+           break;
+       case REMOVE:
+           if (_item.status().isToBeInstalled()) {
+               _item.status().setTransact (false,ResStatus::USER);
+               _item.status().setLock (true,ResStatus::USER); // no other dependency can set it again
+           } else if (_item.status().isInstalled())
+               _item.status().setToBeUninstalled (ResStatus::USER);
+           else
+               _item.status().setLock (true,ResStatus::USER); // no other dependency can set it again
+           break;
+       case UNLOCK:
+           ret = _item.status().setLock (false, ResStatus::USER);
+           if (!ret) ERR << "Cannot unlock " << _item << endl;
+           break;
+       case LOCK:
+           _item.status().resetTransact (ResStatus::USER);
+           ret = _item.status().setLock (true, ResStatus::APPL_HIGH); // APPL_HIGH: Locking should not be saved permanently
+           if (!ret) ERR << "Cannot lock " << _item << endl;
+           break;          
+       case REMOVE_EXTRA_REQUIRE:
+           resolver.removeExtraRequire (_capability);
+           break;
+       case REMOVE_EXTRA_CONFLICT:
+           resolver.removeExtraConflict (_capability);
+           break;
+       case ADD_SOLVE_QUEUE_ITEM:
+           resolver.addQueueItem(_solverQueueItem);
+           break;
+       case REMOVE_SOLVE_QUEUE_ITEM:
+           resolver.removeQueueItem(_solverQueueItem);
+           break;
+       default:
+           ERR << "Wrong TransactionKind" << endl;
+           ret = false;
+    }
+    return ret;
+}
+
+bool
+InjectSolutionAction::execute(Resolver & resolver) const
+{
+    switch (_kind) {
+        case WEAK:
+           // set item dependencies to weak
+           resolver.addWeak (_item);
+            break;
+        default:
+           ERR << "No valid InjectSolutionAction kind found" << endl;
+           return false;
+    }
+
+    return true;
+}
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolutionAction.h b/zypp/solver/detail/SolutionAction.h
new file mode 100644 (file)
index 0000000..2b03a6c
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *
+ * Easy-to use interface to the ZYPP dependency resolver
+ *
+ * Author: Stefan Hundhammer <sh@suse.de>
+ *
+ **/
+
+#ifndef ZYPP_SOLVER_DETAIL_SOLUTIONACTION_H
+#define ZYPP_SOLVER_DETAIL_SOLUTIONACTION_H
+
+#include <list>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Dep.h"
+#include "zypp/Capability.h"
+
+#include "zypp/solver/detail/Types.h"
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+       /**
+        * Abstract base class for one action of a problem solution.
+        **/
+       class SolutionAction : public base::ReferenceCounted
+       {
+       protected:
+           SolutionAction ();
+       public:
+           virtual ~SolutionAction();
+
+           // ---------------------------------- I/O
+           virtual std::ostream & dumpOn( std::ostream & str ) const;
+           friend std::ostream& operator<<(std::ostream & str, const SolutionAction & action)
+               { return action.dumpOn (str); }
+           friend std::ostream& operator<<(std::ostream & str, const SolutionActionList & actionlist);
+           friend std::ostream& operator<<(std::ostream & str, const CSolutionActionList & actionlist);
+
+           // ---------------------------------- methods
+           /**
+            * Execute this action.
+            * Returns 'true' on success, 'false' on error.
+            **/
+           virtual bool execute (Resolver & resolver) const = 0;
+       };
+
+
+       /**
+        * A problem solution action that performs a transaction
+        * (installs, removes, keep ...)  one resolvable
+        * (package, patch, pattern, product).
+        **/
+       typedef enum
+       {
+           KEEP,
+           INSTALL,
+           REMOVE,
+           UNLOCK,
+           LOCK,
+           REMOVE_EXTRA_REQUIRE,
+           REMOVE_EXTRA_CONFLICT,
+           ADD_SOLVE_QUEUE_ITEM,
+           REMOVE_SOLVE_QUEUE_ITEM,
+       } TransactionKind;
+
+
+       class TransactionSolutionAction: public SolutionAction
+       {
+       public:
+           TransactionSolutionAction( PoolItem item,
+                                      TransactionKind action )
+               : SolutionAction(),
+                 _item( item ), _action( action ) {}
+
+           TransactionSolutionAction( Capability capability,
+                                      TransactionKind action )
+               : SolutionAction(),
+                 _capability( capability ), _action( action ) {}
+
+
+           TransactionSolutionAction( SolverQueueItem_Ptr item,
+                                      TransactionKind action )
+               : SolutionAction(),
+                 _solverQueueItem( item ), _action( action ) {}
+
+           TransactionSolutionAction( TransactionKind action )
+               : SolutionAction(),
+                 _item(), _action( action ) {}
+
+         // ---------------------------------- I/O
+         virtual std::ostream & dumpOn( std::ostream & str ) const;
+         friend std::ostream& operator<<(std::ostream& str, const TransactionSolutionAction & action)
+               { return action.dumpOn (str); }
+
+         // ---------------------------------- accessors
+
+         const PoolItem item() const { return _item; }
+         const Capability capability() const { return _capability; }       
+         TransactionKind action() const { return _action; }
+
+         // ---------------------------------- methods
+           virtual bool execute(Resolver & resolver) const;
+
+       protected:
+
+           PoolItem _item;
+           Capability _capability;
+           SolverQueueItem_Ptr _solverQueueItem;
+           
+           const TransactionKind _action;
+       };
+
+
+       /**
+        * Type of ignoring; currently only WEAK
+        **/
+
+       typedef enum
+       {
+           WEAK
+       } InjectSolutionKind;
+
+
+       /**
+        * A problem solution action that injects an artificial "provides" to
+        * the pool to satisfy open requirements or remove the conflict of
+        * concerning resolvable
+        *
+        * This is typically used by "ignore" (user override) solutions.
+        **/
+       class InjectSolutionAction: public SolutionAction
+       {
+       public:
+
+           InjectSolutionAction( PoolItem item,
+                                 const InjectSolutionKind & kind)
+               : SolutionAction(),
+                 _item( item ), 
+                 _kind( kind ) {}
+
+         // ---------------------------------- I/O
+         virtual std::ostream & dumpOn( std::ostream & str ) const;
+         friend std::ostream& operator<<(std::ostream& str, const InjectSolutionAction & action)
+               { return action.dumpOn (str); }
+
+         // ---------------------------------- accessors
+           const PoolItem item() const { return _item; }
+
+         // ---------------------------------- methods
+           virtual bool execute(Resolver & resolver) const;
+
+       protected:
+           PoolItem _item;
+           const InjectSolutionKind _kind;
+       };
+
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_SOLUTIONACTION_H
+
diff --git a/zypp/solver/detail/SolverQueueItem.cc b/zypp/solver/detail/SolverQueueItem.cc
new file mode 100644 (file)
index 0000000..e7ca81d
--- /dev/null
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItem);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItem::dumpOn( std::ostream & os ) const
+{
+    switch (_type) {
+      case QUEUE_ITEM_TYPE_UNKNOWN       :     os << "unknown"; break;
+      case QUEUE_ITEM_TYPE_UPDATE        :     os << "update"; break;
+      case QUEUE_ITEM_TYPE_LOCK          :     os << "lock"; break;
+      case QUEUE_ITEM_TYPE_INSTALL       :     os << "install"; break;
+      case QUEUE_ITEM_TYPE_DELETE        :     os << "delete"; break;
+      case QUEUE_ITEM_TYPE_INSTALL_ONE_OF:     os << "install one of"; break;
+      default: os << "?solverqueueitem?"; break;
+    }
+    return os;
+}
+
+
+ostream&
+operator<<( ostream & os, const SolverQueueItemList & itemlist )
+{
+    for (SolverQueueItemList::const_iterator iter = itemlist.begin(); iter != itemlist.end(); ++iter) {
+       if (iter != itemlist.begin())
+           os << "," << endl << "\t";
+       os << **iter;
+    }
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItem::SolverQueueItem (SolverQueueItemType type, const ResPool & pool)
+    : _type (type)
+    , _pool (pool)
+{
+}
+
+
+SolverQueueItem::~SolverQueueItem()
+{
+}
+
+//---------------------------------------------------------------------------
+
+void
+SolverQueueItem::copy (const SolverQueueItem *from)
+{
+}
+
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItem.h b/zypp/solver/detail/SolverQueueItem.h
new file mode 100644 (file)
index 0000000..296d47a
--- /dev/null
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEM_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEM_H
+
+#include <iosfwd>
+#include <list>
+#include <string>
+
+#include "zypp/solver/detail/Types.h"
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ResPool.h"
+
+extern "C" {
+  struct _Queue;
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+typedef enum {
+    QUEUE_ITEM_TYPE_UNKNOWN = 0,
+    QUEUE_ITEM_TYPE_UPDATE,
+    QUEUE_ITEM_TYPE_INSTALL,
+    QUEUE_ITEM_TYPE_DELETE,
+    QUEUE_ITEM_TYPE_INSTALL_ONE_OF,
+    QUEUE_ITEM_TYPE_LOCK
+} SolverQueueItemType;
+
+
+typedef std::list<SolverQueueItem_Ptr> SolverQueueItemList;
+
+#define CMP(a,b) (((a) < (b)) - ((b) < (a)))
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItem
+
+class SolverQueueItem : public base::ReferenceCounted, private base::NonCopyable {
+
+  private:
+
+    SolverQueueItemType _type;
+    ResPool _pool;
+
+  protected:
+
+    SolverQueueItem (SolverQueueItemType type, const ResPool & pool);
+
+  public:
+
+    virtual ~SolverQueueItem();
+
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItem & obj)
+    { return obj.dumpOn (str); }
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemList & itemlist);
+
+    // ---------------------------------- accessors
+
+    ResPool pool (void) const { return _pool; }
+
+    // ---------------------------------- methods
+
+    void copy (const SolverQueueItem *from);
+
+    bool isDelete (void) const { return _type == QUEUE_ITEM_TYPE_DELETE; }
+    bool isInstall (void) const { return _type == QUEUE_ITEM_TYPE_INSTALL; }
+    bool isUpdate (void) const { return _type == QUEUE_ITEM_TYPE_UPDATE; }
+    bool isLock (void) const { return _type == QUEUE_ITEM_TYPE_LOCK; }
+    bool isInstallOneOf (void) const { return _type == QUEUE_ITEM_TYPE_INSTALL_ONE_OF; }
+
+
+    virtual SolverQueueItem_Ptr copy (void) const = 0;
+    virtual bool addRule (_Queue & q) =0 ;
+    virtual int cmp (SolverQueueItem_constPtr item) const = 0;
+    int compare (SolverQueueItem_constPtr item) const { return CMP(_type, item->_type); }
+
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEM_H
diff --git a/zypp/solver/detail/SolverQueueItemDelete.cc b/zypp/solver/detail/SolverQueueItemDelete.cc
new file mode 100644 (file)
index 0000000..f0fe78d
--- /dev/null
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/IdString.h"
+#include "zypp/Resolver.h"
+#include "zypp/solver/detail/SolverQueueItemDelete.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItemDelete);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItemDelete::dumpOn( std::ostream & os ) const
+{
+    os << "[" << (_soft?"Soft":"") << "Delete: "
+       << _name << "]";
+
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItemDelete::SolverQueueItemDelete (const ResPool & pool, std::string name, bool soft)
+    : SolverQueueItem (QUEUE_ITEM_TYPE_DELETE, pool)
+    , _name (name)
+    , _soft (soft)
+{
+}
+
+
+SolverQueueItemDelete::~SolverQueueItemDelete()
+{
+}
+
+//---------------------------------------------------------------------------
+
+bool SolverQueueItemDelete::addRule (_Queue & q)
+{
+#define MAYBE_CLEANDEPS (pool().resolver().cleandepsOnRemove()?SOLVER_CLEANDEPS:0)
+
+    ::Id id = IdString(_name).id();
+    if (_soft) {
+       queue_push( &(q), SOLVER_ERASE | SOLVER_SOLVABLE_NAME | SOLVER_WEAK | MAYBE_CLEANDEPS );
+    } else {
+       queue_push( &(q), SOLVER_ERASE | SOLVER_SOLVABLE_NAME | MAYBE_CLEANDEPS );
+    }
+    queue_push( &(q), id);
+
+    MIL << "Delete " << _name << (_soft ? "(soft)" : "")
+       << " with SAT-Pool: " << id << endl;
+    return true;
+}
+
+SolverQueueItem_Ptr
+SolverQueueItemDelete::copy (void) const
+{
+    SolverQueueItemDelete_Ptr new_delete = new SolverQueueItemDelete (pool(), _name);
+    new_delete->SolverQueueItem::copy(this);
+
+    new_delete->_soft = _soft;
+    return new_delete;
+}
+
+int
+SolverQueueItemDelete::cmp (SolverQueueItem_constPtr item) const
+{
+    int cmp = this->compare (item);
+    if (cmp != 0)
+        return cmp;
+    SolverQueueItemDelete_constPtr del = dynamic_pointer_cast<const SolverQueueItemDelete>(item);
+    if (_name != del->_name) {
+       return _name.compare(del->_name);
+    }
+    return 0;
+}
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItemDelete.h b/zypp/solver/detail/SolverQueueItemDelete.h
new file mode 100644 (file)
index 0000000..3770ada
--- /dev/null
@@ -0,0 +1,85 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEMDELETE_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEMDELETE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItemDelete
+
+class SolverQueueItemDelete : public SolverQueueItem {
+    
+  private:
+
+    std::string _name;
+    bool _soft;          // if triggered by a soft requirement (a recommends)
+
+  public:
+
+    SolverQueueItemDelete (const ResPool & pool, std::string name, bool soft = false);
+    virtual ~SolverQueueItemDelete();
+    
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemDelete & obj)
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- accessors
+
+    bool isSoft (void) const { return _soft; }    
+
+    // ---------------------------------- methods
+    
+    virtual bool addRule (_Queue & q);
+    virtual SolverQueueItem_Ptr copy (void) const;
+    virtual int cmp (SolverQueueItem_constPtr item) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEMDELETE_H
diff --git a/zypp/solver/detail/SolverQueueItemInstall.cc b/zypp/solver/detail/SolverQueueItemInstall.cc
new file mode 100644 (file)
index 0000000..778ad2e
--- /dev/null
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/IdString.h"
+#include "zypp/IdStringType.h"
+#include "zypp/solver/detail/SolverQueueItemInstall.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItemInstall);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItemInstall::dumpOn( std::ostream & os ) const
+{
+    os << "[" << (_soft?"Soft":"") << "Install: "
+    << _name
+    << "]";
+
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItemInstall::SolverQueueItemInstall (const ResPool & pool, std::string name, bool soft)
+    : SolverQueueItem (QUEUE_ITEM_TYPE_INSTALL, pool)
+    , _name (name)
+    , _soft (soft)
+{
+}
+
+
+SolverQueueItemInstall::~SolverQueueItemInstall()
+{
+}
+
+//---------------------------------------------------------------------------
+
+bool SolverQueueItemInstall::addRule (_Queue & q)
+{
+    ::Id id = IdString(_name).id();
+    if (_soft) {
+       queue_push( &(q), SOLVER_INSTALL | SOLVER_SOLVABLE_NAME | SOLVER_WEAK  );
+    } else {
+       queue_push( &(q), SOLVER_INSTALL | SOLVER_SOLVABLE_NAME );
+    }
+    queue_push( &(q), id);
+
+    MIL << "Install " << _name << (_soft ? "(soft)" : "")
+       << " with SAT-PoolID: " << id << endl;
+    return true;
+}
+
+SolverQueueItem_Ptr
+SolverQueueItemInstall::copy (void) const
+{
+    SolverQueueItemInstall_Ptr new_install = new SolverQueueItemInstall (pool(), _name);
+    new_install->SolverQueueItem::copy(this);
+
+    new_install->_soft = _soft;
+    return new_install;
+}
+
+int
+SolverQueueItemInstall::cmp (SolverQueueItem_constPtr item) const
+{
+    int cmp = this->compare (item);
+    if (cmp != 0)
+        return cmp;
+    SolverQueueItemInstall_constPtr ins = dynamic_pointer_cast<const SolverQueueItemInstall>(item);
+    if (_name != ins->_name) {
+       return _name.compare(ins->_name);
+    }
+    return 0;
+}
+
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItemInstall.h b/zypp/solver/detail/SolverQueueItemInstall.h
new file mode 100644 (file)
index 0000000..9d5df4f
--- /dev/null
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALL_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALL_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItemInstall
+
+class SolverQueueItemInstall : public SolverQueueItem {
+    
+  private:
+
+    std::string _name;
+    bool _soft;          // if triggered by a soft requirement (a recommends)
+
+  public:
+
+    SolverQueueItemInstall (const ResPool & pool, std::string name, bool soft = false);
+    virtual ~SolverQueueItemInstall();
+    
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemInstall & obj)
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- accessors
+
+    bool isSoft (void) const { return _soft; }    
+
+    // ---------------------------------- methods
+
+    virtual bool addRule (_Queue & q);    
+    virtual SolverQueueItem_Ptr copy (void) const;
+    virtual int cmp (SolverQueueItem_constPtr item) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALL_H
diff --git a/zypp/solver/detail/SolverQueueItemInstallOneOf.cc b/zypp/solver/detail/SolverQueueItemInstallOneOf.cc
new file mode 100644 (file)
index 0000000..391cae8
--- /dev/null
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/SolverQueueItemInstallOneOf.h"
+#include "zypp/sat/Pool.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItemInstallOneOf);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItemInstallOneOf::dumpOn( std::ostream & os ) const
+{
+    os << "[" << (_soft?"Soft":"") << "InstallOneOf: ";
+    for (PoolItemList::const_iterator iter = _oneOfList.begin();
+        iter != _oneOfList.end();
+        iter++)
+       os << *iter;
+    os << "]";
+
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItemInstallOneOf::SolverQueueItemInstallOneOf (const ResPool & pool, const PoolItemList & itemList,
+                                                         bool soft)
+    : SolverQueueItem (QUEUE_ITEM_TYPE_INSTALL_ONE_OF, pool)
+    , _oneOfList (itemList)
+    , _soft (soft)
+{
+}
+
+
+SolverQueueItemInstallOneOf::~SolverQueueItemInstallOneOf()
+{
+}
+
+//---------------------------------------------------------------------------
+
+bool SolverQueueItemInstallOneOf::addRule (_Queue & q)
+{
+    bool ret = true;
+    MIL << "Install one of " << (_soft ? "(soft):" : ":")<< endl;
+    Queue qs;
+
+    if (_soft) {
+       queue_push( &(q), SOLVER_INSTALL | SOLVER_SOLVABLE_ONE_OF | SOLVER_WEAK);
+    } else {
+       queue_push( &(q), SOLVER_INSTALL | SOLVER_SOLVABLE_ONE_OF );
+    }
+
+    queue_init(&qs);
+    for (PoolItemList::const_iterator iter = _oneOfList.begin(); iter != _oneOfList.end(); iter++) {
+       Id id = (*iter)->satSolvable().id();
+       if (id == ID_NULL) {
+           ERR << *iter << " not found" << endl;
+           ret = false;
+       } else {
+           MIL << "    candidate:" << *iter << " with the SAT-Pool ID: " << id << endl;
+           queue_push( &(qs), id );
+       }
+    }
+    sat::Pool satPool( sat::Pool::instance() );
+    queue_push( &(q), pool_queuetowhatprovides(satPool.get(), &qs));
+    queue_free(&qs);
+
+    return ret;
+}
+
+SolverQueueItem_Ptr
+SolverQueueItemInstallOneOf::copy (void) const
+{
+    SolverQueueItemInstallOneOf_Ptr new_installOneOf = new SolverQueueItemInstallOneOf (pool(), _oneOfList);
+    new_installOneOf->SolverQueueItem::copy(this);
+    new_installOneOf->_soft = _soft;
+
+    return new_installOneOf;
+}
+
+int
+SolverQueueItemInstallOneOf::cmp (SolverQueueItem_constPtr item) const
+{
+    int cmp = this->compare (item);
+    if (cmp != 0)
+        return cmp;
+    SolverQueueItemInstallOneOf_constPtr install = dynamic_pointer_cast<const SolverQueueItemInstallOneOf>(item);
+
+    return (_oneOfList == install->_oneOfList) ? 0 : -1; // more evaluation would be not useful
+}
+
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItemInstallOneOf.h b/zypp/solver/detail/SolverQueueItemInstallOneOf.h
new file mode 100644 (file)
index 0000000..bd1f502
--- /dev/null
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALLONEOF_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALLONEOF_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/solver/detail/SolverQueueItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItemInstallOneOf
+
+class SolverQueueItemInstallOneOf : public SolverQueueItem {
+
+  public:
+    typedef std::list<PoolItem> PoolItemList;
+    
+  private:
+
+    PoolItemList _oneOfList; // List of candidates
+    bool _soft;          // if triggered by a soft requirement (a recommends)    
+
+  public:
+
+    SolverQueueItemInstallOneOf (const ResPool & pool, const PoolItemList & itemList, bool soft = false);
+    virtual ~SolverQueueItemInstallOneOf();
+    
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemInstallOneOf & obj)
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- accessors
+
+    bool isSoft (void) const { return _soft; }    
+    
+    // ---------------------------------- methods
+
+    virtual bool addRule (_Queue & q);    
+    virtual SolverQueueItem_Ptr copy (void) const;
+    virtual int cmp (SolverQueueItem_constPtr item) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEMINSTALLONEOF_H
diff --git a/zypp/solver/detail/SolverQueueItemLock.cc b/zypp/solver/detail/SolverQueueItemLock.cc
new file mode 100644 (file)
index 0000000..346aca8
--- /dev/null
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/SolverQueueItemLock.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItemLock);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItemLock::dumpOn( std::ostream & os ) const
+{
+    os << "[" << (_soft?"Soft":"") << "Lock: " <<
+       _item << "]";
+
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItemLock::SolverQueueItemLock (const ResPool & pool,
+                                             const PoolItem & item, bool soft)
+    : SolverQueueItem (QUEUE_ITEM_TYPE_LOCK, pool)
+    , _item (item)
+    , _soft (soft)
+{
+}
+
+
+SolverQueueItemLock::~SolverQueueItemLock()
+{
+}
+
+//---------------------------------------------------------------------------
+
+bool SolverQueueItemLock::addRule (_Queue & q)
+{
+    ::Id id = _item.satSolvable().id();
+    if (id == ID_NULL) {
+       ERR << "Lock : " << _item << " not found" << endl;
+       return false;
+    }
+    MIL << "Lock " << _item << " with the SAT-Pool ID: " << id << endl;
+    if (_item.status().isInstalled()) {
+       if (_soft) {
+           queue_push( &(q), SOLVER_INSTALL_SOLVABLE | SOLVER_WEAK );
+       } else {
+           queue_push( &(q), SOLVER_INSTALL_SOLVABLE );
+       }
+    } else {
+       if (_soft) {
+           queue_push( &(q), SOLVER_ERASE_SOLVABLE | SOLVER_WEAK );
+       } else {
+           queue_push( &(q), SOLVER_ERASE_SOLVABLE );
+       }
+    }
+    queue_push( &(q), id );
+    return true;
+}
+
+SolverQueueItem_Ptr
+SolverQueueItemLock::copy (void) const
+{
+    SolverQueueItemLock_Ptr new_lock = new SolverQueueItemLock (pool(), _item);
+    new_lock->SolverQueueItem::copy(this);
+
+    new_lock->_soft = _soft;
+    return new_lock;
+}
+
+int
+SolverQueueItemLock::cmp (SolverQueueItem_constPtr item) const
+{
+    int cmp = this->compare (item);
+    if (cmp != 0)
+        return cmp;
+    SolverQueueItemLock_constPtr lock = dynamic_pointer_cast<const SolverQueueItemLock>(item);
+    return compareByNVRA (_item.resolvable(), lock->_item.resolvable());
+}
+
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItemLock.h b/zypp/solver/detail/SolverQueueItemLock.h
new file mode 100644 (file)
index 0000000..55f5104
--- /dev/null
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEMLOCK_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEMLOCK_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/solver/detail/SolverQueueItem.h"
+#include "zypp/PoolItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItemLock
+
+class SolverQueueItemLock : public SolverQueueItem {
+    
+  private:
+
+    PoolItem _item;    // the item to-be-locked
+    bool _soft;         // if triggered by a soft requirement (a recommends)
+
+  public:
+
+    SolverQueueItemLock (const ResPool & pool, const PoolItem & item, bool soft = false);
+    virtual ~SolverQueueItemLock();
+    
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemLock & obj)
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- accessors
+
+    bool isSoft (void) const { return _soft; }    
+
+    // ---------------------------------- methods
+    
+    virtual bool addRule (_Queue & q);
+    virtual SolverQueueItem_Ptr copy (void) const;
+    virtual int cmp (SolverQueueItem_constPtr item) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEMLOCK_H
diff --git a/zypp/solver/detail/SolverQueueItemUpdate.cc b/zypp/solver/detail/SolverQueueItemUpdate.cc
new file mode 100644 (file)
index 0000000..5759bdf
--- /dev/null
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* SolverQueueItem.cc
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+extern "C"
+{
+#include <satsolver/solver.h>
+}
+
+#include "zypp/base/Logger.h"
+#include "zypp/solver/detail/SolverQueueItemUpdate.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+IMPL_PTR_TYPE(SolverQueueItemUpdate);
+
+//---------------------------------------------------------------------------
+
+std::ostream &
+SolverQueueItemUpdate::dumpOn( std::ostream & os ) const
+{
+    os << "[" << (_soft?"Soft":"") << "Update: " <<
+       _item << "]";
+
+    return os;
+}
+
+//---------------------------------------------------------------------------
+
+SolverQueueItemUpdate::SolverQueueItemUpdate (const ResPool & pool,
+                                             const PoolItem & item, bool soft)
+    : SolverQueueItem (QUEUE_ITEM_TYPE_UPDATE, pool)
+    , _item (item)
+    , _soft (soft)
+{
+}
+
+
+SolverQueueItemUpdate::~SolverQueueItemUpdate()
+{
+}
+
+//---------------------------------------------------------------------------
+
+bool SolverQueueItemUpdate::addRule (_Queue & q)
+{
+    ::Id id = _item.satSolvable().id();
+    if (id == ID_NULL) {
+       ERR << "Update explicit: " << _item << " not found" << endl;
+       return false;
+    }
+    MIL << "Update explicit " << _item << " with the SAT-Pool ID: " << id << endl;
+    queue_push( &(q), SOLVER_UPDATE | SOLVER_SOLVABLE );
+    queue_push( &(q), id );
+    return true;
+}
+
+SolverQueueItem_Ptr
+SolverQueueItemUpdate::copy (void) const
+{
+    SolverQueueItemUpdate_Ptr new_update = new SolverQueueItemUpdate (pool(), _item);
+    new_update->SolverQueueItem::copy(this);
+
+    new_update->_soft = _soft;
+    return new_update;
+}
+
+int
+SolverQueueItemUpdate::cmp (SolverQueueItem_constPtr item) const
+{
+    int cmp = this->compare (item);
+    if (cmp != 0)
+        return cmp;
+    SolverQueueItemUpdate_constPtr update = dynamic_pointer_cast<const SolverQueueItemUpdate>(item);
+    return compareByNVRA (_item.resolvable(), update->_item.resolvable());
+}
+
+
+//---------------------------------------------------------------------------
+
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SolverQueueItemUpdate.h b/zypp/solver/detail/SolverQueueItemUpdate.h
new file mode 100644 (file)
index 0000000..ff00306
--- /dev/null
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* QueueItem.h
+ *
+ * Copyright (C) 2008 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_QUEUEITEMUPDATE_H
+#define ZYPP_SOLVER_DETAIL_QUEUEITEMUPDATE_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/solver/detail/SolverQueueItem.h"
+#include "zypp/PoolItem.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : SolverQueueItemUpdate
+
+class SolverQueueItemUpdate : public SolverQueueItem {
+    
+  private:
+
+    PoolItem _item;    // the item to-be-updated
+    bool _soft;         // if triggered by a soft requirement (a recommends)
+
+  public:
+
+    SolverQueueItemUpdate (const ResPool & pool, const PoolItem & item, bool soft = false);
+    virtual ~SolverQueueItemUpdate();
+    
+    // ---------------------------------- I/O
+
+    virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+    friend std::ostream& operator<<(std::ostream & str, const SolverQueueItemUpdate & obj)
+    { return obj.dumpOn (str); }
+
+    // ---------------------------------- accessors
+
+    bool isSoft (void) const { return _soft; }    
+
+    // ---------------------------------- methods
+    
+    virtual bool addRule (_Queue & q);
+    virtual SolverQueueItem_Ptr copy (void) const;
+    virtual int cmp (SolverQueueItem_constPtr item) const;
+};
+
+///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_QUEUEITEMUPDATE_H
diff --git a/zypp/solver/detail/SystemCheck.cc b/zypp/solver/detail/SystemCheck.cc
new file mode 100644 (file)
index 0000000..cd6da86
--- /dev/null
@@ -0,0 +1,132 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/solver/detail/SystemCheck.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZConfig.h"
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+#include "zypp/solver/detail/SystemCheck.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+    Pathname           _file = "";
+    CapabilitySet      _require;
+    CapabilitySet      _conflict;
+
+    typedef vector<string> CapList;
+
+    const SystemCheck & SystemCheck::instance()
+    {
+       static SystemCheck _val;
+       return _val;
+    }
+
+
+    SystemCheck::SystemCheck() {
+       if (_file.empty()) {
+           _file = ZConfig::instance().solver_checkSystemFile();
+           loadFile();
+       }
+    }
+
+    bool SystemCheck::setFile(const Pathname & file) const{
+       MIL << "Setting checkFile to : " << file << endl;
+       _file = file;
+       loadFile();
+       return true;
+    }
+
+    const Pathname & SystemCheck::file() {
+       return _file;
+    }
+
+    const CapabilitySet & SystemCheck::requiredSystemCap() const{
+       return _require;
+    }
+
+    const CapabilitySet & SystemCheck::conflictSystemCap() const{
+       return _conflict;
+    }
+
+    bool SystemCheck::loadFile() const{
+        Target_Ptr trg( getZYpp()->getTarget() );
+        if ( trg )
+          _file = trg->assertRootPrefix( _file );
+
+       PathInfo pi( _file );
+       if ( ! pi.isFile() ) {
+           WAR << "Can't read " << _file << " " << pi << endl;
+           return false;
+       }
+
+       _require.clear();
+       _conflict.clear();
+
+       std::ifstream infile( _file.c_str() );
+       for( iostr::EachLine in( infile ); in; in.next() ) {
+           std::string l( str::trim(*in) );
+           if ( ! l.empty() && l[0] != '#' )
+           {
+               CapList capList;
+               str::split( l, back_inserter(capList), ":" );
+               if (capList.size() == 2 ) {
+                   CapList::iterator it = capList.begin();
+                   if (*it == "requires") {
+                       _require.insert(Capability(*(it+1)));
+                   } else if (*it == "conflicts") {
+                       _conflict.insert(Capability(*(it+1)));
+                   } else {
+                       ERR << "Wrong parameter: " << l << endl;
+                   }
+               } else {
+                   ERR << "Wrong line: " << l << endl;
+               }
+           }
+       }
+       MIL << "Read " << pi << endl;
+       return true;
+    }
+
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const SystemCheck & obj )
+    {
+      str << _file << endl;
+      str << "requires" << endl;
+      for (CapabilitySet::const_iterator it = _require.begin(); it != _require.end(); ++it)
+         str << "  " << *it << endl;
+
+      str << "conflicts" << endl;
+      for (CapabilitySet::const_iterator it = _conflict.begin(); it != _conflict.end(); ++it)
+         str << "  " << *it << endl;
+
+      return str;
+    }
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/SystemCheck.h b/zypp/solver/detail/SystemCheck.h
new file mode 100644 (file)
index 0000000..92a8659
--- /dev/null
@@ -0,0 +1,68 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/SystemCheck.h
+ *
+*/
+#ifndef ZYPP_TARGET_SYSTEMCHECK_H
+#define ZYPP_TARGET_SYSTEMCHECK_H
+
+#include <iosfwd>
+
+#include "zypp/base/NonCopyable.h"
+#include "zypp/Capability.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SystemCheck
+    //
+    /** Save and restore locale set from file.
+     */
+    class SystemCheck : private base::NonCopyable
+    {
+      friend std::ostream & operator<<( std::ostream & str, const SystemCheck & obj );
+
+      public:
+
+       /** Singleton */
+       static const SystemCheck & instance();
+
+        /** Return the file path. */
+        const Pathname & file();
+
+        /** Set configuration file of system requirements
+        *  Should be used for testcase only   
+        */
+        bool setFile(const Pathname & file) const;
+
+        /** Returns a list of required system capabilities.
+        */
+        const CapabilitySet & requiredSystemCap() const;
+
+        /** Returns a list of conflicting system capabilities.
+        */
+        const CapabilitySet & conflictSystemCap() const;
+
+      private:
+        /** Ctor taking the file to read. */
+        SystemCheck();
+       bool loadFile() const;
+
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates SystemCheck Stream output */
+    std::ostream & operator<<( std::ostream & str, const SystemCheck & obj );
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_SYSTEMCHECK_H
diff --git a/zypp/solver/detail/Testcase.cc b/zypp/solver/detail/Testcase.cc
new file mode 100644 (file)
index 0000000..c5ff70c
--- /dev/null
@@ -0,0 +1,586 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/solver/detail/Testcase.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <streambuf>
+
+#include "zypp/solver/detail/Testcase.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
+#include "zypp/base/GzStream.h"
+#include "zypp/base/String.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/ReferenceCounted.h"
+
+#include "zypp/parser/xml/XmlEscape.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/PathInfo.h"
+#include "zypp/ResPool.h"
+#include "zypp/Repository.h"
+
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/solver/detail/SystemCheck.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+#define TAB "\t"
+#define TAB2 "\t\t"
+
+using namespace std;
+using namespace zypp::str;
+
+//---------------------------------------------------------------------------
+
+inline std::string xml_escape( const std::string &text )
+{
+  return zypp::xml::escape(text);
+}
+
+inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
+{
+  string result;
+  result += "<" + tag + ">";
+
+  if ( escape)
+   result += xml_escape(text);
+  else
+   result += text;
+
+  result += "</" + tag + ">";
+  return result;
+}
+
+template<class T>
+std::string helixXML( const T &obj ); //undefined
+
+template<>
+std::string helixXML( const Edition &edition )
+{
+    stringstream str;
+    str << xml_tag_enclose(edition.version(), "version");
+    if (!edition.release().empty())
+       str << xml_tag_enclose(edition.release(), "release");
+    if (edition.epoch() != Edition::noepoch)
+       str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
+    return str.str();
+}
+
+template<>
+std::string helixXML( const Arch &arch )
+{
+    stringstream str;
+    str << xml_tag_enclose(arch.asString(), "arch");
+    return str.str();
+}
+
+template<>
+std::string helixXML( const Capability &cap )
+{
+    stringstream str;
+    CapDetail detail = cap.detail();
+    if (detail.isSimple()) {
+       if (detail.isVersioned()) {
+           str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
+               << " op='" << xml_escape(detail.op().asString()) << "'"
+               << " version='" <<  xml_escape(detail.ed().version()) << "'";
+           if (!detail.ed().release().empty())
+               str << " release='" << xml_escape(detail.ed().release()) << "'";
+           if (detail.ed().epoch() != Edition::noepoch)
+               str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
+           str << " />" << endl;
+       } else {
+           str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
+       }
+    } else if (detail.isExpression()) {
+       if (detail.capRel() == CapDetail::CAP_AND
+           && detail.lhs().detail().isNamed()
+           && detail.rhs().detail().isNamed()) {
+           // packageand dependency
+           str << "<dep name='packageand("
+               << IdString(detail.lhs().id()) << ":"
+               << IdString(detail.rhs().id()) << ")' />" << endl;
+       } else {
+           // modalias ?
+           IdString packageName;
+           if (detail.capRel() == CapDetail::CAP_AND) {
+               packageName = IdString(detail.lhs().id());
+               detail = detail.rhs().detail();
+           }
+           if (detail.capRel() == CapDetail::CAP_NAMESPACE
+               && detail.lhs().id() == NAMESPACE_MODALIAS) {
+               str << "<dep name='modalias(";
+               if (!packageName.empty())
+                   str << packageName << ":";
+               str << IdString(detail.rhs().id()) << ")' />" << endl;
+           } else {
+               str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
+           }
+       }
+    } else {
+       str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
+    }
+
+    return str.str();
+}
+
+template<>
+std::string helixXML( const Capabilities &caps )
+{
+    stringstream str;
+    Capabilities::const_iterator it = caps.begin();
+    str << endl;
+    for ( ; it != caps.end(); ++it)
+    {
+       str << TAB2 << helixXML((*it));
+    }
+    str << TAB;
+    return str.str();
+}
+
+template<>
+std::string helixXML( const CapabilitySet &caps )
+{
+    stringstream str;
+    CapabilitySet::const_iterator it = caps.begin();
+    str << endl;
+    for ( ; it != caps.end(); ++it)
+    {
+       str << TAB2 << helixXML((*it));
+    }
+    str << TAB;
+    return str.str();
+}
+
+inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
+{
+  stringstream out;
+  Capabilities caps( obj->dep(deptag_r) );
+  if ( ! caps.empty() )
+    out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
+  return out.str();
+}
+
+std::string helixXML( const PoolItem &item )
+{
+  const Resolvable::constPtr resolvable = item.resolvable();
+  stringstream str;
+  str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
+  str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
+  str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
+  str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;
+  if ( isKind<Package>(resolvable) ) {
+      str << TAB << "<history>" << endl << TAB << "<update>" << endl;
+      str << TAB2 << helixXML (resolvable->arch()) << endl;
+      str << TAB2 << helixXML (resolvable->edition()) << endl;
+      str << TAB << "</update>" << endl << TAB << "</history>" << endl;
+  } else {
+      str << TAB << helixXML (resolvable->arch()) << endl;
+      str << TAB << helixXML (resolvable->edition()) << endl;
+  }
+  str << helixXML( resolvable, Dep::PROVIDES);
+  str << helixXML( resolvable, Dep::PREREQUIRES);
+  str << helixXML( resolvable, Dep::CONFLICTS);
+  str << helixXML( resolvable, Dep::OBSOLETES);
+  str << helixXML( resolvable, Dep::REQUIRES);
+  str << helixXML( resolvable, Dep::RECOMMENDS);
+  str << helixXML( resolvable, Dep::ENHANCES);
+  str << helixXML( resolvable, Dep::SUPPLEMENTS);
+  str << helixXML( resolvable, Dep::SUGGESTS);
+
+  str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
+  return str.str();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : HelixResolvable
+/**
+ * Creates a file in helix format which includes all available
+ * or installed packages,patches,selections.....
+ **/
+class  HelixResolvable : public base::ReferenceCounted, private base::NonCopyable{
+
+  private:
+    std::string dumpFile; // Path of the generated testcase
+    ofgzstream *file;
+
+  public:
+    HelixResolvable (const std::string & path);
+    ~HelixResolvable ();
+
+    void addResolvable (const PoolItem item)
+    { *file << helixXML (item); }
+
+    std::string filename ()
+    { return dumpFile; }
+};
+
+DEFINE_PTR_TYPE(HelixResolvable);
+IMPL_PTR_TYPE(HelixResolvable);
+
+typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
+
+HelixResolvable::HelixResolvable(const std::string & path)
+    :dumpFile (path)
+{
+    file = new ofgzstream(path.c_str());
+    if (!file) {
+       ZYPP_THROW (Exception( "Can't open " + path ) );
+    }
+
+    *file << "<channel><subchannel>" << endl;
+}
+
+HelixResolvable::~HelixResolvable()
+{
+    *file << "</subchannel></channel>" << endl;
+    delete(file);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : HelixControl
+/**
+ * Creates a file in helix format which contains all controll
+ * action of a testcase ( file is known as *-test.xml)
+ **/
+class  HelixControl {
+
+  private:
+    std::string dumpFile; // Path of the generated testcase
+    std::ofstream *file;
+
+  public:
+    HelixControl (const std::string & controlPath,
+                 const RepositoryTable & sourceTable,
+                 const Arch & systemArchitecture,
+                 const LocaleSet &languages,
+                 const std::string & systemPath = "solver-system.xml.gz",
+                 const bool forceResolve = false,
+                 const bool onlyRequires = false,
+                 const bool ignorealreadyrecommended = false);
+    HelixControl ();
+    ~HelixControl ();
+
+    void installResolvable (const ResObject::constPtr &resObject,
+                           const ResStatus &status);
+    void lockResolvable (const ResObject::constPtr &resObject,
+                        const ResStatus &status);
+    void keepResolvable (const ResObject::constPtr &resObject,
+                        const ResStatus &status);
+    void deleteResolvable (const ResObject::constPtr &resObject,
+                          const ResStatus &status);
+    void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
+    void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
+
+    void distupgrade ();
+    void verifySystem ();
+    void update ();
+
+    std::string filename () { return dumpFile; }
+};
+
+HelixControl::HelixControl(const std::string & controlPath,
+                          const RepositoryTable & repoTable,
+                          const Arch & systemArchitecture,
+                          const LocaleSet &languages,
+                          const std::string & systemPath,
+                          const bool forceResolve,
+                          const bool onlyRequires,
+                          const bool ignorealreadyrecommended)
+    :dumpFile (controlPath)
+{
+    file = new ofstream(controlPath.c_str());
+    if (!file) {
+       ZYPP_THROW (Exception( "Can't open " + controlPath ) );
+    }
+
+    *file << "<?xml version=\"1.0\"?>" << endl
+         << "<!-- testcase generated by YaST -->" << endl
+         << "<test>" << endl
+         << "<setup arch=\"" << systemArchitecture << "\">" << endl
+         << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
+    for ( RepositoryTable::const_iterator it = repoTable.begin();
+         it != repoTable.end(); ++it ) {
+       RepoInfo repo = it->first.info();
+       *file << TAB << "<!-- " << endl
+             << TAB << "- alias       : " << repo.alias() << endl;
+       for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
+             itUrl != repo.baseUrlsEnd();
+             ++itUrl )
+       {
+           *file << TAB << "- url         : " << *itUrl << endl;
+       }
+       *file << TAB << "- path        : " << repo.path() << endl;
+       *file << TAB << "- type        : " << repo.type() << endl;
+       *file << TAB << "- generated   : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
+       *file << TAB << "- outdated    : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
+       *file << TAB << " -->" << endl;
+
+       *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
+             << "-package.xml.gz\" name=\"" << repo.alias() << "\""
+             << " priority=\"" << repo.priority()
+             << "\" />" << endl << endl;
+    }
+    for (LocaleSet::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
+       *file << TAB << "<locale name=\"" <<  iter->code()
+             << "\" />" << endl;
+    }
+
+    if (forceResolve)
+       *file << TAB << "<forceResolve/>" << endl;
+    if (onlyRequires)
+       *file << TAB << "<onlyRequires/>" << endl;
+
+    if (ignorealreadyrecommended)
+       *file << TAB << "<ignorealreadyrecommended/>" << endl;
+
+    *file << "</setup>" << endl
+         << "<trial>" << endl;
+}
+
+HelixControl::HelixControl()
+    :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
+{
+    HelixControl (dumpFile);
+}
+
+HelixControl::~HelixControl()
+{
+    *file << "</trial>" << endl
+         << "</test>" << endl;
+    delete(file);
+}
+
+void HelixControl::installResolvable(const ResObject::constPtr &resObject,
+                                    const ResStatus &status)
+{
+    *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
+         << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
+         << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
+         << " status=\"" << status << "\""
+         << "/>" << endl;
+}
+
+void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
+                                 const ResStatus &status)
+{
+    *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
+         << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
+         << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
+         << " status=\"" << status << "\""
+         << "/>" << endl;
+}
+
+void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
+                                 const ResStatus &status)
+{
+    *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
+         << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
+         << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
+         << " status=\"" << status << "\""
+         << "/>" << endl;
+}
+
+void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
+                                   const ResStatus &status)
+{
+    *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
+         << " name=\"" << resObject->name() << "\""
+         << " status=\"" << status << "\""
+         << "/>" << endl;
+}
+
+void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
+{
+    for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
+       *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
+    }
+    for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
+       *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
+    }
+}
+
+void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
+{
+  for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
+  {
+    *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
+  }
+}
+
+void HelixControl::distupgrade()
+{
+    *file << "<distupgrade/>" << endl;
+}
+
+void HelixControl::verifySystem()
+{
+    *file << "<verify/>" << endl;
+}
+
+void HelixControl::update()
+{
+    *file << "<update/>" << endl;
+}
+
+//---------------------------------------------------------------------------
+
+Testcase::Testcase()
+    :dumpPath("/var/log/YaST2/solverTestcase")
+{}
+
+Testcase::Testcase(const std::string & path)
+    :dumpPath(path)
+{}
+
+Testcase::~Testcase()
+{}
+
+bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
+{
+    PathInfo path (dumpPath);
+
+    if ( !path.isExist() ) {
+       if (zypp::filesystem::assert_dir (dumpPath)!=0) {
+           ERR << "Cannot create directory " << dumpPath << endl;
+           return false;
+       }
+    } else {
+       if (!path.isDir()) {
+           ERR << dumpPath << " is not a directory." << endl;
+           return false;
+       }
+       // remove old stuff if pool will be dump
+       if (dumpPool)
+           zypp::filesystem::clean_dir (dumpPath);
+    }
+
+    if (runSolver) {
+        zypp::base::LogControl::TmpLineWriter tempRedirect;
+       zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
+       zypp::base::LogControl::TmpExcessive excessive;
+
+       resolver.resolvePool();
+    }
+
+    ResPool pool       = resolver.pool();
+    RepositoryTable    repoTable;
+    PoolItemList       items_to_install;
+    PoolItemList       items_to_remove;
+    PoolItemList       items_locked;
+    PoolItemList       items_keep;
+    HelixResolvable_Ptr        system = NULL;
+
+    if (dumpPool)
+       system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
+
+    for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
+    {
+       Resolvable::constPtr res = it->resolvable();
+
+       if ( system && it->status().isInstalled() ) {
+           // system channel
+           system->addResolvable (*it);
+       } else {
+           // repo channels
+           Repository repo  = it->resolvable()->satSolvable().repository();
+           if (dumpPool) {
+               if (repoTable.find (repo) == repoTable.end()) {
+                   repoTable[repo] = new HelixResolvable(dumpPath + "/"
+                                                         + str::numstring((long)repo.id())
+                                                         + "-package.xml.gz");
+               }
+               repoTable[repo]->addResolvable (*it);
+           }
+       }
+
+       if ( it->status().isToBeInstalled()
+            && !(it->status().isBySolver())) {
+           items_to_install.push_back (*it);
+       }
+       if ( it->status().isKept()
+            && !(it->status().isBySolver())) {
+           items_keep.push_back (*it);
+       }
+       if ( it->status().isToBeUninstalled()
+            && !(it->status().isBySolver())) {
+           items_to_remove.push_back (*it);
+       }
+       if ( it->status().isLocked()
+            && !(it->status().isBySolver())) {
+           items_locked.push_back (*it);
+       }
+    }
+
+    // writing control file "*-test.xml"
+    HelixControl control (dumpPath + "/solver-test.xml",
+                         repoTable,
+                         ZConfig::instance().systemArchitecture(),
+                         pool.getRequestedLocales(),
+                         "solver-system.xml.gz",
+                         resolver.forceResolve(),
+                         resolver.onlyRequires(),
+                         resolver.ignoreAlreadyRecommended() );
+
+    for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
+       control.installResolvable (iter->resolvable(), iter->status());
+    }
+
+    for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
+       control.lockResolvable (iter->resolvable(), iter->status());
+    }
+
+    for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
+       control.keepResolvable (iter->resolvable(), iter->status());
+    }
+
+    for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
+       control.deleteResolvable (iter->resolvable(), iter->status());
+    }
+
+    control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
+    control.addDependencies (SystemCheck::instance().requiredSystemCap(),
+                            SystemCheck::instance().conflictSystemCap());
+    control.addUpgradeRepos( resolver.upgradeRepos() );
+
+    if (resolver.isUpgradeMode())
+       control.distupgrade ();
+    if (resolver.isUpdateMode())
+       control.update();
+    if (resolver.isVerifyingMode())
+       control.verifySystem();
+
+    return true;
+}
+
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
diff --git a/zypp/solver/detail/Testcase.h b/zypp/solver/detail/Testcase.h
new file mode 100644 (file)
index 0000000..16ceb9b
--- /dev/null
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file       zypp/solver/detail/Testcase.h
+ *
+*/
+
+#ifndef ZYPP_SOLVER_DETAIL_TESTCASE_H
+#define ZYPP_SOLVER_DETAIL_TESTCASE_H
+
+#include <string>
+#include "zypp/solver/detail/Resolver.h"
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : Testcase
+      /**
+       * Generating a testcase of the current pool and solver state.
+       **/
+      class Testcase
+      {
+       private:
+         std::string dumpPath; // Path of the generated testcase
+
+       public:
+         Testcase();
+         Testcase( const std::string & path );
+         ~Testcase();
+
+         bool createTestcase( Resolver & resolver, bool dumpPool = true, bool runSolver = true );
+      };
+
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_TESTCASE_H
diff --git a/zypp/solver/detail/Types.h b/zypp/solver/detail/Types.h
new file mode 100644 (file)
index 0000000..e50f351
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* Types.h
+ *
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_TYPES_H
+#define ZYPP_SOLVER_DETAIL_TYPES_H
+
+#include <iosfwd>
+#include <list>
+#include <set>
+#include <map>
+#include <string>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Functional.h"
+
+#include "zypp/PoolItem.h"
+
+#define _DEBUG(x) DBG << x << std::endl;
+#define _XDEBUG(x) do { if (base::logger::isExcessive()) XXX << x << std::endl;} while (0)
+//#define _DEBUG(x)
+
+/////////////////////////////////////////////////////////////////////////
+namespace zypp 
+{ ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+  namespace solver
+  { /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+    namespace detail
+    { ///////////////////////////////////////////////////////////////////
+
+typedef std::list<PoolItem> PoolItemList;
+typedef std::set<PoolItem> PoolItemSet;
+      
+DEFINE_PTR_TYPE(Resolver);
+
+DEFINE_PTR_TYPE(SolutionAction);
+typedef std::list<SolutionAction_Ptr> SolutionActionList;
+typedef std::list<SolutionAction_constPtr> CSolutionActionList;
+DEFINE_PTR_TYPE(TransactionSolutionAction);
+DEFINE_PTR_TYPE(InjectSolutionAction);
+DEFINE_PTR_TYPE(SolverQueueItem);
+DEFINE_PTR_TYPE(SolverQueueItemUpdate);
+DEFINE_PTR_TYPE(SolverQueueItemDelete);
+DEFINE_PTR_TYPE(SolverQueueItemInstall);       
+DEFINE_PTR_TYPE(SolverQueueItemInstallOneOf);
+DEFINE_PTR_TYPE(SolverQueueItemLock);          
+      
+      ///////////////////////////////////////////////////////////////////
+    };// namespace detail
+    /////////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////////
+  };// namespace solver
+  ///////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////
+};// namespace zypp
+/////////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_SOLVER_DETAIL_TYPES_H
diff --git a/zypp/solver/libzypp_solver.h b/zypp/solver/libzypp_solver.h
new file mode 100644 (file)
index 0000000..fab8352
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* libzypp.h
+ * Copyright (C) 2000-2002 Ximian, Inc.
+ * Copyright (C) 2005 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef ZYPP_SOLVER_DETAIL_LIBZYPP_H
+#define ZYPP_SOLVER_DETAIL_LIBZYPP_H
+
+#include "zypp/solver/detail/Resolver.h"
+#include "zypp/solver/detail/ResolverContext.h"
+#include "zypp/solver/detail/ResolverProblem.h"
+#include "zypp/solver/detail/ProblemSolution.h"
+#include "zypp/solver/detail/SolutionAction.h"
+#include "zypp/solver/detail/InstallOrder.h"
+
+#endif // ZYPP_SOLVER_DETAIL_LIBZYPP_H
diff --git a/zypp/target/CommitPackageCache.cc b/zypp/target/CommitPackageCache.cc
new file mode 100644 (file)
index 0000000..11425c0
--- /dev/null
@@ -0,0 +1,80 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCache.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/target/CommitPackageCache.h"
+#include "zypp/target/CommitPackageCacheImpl.h"
+#include "zypp/target/CommitPackageCacheReadAhead.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CommitPackageCache
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    CommitPackageCache::CommitPackageCache( Impl * pimpl_r )
+    : _pimpl( pimpl_r )
+    {
+      assert( _pimpl );
+    }
+
+    CommitPackageCache::CommitPackageCache( const Pathname &        rootDir_r,
+                                            const PackageProvider & packageProvider_r )
+    {
+      if ( getenv("ZYPP_COMMIT_NO_PACKAGE_CACHE") )
+        {
+          MIL << "$ZYPP_COMMIT_NO_PACKAGE_CACHE is set." << endl;
+          _pimpl.reset( new Impl( packageProvider_r ) ); // no cache
+        }
+      else
+        {
+          _pimpl.reset( new CommitPackageCacheReadAhead( rootDir_r, packageProvider_r ) );
+        }
+      assert( _pimpl );
+    }
+
+    CommitPackageCache::~CommitPackageCache()
+    {}
+
+    void CommitPackageCache::setCommitList( std::vector<sat::Solvable> commitList_r )
+    {
+      _pimpl->setCommitList( commitList_r );
+    }
+
+    ManagedFile CommitPackageCache::get( const PoolItem & citem_r )
+    { return _pimpl->get( citem_r ); }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const CommitPackageCache & obj )
+    { return str << *obj._pimpl; }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/CommitPackageCache.h b/zypp/target/CommitPackageCache.h
new file mode 100644 (file)
index 0000000..c1c5797
--- /dev/null
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCache.h
+ *
+*/
+#ifndef ZYPP_TARGET_COMMITPACKAGECACHE_H
+#define ZYPP_TARGET_COMMITPACKAGECACHE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/Pathname.h"
+#include "zypp/ManagedFile.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CommitPackageCache
+    //
+    /** Target::commit helper optimizing package provision.
+    */
+    class CommitPackageCache
+    {
+      friend std::ostream & operator<<( std::ostream & str, const CommitPackageCache & obj );
+
+    public:
+      typedef function<ManagedFile( const PoolItem & pi )> PackageProvider;
+
+    public:
+      /** Ctor */
+      CommitPackageCache( const Pathname &        rootDir_r,
+                          const PackageProvider & packageProvider_r );
+
+      /** Dtor */
+      ~CommitPackageCache();
+
+    public:
+      /** Download(commit) sequence of solvables to compute read ahead. */
+      void setCommitList( std::vector<sat::Solvable> commitList_r );
+      /** \overload */
+      template <class _Iterator>
+      void setCommitList( _Iterator begin_r, _Iterator end_r )
+      { setCommitList( std::vector<sat::Solvable>( begin_r, end_r  ) ); }
+
+      /** Provide a package. */
+      ManagedFile get( const PoolItem & citem_r );
+
+    public:
+      /** Implementation. */
+      class Impl;
+      /** Ctor taking an implementation. */
+      explicit CommitPackageCache( Impl * pimpl_r );
+    private:
+      /** Pointer to implementation. */
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates CommitPackageCache Stream output */
+    std::ostream & operator<<( std::ostream & str, const CommitPackageCache & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_COMMITPACKAGECACHE_H
diff --git a/zypp/target/CommitPackageCacheImpl.cc b/zypp/target/CommitPackageCacheImpl.cc
new file mode 100644 (file)
index 0000000..9beda5a
--- /dev/null
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCacheImpl.cc
+ *
+*/
+#include <iostream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/CommitPackageCacheImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/CommitPackageCacheImpl.h b/zypp/target/CommitPackageCacheImpl.h
new file mode 100644 (file)
index 0000000..41f8c1e
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCacheImpl.h
+ *
+*/
+#ifndef ZYPP_TARGET_COMMITPACKAGECACHEIMPL_H
+#define ZYPP_TARGET_COMMITPACKAGECACHEIMPL_H
+
+#include <iosfwd>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/target/CommitPackageCache.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CommitPackageCache::Impl
+    //
+    /** Base for CommitPackageCache implementations (implements no chache).
+     *
+     * All packages are directly retrieved from the source via the
+     * PackageProvider passed to the ctor. The PackageProvider is expected
+     * to throw or return an empty ManagedFile if the package can't be provided.
+    */
+    class CommitPackageCache::Impl
+    {
+    public:
+      typedef CommitPackageCache::PackageProvider  PackageProvider;
+
+    public:
+      Impl( const PackageProvider & packageProvider_r )
+      : _packageProvider( packageProvider_r )
+      {}
+
+      virtual ~Impl()
+      {}
+
+    public:
+      /** Provide the package.
+       * Derived classes overload this.
+      */
+      virtual ManagedFile get( const PoolItem & citem_r )
+      {
+        return sourceProvidePackage( citem_r );
+      }
+
+      void setCommitList( std::vector<sat::Solvable> commitList_r )
+      { _commitList = commitList_r; }
+
+    protected:
+      /** Let the Source provide the package. */
+      virtual ManagedFile sourceProvidePackage( const PoolItem & pi ) const
+      {
+        if ( ! _packageProvider )
+          {
+            ZYPP_THROW( Exception("No package provider configured.") );
+          }
+
+        ManagedFile ret( _packageProvider( pi ) );
+        if ( ret.value().empty() )
+          {
+            ZYPP_THROW( Exception("Package provider failed.") );
+          }
+
+        return ret;
+      }
+
+    protected:
+      std::vector<sat::Solvable> _commitList;
+
+    private:
+      PackageProvider _packageProvider;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates CommitPackageCache::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const CommitPackageCache::Impl & obj )
+    {
+      return str << "CommitPackageCache::Impl";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_COMMITPACKAGECACHEIMPL_H
diff --git a/zypp/target/CommitPackageCacheReadAhead.cc b/zypp/target/CommitPackageCacheReadAhead.cc
new file mode 100644 (file)
index 0000000..e55c082
--- /dev/null
@@ -0,0 +1,247 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCacheReadAhead.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/PathInfo.h"
+#include "zypp/RepoInfo.h"
+#include "zypp/target/CommitPackageCacheReadAhead.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : IMediaKey
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    std::ostream & operator<<( std::ostream & str, const IMediaKey & obj )
+    {
+      return str << "[S" << obj._repo.id() << ":" << obj._mediaNr << "]"
+                 << " " << obj._repo.info().alias();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CommitPackageCacheReadAhead
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : CommitPackageCacheReadAhead::CommitPackageCacheReadAhead
+    // METHOD TYPE : Ctor
+    //
+    CommitPackageCacheReadAhead::CommitPackageCacheReadAhead( const Pathname &        rootDir_r,
+                                                              const PackageProvider & packageProvider_r )
+    : CommitPackageCache::Impl( packageProvider_r )
+    , _rootDir( rootDir_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : CommitPackageCacheReadAhead::onInteractiveMedia
+    // METHOD TYPE : bool
+    //
+    bool CommitPackageCacheReadAhead::onInteractiveMedia( const PoolItem & pi ) const
+    {
+      if ( pi->mediaNr() == 0 ) // no media access at all
+        return false;
+      if ( pi->repoInfo().baseUrlsEmpty() )
+        return false; // no Url - should actually not happen
+      std::string scheme( pi->repoInfo().baseUrlsBegin()->getScheme() );
+      return ( scheme == "dvd" || scheme == "cd" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : CommitPackageCacheReadAhead::cacheLastInteractive
+    // METHOD TYPE : void
+    //
+    void CommitPackageCacheReadAhead::cacheLastInteractive( const PoolItem & citem_r )
+    {
+      // Fill cache errors are never proagated.
+      try
+        {
+          doCacheLastInteractive( citem_r );
+        }
+      catch ( const Exception & excpt_r )
+        {
+          ZYPP_CAUGHT( excpt_r );
+          WAR << "Failed to cache " << _lastInteractive << endl;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : CommitPackageCacheReadAhead::doCacheLastInteractive
+    // METHOD TYPE : void
+    //
+    void CommitPackageCacheReadAhead::doCacheLastInteractive( const PoolItem & citem_r )
+    {
+      CacheMap  addToCache;
+      ByteCount addSize;
+
+      // Collect all remaining packages to install from
+      // _lastInteractive media. (just the PoolItem data)
+      for_( it,_commitList.begin(), _commitList.end() )
+        {
+         PoolItem pi( *it );
+          if ( IMediaKey( pi ) == _lastInteractive
+               && isKind<Package>(pi.resolvable())
+               && pi.status().isToBeInstalled() )
+            {
+              if ( _cacheMap.find( pi ) == _cacheMap.end() )
+                {
+                  addToCache[pi];
+                  addSize += pi->downloadSize();
+                }
+            }
+        }
+
+      if ( addToCache.empty() )
+        return;
+      MIL << "could cache " << _lastInteractive << ": " << addToCache.size() << " items: " <<  addSize << endl;
+
+      // Check whether we can afford caching the items. We cache them all or
+      // nothing. It does not make sense to cache only some packages, if a
+      // CD change can't be avoided.
+      if ( ! _cacheDir )
+        {
+          _cacheDir.reset( new filesystem::TmpDir( _rootDir, "commitCache." ) );
+          PathInfo pi( _cacheDir->path() );
+          if ( ! pi.isDir() )
+            {
+              ERR << "Can not initialize cache dir " << pi << endl;
+              return;
+            }
+        }
+
+      // In case someone removes cacheDir behind our back, df will be
+      // -1, so we won't cache.
+      ByteCount df( filesystem::df( _cacheDir->path() ) );
+      MIL << "available disk space in " << _cacheDir->path() << ": " << df << endl;
+
+      if ( df / 10 < addSize )
+        {
+          WAR << "cache would require more than 10% of the available " << df << " disk space " << endl;
+          WAR << "not caching " << _lastInteractive << endl;
+          return;
+        }
+
+      // Get all files to cache from the Source and copy them to
+      // the cache.
+      // NOTE: All files copied to the cache directory are stored in addToCache,
+      // which is a local variable. If we throw on error, addToCache will be
+      // deleted and all the ManagedFiles stored so far will delete themself.
+      // THIS IS EXACTLY WHAT WE WANT.
+      for ( CacheMap::iterator it = addToCache.begin(); it != addToCache.end(); ++it )
+        {
+          // let the source provide the file
+          ManagedFile fromSource( sourceProvidePackage( it->first ) );
+
+          // copy it to the cachedir
+          std::string destName( str::form( "S%p_%u_%s",
+                                           it->first->repository().id(),
+                                           it->first->mediaNr(),
+                                           fromSource.value().basename().c_str() ) );
+
+          ManagedFile fileInCache( _cacheDir->path() / destName,
+                                   filesystem::unlink );
+
+          if ( filesystem::copy( fromSource.value(), fileInCache ) != 0 )
+            {
+              // copy to cache failed.
+              ERR << "Copy to cache failed on " << fromSource.value() << endl;
+              ZYPP_THROW( Exception("Copy to cache failed.") );
+            }
+
+          // remember the cached file.
+          it->second = fileInCache;
+        }
+
+      // Here: All files are sucessfully copied to the cache.
+      // Update the real cache map.
+      _cacheMap.insert( addToCache.begin(), addToCache.end() );
+      return;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : CommitPackageCacheReadAhead::get
+    // METHOD TYPE : ManagedFile
+    //
+    ManagedFile CommitPackageCacheReadAhead::get( const PoolItem & citem_r )
+    {
+      // Non CD/DVD media provide their packages without cache.
+      if ( ! onInteractiveMedia( citem_r ) )
+        {
+          return sourceProvidePackage( citem_r );
+        }
+
+      // Check whether it's cached.
+      CacheMap::iterator it = _cacheMap.find( citem_r );
+      if ( it != _cacheMap.end() )
+        {
+          // ManagedFile delivered to the application is removed
+          // from the cache. So if the application releases the
+          // file, it actually gets deleted from disk.
+          ManagedFile cacheHit( it->second );
+          _cacheMap.erase( it );
+
+          // safety check whether the file still exists
+          PathInfo pi( cacheHit.value() );
+          if ( pi.isFile() )
+            {
+              MIL << "Cache package provide " << cacheHit << endl;
+              return cacheHit;
+            }
+
+          WAR << "Cached file vanished: " << pi << endl;
+        }
+
+      // HERE: It's not in the cache.
+      // In case we have to change the media to provide the requested
+      // file, try to cache files from the current media, that are
+      // required later.
+      IMediaKey current( citem_r );
+      if ( current != _lastInteractive )
+        {
+          if ( _lastInteractive != IMediaKey() )
+            {
+              cacheLastInteractive( citem_r );
+            }
+
+          DBG << "Interactive change [" << ++_dbgChanges << "] from " << _lastInteractive
+          << " to " << current << endl;
+          _lastInteractive = current;
+        }
+
+      // Provide and return the file from media.
+      return sourceProvidePackage( citem_r );
+    }
+
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/CommitPackageCacheReadAhead.h b/zypp/target/CommitPackageCacheReadAhead.h
new file mode 100644 (file)
index 0000000..007951b
--- /dev/null
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/CommitPackageCacheReadAhead.h
+ *
+*/
+#ifndef ZYPP_TARGET_COMMITPACKAGECACHEREADAHEAD_H
+#define ZYPP_TARGET_COMMITPACKAGECACHEREADAHEAD_H
+
+#include <map>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/DefaultIntegral.h"
+#include "zypp/Repository.h"
+#include "zypp/TmpPath.h"
+#include "zypp/target/CommitPackageCacheImpl.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : IMediaKey
+    //
+    /** Helper storing a source id and media number. */
+    struct IMediaKey
+    {
+      IMediaKey()
+      {}
+
+      explicit
+      IMediaKey( const PoolItem & obj_r )
+      : _repo( obj_r->repository() )
+      , _mediaNr( obj_r->mediaNr() )
+      {}
+
+      explicit
+      IMediaKey( const ResObject::constPtr & obj_r )
+      : _repo( obj_r->repository() )
+      , _mediaNr( obj_r->mediaNr() )
+      {}
+
+      IMediaKey( const Repository & repo, unsigned mediaNr_r )
+      : _repo( repo )
+      , _mediaNr( mediaNr_r )
+      {}
+
+      bool operator==( const IMediaKey & rhs ) const
+      { return( _repo == rhs._repo && _mediaNr == rhs._mediaNr ); }
+
+      bool operator!=( const IMediaKey & rhs ) const
+      { return ! operator==( rhs ); }
+
+      bool operator<( const IMediaKey & rhs ) const
+      {
+        return( _repo.id() < rhs._repo.id()
+                || ( _repo.id() == rhs._repo.id()
+                     && _mediaNr < rhs._mediaNr ) );
+      }
+
+      Repository                  _repo;
+      DefaultIntegral<unsigned,0> _mediaNr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    std::ostream & operator<<( std::ostream & str, const IMediaKey & obj );
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : CommitPackageCacheReadAhead
+    //
+    /** */
+    class CommitPackageCacheReadAhead : public CommitPackageCache::Impl
+    {
+      typedef std::map<PoolItem,ManagedFile>     CacheMap;
+
+    public:
+      CommitPackageCacheReadAhead( const Pathname &        rootDir_r,
+                                   const PackageProvider & packageProvider_r );
+
+    public:
+      /** Provide the package. Either from Source or from cache. */
+      virtual ManagedFile get( const PoolItem & citem_r );
+
+    private:
+      /** Return whether \a pi is located on a CD/DVD */
+      bool onInteractiveMedia( const PoolItem & pi ) const;
+
+    private:
+      /** Fill the cache.
+       * Called before changing from one interactive media to another.
+       * Performs the read ahead of packages trying to avoid the necessity
+       * of switching back to the current media later.
+      */
+      void cacheLastInteractive( const PoolItem & citem_r );
+
+      /** cacheLastInteractive helper . */
+      void doCacheLastInteractive( const PoolItem & citem_r );
+
+    private:
+      DefaultIntegral<unsigned,0> _dbgChanges;
+
+      IMediaKey                      _lastInteractive;
+
+      Pathname                       _rootDir;
+      shared_ptr<filesystem::TmpDir> _cacheDir;
+      CacheMap                       _cacheMap;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_COMMITPACKAGECACHEREADAHEAD_H
diff --git a/zypp/target/HardLocksFile.cc b/zypp/target/HardLocksFile.cc
new file mode 100644 (file)
index 0000000..fbfd5a8
--- /dev/null
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/HardLocksFile.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Date.h"
+
+#include "zypp/target/HardLocksFile.h"
+#include "zypp/PoolQueryUtil.tcc"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    void HardLocksFile::load( const Pathname & file_r, Data & data_r )
+    {
+      PathInfo pi( file_r );
+      if ( ! pi.isFile() )
+      {
+        WAR << "Can't read " << pi << endl;
+        return;
+      }
+
+      readPoolQueriesFromFile( file_r, std::back_inserter( data_r ) );
+
+      MIL << "Read " << pi << endl;
+    }
+
+    void HardLocksFile::store( const Pathname & file_r, const Data & data_r )
+    {
+      filesystem::TmpFile tmp( filesystem::TmpFile::makeSibling( file_r ) );
+      filesystem::chmod( tmp.path(), 0644 );
+
+      writePoolQueriesToFile( tmp.path(), data_r.begin(), data_r.end() );
+
+      if ( true ) // by now: no error info from writePoolQueriesToFile
+      {
+        filesystem::rename( tmp.path(), file_r );
+        MIL << "Wrote " << PathInfo(file_r) << endl;
+      }
+      else
+      {
+        ERR << "Can't write " << PathInfo(tmp.path()) << endl;
+      }
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const HardLocksFile & obj )
+    {
+      str << obj.file() << ' ';
+      if ( obj._dataPtr )
+        str << obj.data();
+      else
+        str << "(unloaded)";
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/HardLocksFile.h b/zypp/target/HardLocksFile.h
new file mode 100644 (file)
index 0000000..70f9e9e
--- /dev/null
@@ -0,0 +1,122 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/HardLocksFile.h
+ *
+*/
+#ifndef ZYPP_TARGET_HARDLOCKSFILE_H
+#define ZYPP_TARGET_HARDLOCKSFILE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/pool/PoolTraits.h"
+#include "zypp/PoolQuery.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : HardLocksFile
+    //
+    /** Save and restore hardlocks.
+     */
+    class HardLocksFile
+    {
+      friend std::ostream & operator<<( std::ostream & str, const HardLocksFile & obj );
+      public:
+
+        typedef pool::PoolTraits::HardLockQueries Data;
+
+      public:
+        /** Ctor taking the file to read/write. */
+        HardLocksFile( const Pathname & file_r )
+        : _file( file_r )
+        {}
+
+        /** Return the file path. */
+        const Pathname & file() const
+        { return _file; }
+
+        /** Return the data.
+         * The file is read once on demand. Returns empty \ref Data if
+         * the file does not exist or is not readable.
+        */
+        const Data & data() const
+        {
+          if ( !_dataPtr )
+          {
+            _dataPtr.reset( new Data );
+            Data & mydata( *_dataPtr );
+            load( _file, mydata );
+          }
+          return *_dataPtr;
+        }
+
+        /** Store new \ref Data.
+         * Write the new \ref Data to file, unless we know it
+         * did not change. The directory containing file must
+         * exist.
+        */
+        void setData( const Data & data_r )
+        {
+          if ( !_dataPtr )
+            _dataPtr.reset( new Data );
+
+          if ( differs( *_dataPtr, data_r ) )
+          {
+            store( _file, data_r );
+            *_dataPtr = data_r;
+          }
+        }
+
+      private:
+        /** Helper testing whether two \ref Data differ. */
+        bool differs( const Data & lhs, const Data & rhs ) const
+        {
+          if ( lhs.size() != rhs.size() )
+            return true;
+          // Complete diff is too expensive and not necessary here.
+          // Just check for the same sequence of items.
+          Data::const_iterator rit = rhs.begin();
+          for_( it, lhs.begin(), lhs.end() )
+          {
+            if ( *it != *rit )
+              return true;
+            ++rit;
+          }
+          return false;
+        }
+        /** Read \ref Data from \c file_r. */
+        static void load( const Pathname & file_r, Data & data_r );
+        /** Write \ref Data to \c file_r. */
+        static void store( const Pathname & file_r, const Data & data_r );
+
+      private:
+        Pathname                 _file;
+        mutable scoped_ptr<Data> _dataPtr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates HardLocksFile Stream output */
+    std::ostream & operator<<( std::ostream & str, const HardLocksFile & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_HARDLOCKSFILE_H
diff --git a/zypp/target/RequestedLocalesFile.cc b/zypp/target/RequestedLocalesFile.cc
new file mode 100644 (file)
index 0000000..3c27e5a
--- /dev/null
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/RequestedLocalesFile.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Date.h"
+
+#include "zypp/target/RequestedLocalesFile.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    void RequestedLocalesFile::load( const Pathname & file_r, LocaleSet & locales_r )
+    {
+      PathInfo pi( file_r );
+      if ( ! pi.isFile() )
+      {
+        WAR << "Can't read " << pi << endl;
+        return;
+      }
+      std::ifstream infile( file_r.c_str() );
+      for( iostr::EachLine in( infile ); in; in.next() )
+      {
+        std::string l( str::trim(*in) );
+        if ( ! l.empty() && l[0] != '#' )
+        {
+          locales_r.insert( Locale(l) );
+        }
+      }
+      MIL << "Read " << pi << endl;
+    }
+
+    void RequestedLocalesFile::store( const Pathname & file_r, const LocaleSet & locales_r )
+    {
+      filesystem::TmpFile tmp( filesystem::TmpFile::makeSibling( file_r ) );
+      filesystem::chmod( tmp.path(), 0644 );
+
+      std::ofstream outs( tmp.path().c_str() );
+      outs << "# zypp::RequestedLocales generated " << Date::now() << endl;
+      dumpRange( outs, locales_r.begin(), locales_r.end(), "#", "\n", "\n", "\n", "#\n" );
+      outs.close();
+
+      if ( outs.good() )
+      {
+        filesystem::rename( tmp.path(), file_r );
+        MIL << "Wrote " << PathInfo(file_r) << endl;
+      }
+      else
+      {
+        ERR << "Can't write " << PathInfo(tmp.path()) << endl;
+      }
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const RequestedLocalesFile & obj )
+    {
+      str << obj.file() << ' ';
+      if ( obj._localesPtr )
+        str << obj.locales();
+      else
+        str << "(unloaded)";
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/RequestedLocalesFile.h b/zypp/target/RequestedLocalesFile.h
new file mode 100644 (file)
index 0000000..bbefe95
--- /dev/null
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/RequestedLocalesFile.h
+ *
+*/
+#ifndef ZYPP_TARGET_REQUESTEDLOCALESFILE_H
+#define ZYPP_TARGET_REQUESTEDLOCALESFILE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/Locale.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : RequestedLocalesFile
+    //
+    /** Save and restore locale set from file.
+     */
+    class RequestedLocalesFile
+    {
+      friend std::ostream & operator<<( std::ostream & str, const RequestedLocalesFile & obj );
+
+      public:
+        /** Ctor taking the file to read/write. */
+        RequestedLocalesFile( const Pathname & file_r )
+        : _file( file_r )
+        {}
+
+        /** Return the file path. */
+        const Pathname & file() const
+        { return _file; }
+
+        /** Return the loacale set.
+         * The file is read once on demand. returns an empty set if
+         * the file does not exist or is not readable.
+        */
+        const LocaleSet & locales() const
+        {
+          if ( !_localesPtr )
+          {
+            _localesPtr.reset( new LocaleSet );
+            LocaleSet & ls( *_localesPtr );
+            load( _file, ls );
+          }
+          return *_localesPtr;
+        }
+
+        /** Store a new locale set.
+         * Write the new localeset to file, unless we know it
+         * did not change. The directory containing file must exist.
+        */
+        void setLocales( const LocaleSet & locales_r )
+        {
+          if ( !_localesPtr )
+            _localesPtr.reset( new LocaleSet );
+
+          if ( differs( *_localesPtr, locales_r ) )
+          {
+            store( _file, locales_r );
+            *_localesPtr = locales_r;
+          }
+        }
+
+      private:
+        /** Helper testing whether two \ref LocaleSet differ. */
+        bool differs( const LocaleSet & lhs, const LocaleSet & rhs ) const
+        {
+          if ( lhs.size() != rhs.size() )
+            return true;
+          for_( it, lhs.begin(), lhs.end() )
+          {
+            if ( rhs.find( *it ) == rhs.end() )
+              return true;
+          }
+          return false;
+        }
+        /** Read \ref LocaleSet from \c file_r. */
+        static void load( const Pathname & file_r, LocaleSet & locales_r );
+        /** Write \ref LocaleSet to \c file_r. */
+        static void store( const Pathname & file_r, const LocaleSet & locales_r );
+
+      private:
+        Pathname                      _file;
+        mutable scoped_ptr<LocaleSet> _localesPtr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates RequestedLocalesFile Stream output */
+    std::ostream & operator<<( std::ostream & str, const RequestedLocalesFile & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_REQUESTEDLOCALESFILE_H
diff --git a/zypp/target/SoftLocksFile.cc b/zypp/target/SoftLocksFile.cc
new file mode 100644 (file)
index 0000000..4e117d2
--- /dev/null
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/SoftLocksFile.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/String.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/TmpPath.h"
+#include "zypp/Date.h"
+
+#include "zypp/target/SoftLocksFile.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    void SoftLocksFile::load( const Pathname & file_r, Data & data_r )
+    {
+      PathInfo pi( file_r );
+      if ( ! pi.isFile() )
+      {
+        WAR << "Can't read " << pi << endl;
+        return;
+      }
+      std::ifstream infile( file_r.c_str() );
+      for( iostr::EachLine in( infile ); in; in.next() )
+      {
+        std::string l( str::trim(*in) );
+        if ( ! l.empty() && l[0] != '#' )
+        {
+          data_r.insert( IdString(l) );
+        }
+      }
+      MIL << "Read " << pi << endl;
+    }
+
+    void SoftLocksFile::store( const Pathname & file_r, const Data & data_r )
+    {
+      filesystem::TmpFile tmp( filesystem::TmpFile::makeSibling( file_r ) );
+      filesystem::chmod( tmp.path(), 0644 );
+
+      std::ofstream outs( tmp.path().c_str() );
+      outs << "# zypp::SoftLocksFile generated " << Date::now() << endl;
+      dumpRange( outs, data_r.begin(), data_r.end(), "#", "\n", "\n", "\n", "#\n" );
+      outs.close();
+
+      if ( outs.good() )
+      {
+        filesystem::rename( tmp.path(), file_r );
+        MIL << "Wrote " << PathInfo(file_r) << endl;
+      }
+      else
+      {
+        ERR << "Can't write " << PathInfo(tmp.path()) << endl;
+      }
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const SoftLocksFile & obj )
+    {
+      str << obj.file() << ' ';
+      if ( obj._dataPtr )
+        str << obj.data();
+      else
+        str << "(unloaded)";
+      return str;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/SoftLocksFile.h b/zypp/target/SoftLocksFile.h
new file mode 100644 (file)
index 0000000..bd91d6a
--- /dev/null
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/SoftLocksFile.h
+ *
+*/
+#ifndef ZYPP_TARGET_SOFTLOCKSFILE_H
+#define ZYPP_TARGET_SOFTLOCKSFILE_H
+
+#include <iosfwd>
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/IdString.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SoftLocksFile
+    //
+    /** Save and restore soft locks.
+     */
+    class SoftLocksFile
+    {
+      friend std::ostream & operator<<( std::ostream & str, const SoftLocksFile & obj );
+      public:
+        typedef std::tr1::unordered_set<IdString> Data;
+
+      public:
+        /** Ctor taking the file to read/write. */
+        SoftLocksFile( const Pathname & file_r )
+        : _file( file_r )
+        {}
+
+        /** Return the file path. */
+        const Pathname & file() const
+        { return _file; }
+
+        /** Return the data.
+         * The file is read once on demand. Returns empty \ref Data if
+         * the file does not exist or is not readable.
+        */
+        const Data & data() const
+        {
+          if ( !_dataPtr )
+          {
+            _dataPtr.reset( new Data );
+            Data & mydata( *_dataPtr );
+            load( _file, mydata );
+          }
+          return *_dataPtr;
+        }
+
+        /** Store new \ref Data.
+         * Write the new \ref Data to file, unless we know it
+         * did not change. The directory containing file must
+         * exist.
+        */
+        void setData( const Data & data_r )
+        {
+          if ( !_dataPtr )
+            _dataPtr.reset( new Data );
+
+          if ( differs( *_dataPtr, data_r ) )
+          {
+            store( _file, data_r );
+            *_dataPtr = data_r;
+          }
+        }
+
+      private:
+        /** Helper testing whether two \ref Data differ. */
+        bool differs( const Data & lhs, const Data & rhs ) const
+        {
+
+          if ( lhs.size() != rhs.size() )
+            return true;
+          for_( it, lhs.begin(), lhs.end() )
+          {
+            if ( rhs.find( *it ) == rhs.end() )
+              return true;
+          }
+          return false;
+        }
+        /** Read \ref Data from \c file_r. */
+        static void load( const Pathname & file_r, Data & data_r );
+        /** Write \ref Data to \c file_r. */
+        static void store( const Pathname & file_r, const Data & data_r );
+
+      private:
+        Pathname                 _file;
+        mutable scoped_ptr<Data> _dataPtr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates SoftLocksFile Stream output */
+    std::ostream & operator<<( std::ostream & str, const SoftLocksFile & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_SOFTLOCKSFILE_H
diff --git a/zypp/target/TargetCallbackReceiver.cc b/zypp/target/TargetCallbackReceiver.cc
new file mode 100644 (file)
index 0000000..0d02f92
--- /dev/null
@@ -0,0 +1,193 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/TargetCallbackReceiver.cc
+ *
+*/
+
+#include "zypp/target/TargetCallbackReceiver.h"
+
+#include "zypp/target/rpm/RpmCallbacks.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+       RpmInstallPackageReceiver::RpmInstallPackageReceiver (Resolvable::constPtr res)
+           : callback::ReceiveReport<rpm::RpmInstallReport> ()
+           , _resolvable (res)
+           , _level( target::rpm::InstallResolvableReport::RPM )
+           , _abort (false)
+       {
+       }
+
+       RpmInstallPackageReceiver::~RpmInstallPackageReceiver ()
+       {
+       }
+
+       void RpmInstallPackageReceiver::reportbegin()
+       {
+       }
+
+       void RpmInstallPackageReceiver::reportend()
+       {
+       }
+
+        /** Start the operation */
+        void RpmInstallPackageReceiver::start( const Pathname & name )
+       {
+           _report->start( _resolvable );
+           _abort = false;
+       }
+
+        /**
+         * Inform about progress
+         * Return true on abort
+         */
+        bool RpmInstallPackageReceiver::progress( unsigned percent )
+       {
+           _abort = ! _report->progress( percent, _resolvable );
+           return _abort;
+       }
+
+       rpm::RpmInstallReport::Action
+       RpmInstallPackageReceiver::problem( Exception & excpt_r )
+       {
+           rpm::InstallResolvableReport::Action user =
+               _report->problem( _resolvable
+                   , rpm::InstallResolvableReport::INVALID
+                   , excpt_r.asUserHistory()
+                   , _level
+               );
+
+           switch (user) {
+               case rpm::InstallResolvableReport::RETRY:
+                   return rpm::RpmInstallReport::RETRY;
+               case rpm::InstallResolvableReport::ABORT:
+                    _abort = true;
+                   return rpm::RpmInstallReport::ABORT;
+               case rpm::InstallResolvableReport::IGNORE:
+                   return rpm::RpmInstallReport::IGNORE;
+           }
+
+           return rpm::RpmInstallReport::problem( excpt_r );
+       }
+
+        void RpmInstallPackageReceiver::finishInfo( const std::string & info_r )
+        {
+          _finishInfo = info_r;
+        }
+
+        /** Finish operation in case of success */
+        void RpmInstallPackageReceiver::finish()
+       {
+           _report->finish( _resolvable, rpm::InstallResolvableReport::NO_ERROR, _finishInfo, _level );
+       }
+
+        /** Finish operation in case of success */
+        void RpmInstallPackageReceiver::finish( Exception & excpt_r )
+       {
+           _report->finish( _resolvable, rpm::InstallResolvableReport::INVALID, std::string(), _level );
+       }
+
+       void RpmInstallPackageReceiver::tryLevel( target::rpm::InstallResolvableReport::RpmLevel level_r )
+       {
+           _level = level_r;
+       }
+
+
+       /////////////////////////////////////////////////////////////////
+       ///  RpmRemovePackageReceiver
+       /////////////////////////////////////////////////////////////////
+
+       RpmRemovePackageReceiver::RpmRemovePackageReceiver (Resolvable::constPtr res)
+           : callback::ReceiveReport<rpm::RpmRemoveReport> ()
+           , _resolvable (res)
+            , _abort(false)
+       {
+       }
+
+       RpmRemovePackageReceiver::~RpmRemovePackageReceiver ()
+       {
+       }
+
+       void RpmRemovePackageReceiver::reportbegin()
+       {
+       }
+
+       void RpmRemovePackageReceiver::reportend()
+       {
+       }
+
+        /** Start the operation */
+        void RpmRemovePackageReceiver::start( const std::string & name )
+       {
+           _report->start( _resolvable );
+            _abort = false;
+       }
+
+        /**
+         * Inform about progress
+         * Return true on abort
+         */
+        bool RpmRemovePackageReceiver::progress( unsigned percent )
+       {
+           _abort = ! _report->progress( percent, _resolvable );
+           return _abort;
+       }
+
+       rpm::RpmRemoveReport::Action
+       RpmRemovePackageReceiver::problem( Exception & excpt_r )
+       {
+           rpm::RemoveResolvableReport::Action user =
+               _report->problem( _resolvable
+                   , rpm::RemoveResolvableReport::INVALID
+                   , excpt_r.asUserHistory()
+               );
+
+           switch (user) {
+               case rpm::RemoveResolvableReport::RETRY:
+                   return rpm::RpmRemoveReport::RETRY;
+               case rpm::RemoveResolvableReport::ABORT:
+                    _abort = true;
+                   return rpm::RpmRemoveReport::ABORT;
+               case rpm::RemoveResolvableReport::IGNORE:
+                   return rpm::RpmRemoveReport::IGNORE;
+           }
+
+           return rpm::RpmRemoveReport::problem( excpt_r );
+       }
+
+        void RpmRemovePackageReceiver::finishInfo( const std::string & info_r )
+        {
+          _finishInfo = info_r;
+        }
+
+        /** Finish operation in case of success */
+        void RpmRemovePackageReceiver::finish()
+       {
+            _report->progress( 100, _resolvable );
+           _report->finish( _resolvable, rpm::RemoveResolvableReport::NO_ERROR, _finishInfo );
+       }
+
+        /** Finish operation in case of success */
+        void RpmRemovePackageReceiver::finish( Exception & excpt_r )
+       {
+           _report->finish( _resolvable, rpm::RemoveResolvableReport::INVALID, std::string() );
+       }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/TargetCallbackReceiver.h b/zypp/target/TargetCallbackReceiver.h
new file mode 100644 (file)
index 0000000..8b435bc
--- /dev/null
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/TargetCallbackReceiver.h
+ *
+*/
+#ifndef ZYPP_TARGET_TARGETCALLBACKRECEIVER_H
+#define ZYPP_TARGET_TARGETCALLBACKRECEIVER_H
+
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/target/rpm/RpmCallbacks.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    class RpmInstallPackageReceiver
+       : public callback::ReceiveReport<rpm::RpmInstallReport>
+    {
+       callback::SendReport <rpm::InstallResolvableReport> _report;
+       Resolvable::constPtr _resolvable;
+       target::rpm::InstallResolvableReport::RpmLevel _level;
+       bool _abort;
+        std::string _finishInfo;
+
+      public:
+
+       RpmInstallPackageReceiver (Resolvable::constPtr res);
+       virtual ~RpmInstallPackageReceiver ();
+
+       virtual void reportbegin();
+
+       virtual void reportend();
+
+        /** Start the operation */
+        virtual void start( const Pathname & name );
+
+       void tryLevel( target::rpm::InstallResolvableReport::RpmLevel level_r );
+
+       bool aborted() const { return _abort; }
+
+        /**
+         * Inform about progress
+         * Return true on abort
+         */
+        virtual bool progress( unsigned percent );
+
+       /** inform user about a problem */
+       virtual rpm::RpmInstallReport::Action problem( Exception & excpt_r );
+
+        /** Additional rpm output to be reported in \ref finish in case of success. */
+        virtual void finishInfo( const std::string & info_r );
+
+        /** Finish operation in case of success */
+        virtual void finish();
+
+        /** Finish operatin in case of fail, report fail exception */
+        virtual void finish( Exception & excpt_r );
+    };
+
+    class RpmRemovePackageReceiver
+       : public callback::ReceiveReport<rpm::RpmRemoveReport>
+    {
+       callback::SendReport <rpm::RemoveResolvableReport> _report;
+       Resolvable::constPtr _resolvable;
+       bool _abort;
+        std::string _finishInfo;
+
+      public:
+
+       RpmRemovePackageReceiver (Resolvable::constPtr res);
+       virtual ~RpmRemovePackageReceiver ();
+
+       virtual void reportbegin();
+
+       virtual void reportend();
+
+        /** Start the operation */
+        virtual void start( const std::string & name );
+
+        /**
+         * Inform about progress
+         * Return true on abort
+         */
+        virtual bool progress( unsigned percent );
+
+        /**
+         *  Returns true if removing is aborted during progress
+         */
+       bool aborted() const { return _abort; }
+
+       /** inform user about a problem */
+       virtual rpm::RpmRemoveReport::Action problem( Exception & excpt_r );
+
+        /** Additional rpm output to be reported in \ref finish in case of success. */
+        virtual void finishInfo( const std::string & info_r );
+
+        /** Finish operation in case of success */
+        virtual void finish();
+
+        /** Finish operatin in case of fail, report fail exception */
+        virtual void finish( Exception & excpt_r );
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_TARGETCALLBACKRECEIVER_H
diff --git a/zypp/target/TargetException.cc b/zypp/target/TargetException.cc
new file mode 100644 (file)
index 0000000..341da74
--- /dev/null
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/TargetException.cc
+ *
+*/
+
+#include <string>
+#include <iostream>
+
+#include "zypp/target/TargetException.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace target {
+  /////////////////////////////////////////////////////////////////
+
+    std::ostream & TargetAbortedException::dumpOn( std::ostream & str ) const
+    {
+      return str << "Installation aborted by user" << endl;
+    }
+
+
+  /////////////////////////////////////////////////////////////////
+  } // namespace target
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/TargetException.h b/zypp/target/TargetException.h
new file mode 100644 (file)
index 0000000..39ec002
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/TargetException.h
+ *
+*/
+#ifndef ZYPP_TARGET_TARGETEXCEPTION_H
+#define ZYPP_TARGET_TARGETEXCEPTION_H
+
+#include <iosfwd>
+
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  namespace target {
+    ///////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TargetException
+    /** Just inherits Exception to separate target exceptions
+     *
+     **/
+    class TargetException : public Exception
+    {
+    public:
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      TargetException()
+      : Exception( "Target Exception" )
+      {}
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      TargetException( const std::string & msg_r )
+      : Exception( msg_r )
+      {}
+      /** Dtor. */
+      virtual ~TargetException() throw() {};
+    };
+
+    class TargetAbortedException : public TargetException
+    {
+    public:
+      /** Ctor taking message.
+       * Use \ref ZYPP_THROW to throw exceptions.
+      */
+      TargetAbortedException( const std::string & msg_r )
+      : TargetException( msg_r )
+      {}
+      /** Dtor. */
+      virtual ~TargetAbortedException() throw() {};
+    protected:
+      virtual std::ostream & dumpOn( std::ostream & str ) const;
+    private:
+    };
+
+
+  /////////////////////////////////////////////////////////////////
+  } // namespace target
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_TARGETEXCEPTION_H
diff --git a/zypp/target/TargetImpl.cc b/zypp/target/TargetImpl.cc
new file mode 100644 (file)
index 0000000..b286f20
--- /dev/null
@@ -0,0 +1,1788 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/TargetImpl.cc
+ *
+*/
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <list>
+#include <set>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Exception.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/IOStream.h"
+#include "zypp/base/Functional.h"
+#include "zypp/base/UserRequestException.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/ZYppFactory.h"
+
+#include "zypp/PoolItem.h"
+#include "zypp/ResObjects.h"
+#include "zypp/Url.h"
+#include "zypp/TmpPath.h"
+#include "zypp/RepoStatus.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/Repository.h"
+
+#include "zypp/ResFilters.h"
+#include "zypp/HistoryLog.h"
+#include "zypp/target/TargetImpl.h"
+#include "zypp/target/TargetCallbackReceiver.h"
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/CommitPackageCache.h"
+
+#include "zypp/parser/ProductFileReader.h"
+
+#include "zypp/pool/GetResolvablesToInsDel.h"
+#include "zypp/solver/detail/Testcase.h"
+
+#include "zypp/repo/DeltaCandidates.h"
+#include "zypp/repo/PackageProvider.h"
+#include "zypp/repo/SrcPackageProvider.h"
+
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/Transaction.h"
+
+#include "zypp/PluginScript.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    /** Helper for commit plugin execution.
+     * \ingroup g_RAII
+     */
+    class CommitPlugins : private base::NonCopyable
+    {
+      public:
+
+      public:
+       /** Default ctor: Empty plugin list */
+       CommitPlugins()
+       {}
+
+       /** Dtor: Send PLUGINEND message and close plugins. */
+       ~CommitPlugins()
+       {
+         for_( it, _scripts.begin(), _scripts.end() )
+         {
+           MIL << "Unload plugin: " << *it << endl;
+           try {
+             it->send( PluginFrame( "PLUGINEND" ) );
+             PluginFrame ret( it->receive() );
+             if ( ! ret.isAckCommand() )
+             {
+               WAR << "Failed to unload plugin: Bad plugin response." << endl;
+             }
+             it->close();
+           }
+           catch( const zypp::Exception &  )
+           {
+             WAR << "Failed to unload plugin." << endl;
+           }
+         }
+         // _scripts dtor will disconnect all remaining plugins!
+       }
+
+       /** Find and launch plugins sending PLUGINSTART message.
+        *
+        * If \a path_r is a directory all executable files whithin are
+        * expected to be plugins. Otherwise \a path_r must point to an
+        * executable plugin.
+        */
+       void load( const Pathname & path_r )
+       {
+         PathInfo pi( path_r );
+         if ( pi.isDir() )
+         {
+           std::list<Pathname> entries;
+           if ( filesystem::readdir( entries, pi.path(), false ) != 0 )
+           {
+             WAR << "Plugin dir is not readable: " << pi << endl;
+             return;
+           }
+           for_( it, entries.begin(), entries.end() )
+           {
+             PathInfo pii( *it );
+             if ( pii.isFile() && pii.userMayRX() )
+               doLoad( pii );
+           }
+         }
+         else if ( pi.isFile() )
+         {
+           if ( pi.userMayRX() )
+             doLoad( pi );
+           else
+             WAR << "Plugin file is not executable: " << pi << endl;
+         }
+         else
+         {
+           WAR << "Plugin path is neither dir nor file: " << pi << endl;
+         }
+       }
+
+      private:
+       void doLoad( const PathInfo & pi_r )
+       {
+         MIL << "Load plugin: " << pi_r << endl;
+         try {
+           PluginScript plugin( pi_r.path() );
+           plugin.open();
+           plugin.send( PluginFrame( "PLUGINBEGIN" ) );
+           PluginFrame ret( plugin.receive() );
+           if ( ret.isAckCommand() )
+           {
+             _scripts.push_back( plugin );
+           }
+           else
+           {
+             WAR << "Failed to load plugin: Bad plugin response." << endl;
+           }
+         }
+         catch( const zypp::Exception &  )
+         {
+            WAR << "Failed to load plugin." << endl;
+         }
+       }
+
+      private:
+       std::list<PluginScript> _scripts;
+    };
+
+    void testCommitPlugins( const Pathname & path_r ) // for testing only
+    {
+      USR << "+++++" << endl;
+      {
+       CommitPlugins pl;
+       pl.load( path_r );
+       USR << "=====" << endl;
+      }
+      USR << "-----" << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \internal Manage writing a new testcase when doing an upgrade. */
+    void writeUpgradeTestcase()
+    {
+      unsigned toKeep( ZConfig::instance().solver_upgradeTestcasesToKeep() );
+      MIL << "Testcases to keep: " << toKeep << endl;
+      if ( !toKeep )
+        return;
+      Target_Ptr target( getZYpp()->getTarget() );
+      if ( ! target )
+      {
+        WAR << "No Target no Testcase!" << endl;
+        return;
+      }
+
+      std::string stem( "updateTestcase" );
+      Pathname dir( target->assertRootPrefix("/var/log/") );
+      Pathname next( dir / Date::now().form( stem+"-%Y-%m-%d-%H-%M-%S" ) );
+
+      {
+        std::list<std::string> content;
+        filesystem::readdir( content, dir, /*dots*/false );
+        std::set<std::string> cases;
+        for_( c, content.begin(), content.end() )
+        {
+          if ( str::startsWith( *c, stem ) )
+            cases.insert( *c );
+        }
+        if ( cases.size() >= toKeep )
+        {
+          unsigned toDel = cases.size() - toKeep + 1; // +1 for the new one
+          for_( c, cases.begin(), cases.end() )
+          {
+            filesystem::recursive_rmdir( dir/(*c) );
+            if ( ! --toDel )
+              break;
+          }
+        }
+      }
+
+      MIL << "Write new testcase " << next << endl;
+      getZYpp()->resolver()->createSolverTestcase( next.asString(), false/*no solving*/ );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+      /** Execute script and report against report_r.
+       * Return \c std::pair<bool,PatchScriptReport::Action> to indicate if
+       * execution was successfull (<tt>first = true</tt>), or the desired
+       * \c PatchScriptReport::Action in case execution failed
+       * (<tt>first = false</tt>).
+       *
+       * \note The packager is responsible for setting the correct permissions
+       * of the script. If the script is not executable it is reported as an
+       * error. We must not modify the permessions.
+       */
+      std::pair<bool,PatchScriptReport::Action> doExecuteScript( const Pathname & root_r,
+                                                                 const Pathname & script_r,
+                                                                 callback::SendReport<PatchScriptReport> & report_r )
+      {
+        MIL << "Execute script " << PathInfo(Pathname::assertprefix( root_r,script_r)) << endl;
+
+        HistoryLog historylog;
+        historylog.comment(script_r.asString() + _(" executed"), /*timestamp*/true);
+        ExternalProgram prog( script_r.asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
+
+        for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() )
+        {
+          historylog.comment(output);
+          if ( ! report_r->progress( PatchScriptReport::OUTPUT, output ) )
+          {
+            WAR << "User request to abort script " << script_r << endl;
+            prog.kill();
+            // the rest is handled by exit code evaluation
+            // in case the script has meanwhile finished.
+          }
+        }
+
+        std::pair<bool,PatchScriptReport::Action> ret( std::make_pair( false, PatchScriptReport::ABORT ) );
+
+        if ( prog.close() != 0 )
+        {
+          ret.second = report_r->problem( prog.execError() );
+          WAR << "ACTION" << ret.second << "(" << prog.execError() << ")" << endl;
+          std::ostringstream sstr;
+          sstr << script_r << _(" execution failed") << " (" << prog.execError() << ")" << endl;
+          historylog.comment(sstr.str(), /*timestamp*/true);
+          return ret;
+        }
+
+        report_r->finish();
+        ret.first = true;
+        return ret;
+      }
+
+      /** Execute script and report against report_r.
+       * Return \c false if user requested \c ABORT.
+       */
+      bool executeScript( const Pathname & root_r,
+                          const Pathname & script_r,
+                          callback::SendReport<PatchScriptReport> & report_r )
+      {
+        std::pair<bool,PatchScriptReport::Action> action( std::make_pair( false, PatchScriptReport::ABORT ) );
+
+        do {
+          action = doExecuteScript( root_r, script_r, report_r );
+          if ( action.first )
+            return true; // success
+
+          switch ( action.second )
+          {
+            case PatchScriptReport::ABORT:
+              WAR << "User request to abort at script " << script_r << endl;
+              return false; // requested abort.
+              break;
+
+            case PatchScriptReport::IGNORE:
+              WAR << "User request to skip script " << script_r << endl;
+              return true; // requested skip.
+              break;
+
+            case PatchScriptReport::RETRY:
+              break; // again
+          }
+        } while ( action.second == PatchScriptReport::RETRY );
+
+        // THIS is not intended to be reached:
+        INT << "Abort on unknown ACTION request " << action.second << " returned" << endl;
+        return false; // abort.
+      }
+
+      /** Look for update scripts named 'name-version-release-*' and
+       *  execute them. Return \c false if \c ABORT was requested.
+       *
+       * \see http://en.opensuse.org/Software_Management/Code11/Scripts_and_Messages
+       */
+      bool RunUpdateScripts( const Pathname & root_r,
+                             const Pathname & scriptsPath_r,
+                             const std::vector<sat::Solvable> & checkPackages_r,
+                             bool aborting_r )
+      {
+        if ( checkPackages_r.empty() )
+          return true; // no installed packages to check
+
+        MIL << "Looking for new update scripts in (" <<  root_r << ")" << scriptsPath_r << endl;
+        Pathname scriptsDir( Pathname::assertprefix( root_r, scriptsPath_r ) );
+        if ( ! PathInfo( scriptsDir ).isDir() )
+          return true; // no script dir
+
+        std::list<std::string> scripts;
+        filesystem::readdir( scripts, scriptsDir, /*dots*/false );
+        if ( scripts.empty() )
+          return true; // no scripts in script dir
+
+        // Now collect and execute all matching scripts.
+        // On ABORT: at least log all outstanding scripts.
+        // - "name-version-release"
+        // - "name-version-release-*"
+        bool abort = false;
+        for_( it, checkPackages_r.begin(), checkPackages_r.end() )
+        {
+          std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
+          for_( sit, scripts.begin(), scripts.end() )
+          {
+            if ( ! str::hasPrefix( *sit, prefix ) )
+              continue;
+
+            if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
+              continue; // if not exact match it had to continue with '-'
+
+            PathInfo script( scriptsDir / *sit );
+            if ( ! script.isFile() )
+              continue;
+
+            // Assert it's set executable
+            filesystem::addmod( script.path(), 0500 );
+
+            Pathname localPath( scriptsPath_r/(*sit) ); // without root prefix
+            if ( abort || aborting_r )
+            {
+              WAR << "Aborting: Skip update script " << *sit << endl;
+              HistoryLog().comment(
+                  localPath.asString() + _(" execution skipped while aborting"),
+                  /*timestamp*/true);
+            }
+            else
+            {
+              MIL << "Found update script " << *sit << endl;
+              callback::SendReport<PatchScriptReport> report;
+              report->start( make<Package>( *it ), script.path() );
+
+              if ( ! executeScript( root_r, localPath, report ) ) // script path without root prefix!
+                abort = true; // requested abort.
+            }
+          }
+        }
+        return !abort;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      ///////////////////////////////////////////////////////////////////
+
+      inline void copyTo( std::ostream & out_r, const Pathname & file_r )
+      {
+        std::ifstream infile( file_r.c_str() );
+        for( iostr::EachLine in( infile ); in; in.next() )
+        {
+          out_r << *in << endl;
+        }
+      }
+
+      inline std::string notificationCmdSubst( const std::string & cmd_r, const UpdateNotificationFile & notification_r )
+      {
+        std::string ret( cmd_r );
+#define SUBST_IF(PAT,VAL) if ( ret.find( PAT ) != std::string::npos ) ret = str::gsub( ret, PAT, VAL )
+        SUBST_IF( "%p", notification_r.solvable().asString() );
+        SUBST_IF( "%P", notification_r.file().asString() );
+#undef SUBST_IF
+        return ret;
+      }
+
+      void sendNotification( const Pathname & root_r,
+                             const UpdateNotifications & notifications_r )
+      {
+        if ( notifications_r.empty() )
+          return;
+
+        std::string cmdspec( ZConfig::instance().updateMessagesNotify() );
+        MIL << "Notification command is '" << cmdspec << "'" << endl;
+        if ( cmdspec.empty() )
+          return;
+
+        std::string::size_type pos( cmdspec.find( '|' ) );
+        if ( pos == std::string::npos )
+        {
+          ERR << "Can't send Notification: Missing 'format |' in command spec." << endl;
+          HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
+          return;
+        }
+
+        std::string formatStr( str::toLower( str::trim( cmdspec.substr( 0, pos ) ) ) );
+        std::string commandStr( str::trim( cmdspec.substr( pos + 1 ) ) );
+
+        enum Format { UNKNOWN, NONE, SINGLE, DIGEST, BULK };
+        Format format = UNKNOWN;
+        if ( formatStr == "none" )
+          format = NONE;
+        else if ( formatStr == "single" )
+          format = SINGLE;
+        else if ( formatStr == "digest" )
+          format = DIGEST;
+        else if ( formatStr == "bulk" )
+          format = BULK;
+        else
+        {
+          ERR << "Can't send Notification: Unknown format '" << formatStr << " |' in command spec." << endl;
+          HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
+         return;
+        }
+
+        // Take care: commands are ececuted chroot(root_r). The message file
+        // pathnames in notifications_r are local to root_r. For physical access
+        // to the file they need to be prefixed.
+
+        if ( format == NONE || format == SINGLE )
+        {
+          for_( it, notifications_r.begin(), notifications_r.end() )
+          {
+            std::vector<std::string> command;
+            if ( format == SINGLE )
+              command.push_back( "<"+Pathname::assertprefix( root_r, it->file() ).asString() );
+            str::splitEscaped( notificationCmdSubst( commandStr, *it ), std::back_inserter( command ) );
+
+            ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
+            if ( true ) // Wait for feedback
+            {
+              for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+              {
+                DBG << line;
+              }
+              int ret = prog.close();
+              if ( ret != 0 )
+              {
+                ERR << "Notification command returned with error (" << ret << ")." << endl;
+                HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
+                return;
+              }
+            }
+          }
+        }
+        else if ( format == DIGEST || format == BULK )
+        {
+          filesystem::TmpFile tmpfile;
+          ofstream out( tmpfile.path().c_str() );
+          for_( it, notifications_r.begin(), notifications_r.end() )
+          {
+            if ( format == DIGEST )
+            {
+              out << it->file() << endl;
+            }
+            else if ( format == BULK )
+            {
+              copyTo( out << '\f', Pathname::assertprefix( root_r, it->file() ) );
+            }
+          }
+
+          std::vector<std::string> command;
+          command.push_back( "<"+tmpfile.path().asString() ); // redirect input
+          str::splitEscaped( notificationCmdSubst( commandStr, *notifications_r.begin() ), std::back_inserter( command ) );
+
+          ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
+          if ( true ) // Wait for feedback otherwise the TmpFile goes out of scope.
+          {
+            for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
+            {
+              DBG << line;
+            }
+            int ret = prog.close();
+            if ( ret != 0 )
+            {
+              ERR << "Notification command returned with error (" << ret << ")." << endl;
+              HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
+              return;
+            }
+          }
+        }
+        else
+        {
+          INT << "Can't send Notification: Missing handler for 'format |' in command spec." << endl;
+          HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
+          return;
+        }
+      }
+
+
+      /** Look for update messages named 'name-version-release-*' and
+       *  send notification according to \ref ZConfig::updateMessagesNotify.
+       *
+       * \see http://en.opensuse.org/Software_Management/Code11/Scripts_and_Messages
+       */
+      void RunUpdateMessages( const Pathname & root_r,
+                              const Pathname & messagesPath_r,
+                              const std::vector<sat::Solvable> & checkPackages_r,
+                              ZYppCommitResult & result_r )
+      {
+        if ( checkPackages_r.empty() )
+          return; // no installed packages to check
+
+        MIL << "Looking for new update messages in (" <<  root_r << ")" << messagesPath_r << endl;
+        Pathname messagesDir( Pathname::assertprefix( root_r, messagesPath_r ) );
+        if ( ! PathInfo( messagesDir ).isDir() )
+          return; // no messages dir
+
+        std::list<std::string> messages;
+        filesystem::readdir( messages, messagesDir, /*dots*/false );
+        if ( messages.empty() )
+          return; // no messages in message dir
+
+        // Now collect all matching messages in result and send them
+        // - "name-version-release"
+        // - "name-version-release-*"
+        HistoryLog historylog;
+        for_( it, checkPackages_r.begin(), checkPackages_r.end() )
+        {
+          std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
+          for_( sit, messages.begin(), messages.end() )
+          {
+            if ( ! str::hasPrefix( *sit, prefix ) )
+              continue;
+
+            if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
+              continue; // if not exact match it had to continue with '-'
+
+            PathInfo message( messagesDir / *sit );
+            if ( ! message.isFile() || message.size() == 0 )
+              continue;
+
+            MIL << "Found update message " << *sit << endl;
+            Pathname localPath( messagesPath_r/(*sit) ); // without root prefix
+            result_r.rUpdateMessages().push_back( UpdateNotificationFile( *it, localPath ) );
+            historylog.comment( str::Str() << _("New update message") << " " << localPath, /*timestamp*/true );
+          }
+        }
+        sendNotification( root_r, result_r.updateMessages() );
+      }
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+    void XRunUpdateMessages( const Pathname & root_r,
+                             const Pathname & messagesPath_r,
+                             const std::vector<sat::Solvable> & checkPackages_r,
+                             ZYppCommitResult & result_r )
+    { RunUpdateMessages( root_r, messagesPath_r, checkPackages_r, result_r ); }
+
+    /** Helper for PackageProvider queries during commit. */
+    struct QueryInstalledEditionHelper
+    {
+      bool operator()( const std::string & name_r,
+                       const Edition &     ed_r,
+                       const Arch &        arch_r ) const
+      {
+        rpm::librpmDb::db_const_iterator it;
+        for ( it.findByName( name_r ); *it; ++it )
+          {
+            if ( arch_r == it->tag_arch()
+                 && ( ed_r == Edition::noedition || ed_r == it->tag_edition() ) )
+              {
+                return true;
+              }
+          }
+        return false;
+      }
+    };
+
+    /**
+     * \short Let the Source provide the package.
+     * \p pool_r \ref ResPool used to get candidates
+     * \p pi item to be commited
+    */
+    struct RepoProvidePackage
+    {
+      ResPool _pool;
+      repo::RepoMediaAccess &_access;
+
+      RepoProvidePackage( repo::RepoMediaAccess &access, ResPool pool_r )
+        : _pool(pool_r), _access(access)
+      {}
+
+      ManagedFile operator()( const PoolItem & pi )
+      {
+        // Redirect PackageProvider queries for installed editions
+        // (in case of patch/delta rpm processing) to rpmDb.
+        repo::PackageProviderPolicy packageProviderPolicy;
+        packageProviderPolicy.queryInstalledCB( QueryInstalledEditionHelper() );
+
+        Package::constPtr p = asKind<Package>(pi.resolvable());
+
+        // Build a repository list for repos
+        // contributing to the pool
+        std::list<Repository> repos( _pool.knownRepositoriesBegin(), _pool.knownRepositoriesEnd() );
+        repo::DeltaCandidates deltas(repos, p->name());
+        repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
+
+        ManagedFile ret( pkgProvider.providePackage() );
+        return ret;
+      }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    IMPL_PTR_TYPE(TargetImpl);
+
+    TargetImpl_Ptr TargetImpl::_nullimpl;
+
+    /** Null implementation */
+    TargetImpl_Ptr TargetImpl::nullimpl()
+    {
+      if (_nullimpl == 0)
+        _nullimpl = new TargetImpl;
+      return _nullimpl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TargetImpl::TargetImpl
+    // METHOD TYPE : Ctor
+    //
+    TargetImpl::TargetImpl( const Pathname & root_r, bool doRebuild_r )
+    : _root( root_r )
+    , _requestedLocalesFile( home() / "RequestedLocales" )
+    , _softLocksFile( home() / "SoftLocks" )
+    , _hardLocksFile( Pathname::assertprefix( _root, ZConfig::instance().locksFile() ) )
+    {
+      _rpm.initDatabase( root_r, Pathname(), doRebuild_r );
+
+      HistoryLog::setRoot(_root);
+
+      createAnonymousId();
+
+      MIL << "Initialized target on " << _root << endl;
+    }
+
+    /**
+     * generates a random id using uuidgen
+     */
+    static string generateRandomId()
+    {
+      string id;
+      const char* argv[] =
+      {
+         "/usr/bin/uuidgen",
+         NULL
+      };
+
+      ExternalProgram prog( argv,
+                            ExternalProgram::Normal_Stderr,
+                            false, -1, true);
+      std::string line;
+      for(line = prog.receiveLine();
+          ! line.empty();
+          line = prog.receiveLine() )
+      {
+          MIL << line << endl;
+          id = line;
+          break;
+      }
+      prog.close();
+      return id;
+    }
+
+    /**
+     * updates the content of \p filename
+     * if \p condition is true, setting the content
+     * the the value returned by \p value
+     */
+    void updateFileContent( const Pathname &filename,
+                            boost::function<bool ()> condition,
+                            boost::function<string ()> value )
+    {
+        string val = value();
+        // if the value is empty, then just dont
+        // do anything, regardless of the condition
+        if ( val.empty() )
+            return;
+
+        if ( condition() )
+        {
+            MIL << "updating '" << filename << "' content." << endl;
+
+            // if the file does not exist we need to generate the uuid file
+
+            std::ofstream filestr;
+            // make sure the path exists
+            filesystem::assert_dir( filename.dirname() );
+            filestr.open( filename.c_str() );
+
+            if ( filestr.good() )
+            {
+                filestr << val;
+                filestr.close();
+            }
+            else
+            {
+                // FIXME, should we ignore the error?
+                ZYPP_THROW(Exception("Can't openfile '" + filename.asString() + "' for writing"));
+            }
+        }
+    }
+
+    /** helper functor */
+    static bool fileMissing( const Pathname &pathname )
+    {
+        return ! PathInfo(pathname).isExist();
+    }
+
+    void TargetImpl::createAnonymousId() const
+    {
+
+      // create the anonymous unique id
+      // this value is used for statistics
+      Pathname idpath( home() / "AnonymousUniqueId");
+
+      try
+      {
+        updateFileContent( idpath,
+                           boost::bind(fileMissing, idpath),
+                           generateRandomId );
+      }
+      catch ( const Exception &e )
+      {
+        WAR << "Can't create anonymous id file" << endl;
+      }
+
+    }
+
+    void TargetImpl::createLastDistributionFlavorCache() const
+    {
+      // create the anonymous unique id
+      // this value is used for statistics
+      Pathname flavorpath( home() / "LastDistributionFlavor");
+
+      // is there a product
+      Product::constPtr p = baseProduct();
+      if ( ! p )
+      {
+          WAR << "No base product, I won't create flavor cache" << endl;
+          return;
+      }
+
+      string flavor = p->flavor();
+
+      try
+      {
+
+        updateFileContent( flavorpath,
+                           // only if flavor is not empty
+                           functor::Constant<bool>( ! flavor.empty() ),
+                           functor::Constant<string>(flavor) );
+      }
+      catch ( const Exception &e )
+      {
+        WAR << "Can't create flavor cache" << endl;
+        return;
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : TargetImpl::~TargetImpl
+    // METHOD TYPE : Dtor
+    //
+    TargetImpl::~TargetImpl()
+    {
+      _rpm.closeDatabase();
+      MIL << "Targets closed" << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // solv file handling
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Pathname TargetImpl::defaultSolvfilesPath() const
+    {
+      return Pathname::assertprefix( _root, ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoAlias() );
+    }
+
+    void TargetImpl::clearCache()
+    {
+      Pathname base = solvfilesPath();
+      filesystem::recursive_rmdir( base );
+    }
+
+    void TargetImpl::buildCache()
+    {
+      Pathname base = solvfilesPath();
+      Pathname rpmsolv       = base/"solv";
+      Pathname rpmsolvcookie = base/"cookie";
+
+      bool build_rpm_solv = true;
+      // lets see if the rpm solv cache exists
+
+      RepoStatus rpmstatus( RepoStatus( _root/"/var/lib/rpm/Name" )
+                            && (_root/"/etc/products.d") );
+
+      bool solvexisted = PathInfo(rpmsolv).isExist();
+      if ( solvexisted )
+      {
+        // see the status of the cache
+        PathInfo cookie( rpmsolvcookie );
+        MIL << "Read cookie: " << cookie << endl;
+        if ( cookie.isExist() )
+        {
+          RepoStatus status = RepoStatus::fromCookieFile(rpmsolvcookie);
+          // now compare it with the rpm database
+          if ( status.checksum() == rpmstatus.checksum() )
+            build_rpm_solv = false;
+          MIL << "Read cookie: " << rpmsolvcookie << " says: "
+              << (build_rpm_solv ? "outdated" : "uptodate") << endl;
+        }
+      }
+
+      if ( build_rpm_solv )
+      {
+        // if the solvfile dir does not exist yet, we better create it
+        filesystem::assert_dir( base );
+
+        Pathname oldSolvFile( solvexisted ? rpmsolv : Pathname() ); // to speedup rpmdb2solv
+
+        filesystem::TmpFile tmpsolv( filesystem::TmpFile::makeSibling( rpmsolv ) );
+        if ( !tmpsolv )
+        {
+          // Can't create temporary solv file, usually due to insufficient permission
+          // (user query while @System solv needs refresh). If so, try switching
+          // to a location within zypps temp. space (will be cleaned at application end).
+
+          bool switchingToTmpSolvfile = false;
+          Exception ex("Failed to cache rpm database.");
+          ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
+
+          if ( ! solvfilesPathIsTemp() )
+          {
+            base = getZYpp()->tmpPath() / sat::Pool::instance().systemRepoAlias();
+            rpmsolv       = base/"solv";
+            rpmsolvcookie = base/"cookie";
+
+            filesystem::assert_dir( base );
+            tmpsolv = filesystem::TmpFile::makeSibling( rpmsolv );
+
+            if ( tmpsolv )
+            {
+              WAR << "Using a temporary solv file at " << base << endl;
+              switchingToTmpSolvfile = true;
+              _tmpSolvfilesPath = base;
+            }
+            else
+            {
+              ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
+            }
+          }
+
+          if ( ! switchingToTmpSolvfile )
+          {
+            ZYPP_THROW(ex);
+          }
+        }
+
+        // Take care we unlink the solvfile on exception
+        ManagedFile guard( base, filesystem::recursive_rmdir );
+
+        std::ostringstream cmd;
+        cmd << "rpmdb2solv";
+        if ( ! _root.empty() )
+          cmd << " -r '" << _root << "'";
+
+        cmd << " -p '" << Pathname::assertprefix( _root, "/etc/products.d" ) << "'";
+
+        if ( ! oldSolvFile.empty() )
+          cmd << " '" << oldSolvFile << "'";
+
+        cmd << "  > '" << tmpsolv.path() << "'";
+
+        MIL << "Executing: " << cmd << endl;
+        ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
+
+        cmd << endl;
+        for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+          WAR << "  " << output;
+          cmd << "     " << output;
+        }
+
+        int ret = prog.close();
+        if ( ret != 0 )
+        {
+          Exception ex(str::form("Failed to cache rpm database (%d).", ret));
+          ex.remember( cmd.str() );
+          ZYPP_THROW(ex);
+        }
+
+        ret = filesystem::rename( tmpsolv, rpmsolv );
+        if ( ret != 0 )
+          ZYPP_THROW(Exception("Failed to move cache to final destination"));
+        // if this fails, don't bother throwing exceptions
+        filesystem::chmod( rpmsolv, 0644 );
+
+        rpmstatus.saveToCookieFile(rpmsolvcookie);
+
+        // We keep it.
+        guard.resetDispose();
+
+       // Finally send notification to plugins
+       // NOTE: quick hack looking for spacewalk plugin only
+       {
+         Pathname script( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"system/spacewalk" ) );
+         if ( PathInfo( script ).isX() )
+           try {
+             PluginScript spacewalk( script );
+             spacewalk.open();
+
+             PluginFrame notify( "PACKAGESETCHANGED" );
+             spacewalk.send( notify );
+
+             PluginFrame ret( spacewalk.receive() );
+             MIL << ret << endl;
+             if ( ret.command() == "ERROR" )
+               ret.writeTo( WAR ) << endl;
+           }
+           catch ( const Exception & excpt )
+           {
+             WAR << excpt.asUserHistory() << endl;
+           }
+       }
+      }
+    }
+
+    void TargetImpl::unload()
+    {
+      Repository system( sat::Pool::instance().findSystemRepo() );
+      if ( system )
+        system.eraseFromPool();
+    }
+
+
+    void TargetImpl::load()
+    {
+      buildCache();
+
+      // now add the repos to the pool
+      sat::Pool satpool( sat::Pool::instance() );
+      Pathname rpmsolv( solvfilesPath() / "solv" );
+      MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoAlias() << ")" << endl;
+
+      // Providing an empty system repo, unload any old content
+      Repository system( sat::Pool::instance().findSystemRepo() );
+      if ( system && ! system.solvablesEmpty() )
+      {
+        system.eraseFromPool(); // invalidates system
+      }
+      if ( ! system )
+      {
+        system = satpool.systemRepo();
+      }
+
+      try
+      {
+        system.addSolv( rpmsolv );
+      }
+      catch ( const Exception & exp )
+      {
+        ZYPP_CAUGHT( exp );
+        MIL << "Try to handle exception by rebuilding the solv-file" << endl;
+        clearCache();
+        buildCache();
+
+        system.addSolv( rpmsolv );
+      }
+
+      // (Re)Load the requested locales et al.
+      // If the requested locales are empty, we leave the pool untouched
+      // to avoid undoing changes the application applied. We expect this
+      // to happen on a bare metal installation only. An already existing
+      // target should be loaded before its settings are changed.
+      {
+        const LocaleSet & requestedLocales( _requestedLocalesFile.locales() );
+        if ( ! requestedLocales.empty() )
+        {
+          satpool.setRequestedLocales( requestedLocales );
+        }
+      }
+      {
+        SoftLocksFile::Data softLocks( _softLocksFile.data() );
+        if ( ! softLocks.empty() )
+        {
+          // Don't soft lock any installed item.
+          for_( it, system.solvablesBegin(), system.solvablesEnd() )
+          {
+            softLocks.erase( it->ident() );
+          }
+          ResPool::instance().setAutoSoftLocks( softLocks );
+        }
+      }
+      if ( ZConfig::instance().apply_locks_file() )
+      {
+        const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
+        if ( ! hardLocks.empty() )
+        {
+          ResPool::instance().setHardLockQueries( hardLocks );
+        }
+      }
+
+      // now that the target is loaded, we can cache the flavor
+      createLastDistributionFlavorCache();
+
+      MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // COMMIT
+    //
+    ///////////////////////////////////////////////////////////////////
+    ZYppCommitResult TargetImpl::commit( ResPool pool_r, const ZYppCommitPolicy & policy_rX )
+    {
+      // ----------------------------------------------------------------- //
+      ZYppCommitPolicy policy_r( policy_rX );
+
+      // Fake outstanding YCP fix: Honour restriction to media 1
+      // at installation, but install all remaining packages if post-boot.
+      if ( policy_r.restrictToMedia() > 1 )
+        policy_r.allMedia();
+
+      if ( policy_r.downloadMode() == DownloadDefault ) {
+        if ( root() == "/" )
+          policy_r.downloadMode(DownloadInHeaps);
+        else
+          policy_r.downloadMode(DownloadAsNeeded);
+      }
+      // DownloadOnly implies dry-run.
+      else if ( policy_r.downloadMode() == DownloadOnly )
+        policy_r.dryRun( true );
+      // ----------------------------------------------------------------- //
+
+      MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
+
+      ///////////////////////////////////////////////////////////////////
+      // Prepare execution of commit plugins:
+      ///////////////////////////////////////////////////////////////////
+      CommitPlugins commitPlugins;
+      if ( root() == "/" && ! policy_r.dryRun() )
+      {
+       Pathname plugindir( Pathname::assertprefix( _root, ZConfig::instance().pluginsPath()/"commit" ) );
+       commitPlugins.load( plugindir );
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      // Write out a testcase if we're in dist upgrade mode.
+      ///////////////////////////////////////////////////////////////////
+      if ( getZYpp()->resolver()->upgradeMode() )
+      {
+        if ( ! policy_r.dryRun() )
+        {
+          writeUpgradeTestcase();
+        }
+        else
+        {
+          DBG << "dryRun: Not writing upgrade testcase." << endl;
+        }
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      // Store non-package data:
+      ///////////////////////////////////////////////////////////////////
+      if ( ! policy_r.dryRun() )
+      {
+        filesystem::assert_dir( home() );
+        // requested locales
+        _requestedLocalesFile.setLocales( pool_r.getRequestedLocales() );
+        // weak locks
+        {
+          SoftLocksFile::Data newdata;
+          pool_r.getActiveSoftLocks( newdata );
+          _softLocksFile.setData( newdata );
+        }
+        // hard locks
+        if ( ZConfig::instance().apply_locks_file() )
+        {
+          HardLocksFile::Data newdata;
+          pool_r.getHardLockQueries( newdata );
+          _hardLocksFile.setData( newdata );
+        }
+      }
+      else
+      {
+        DBG << "dryRun: Not stroring non-package data." << endl;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      // Compute transaction:
+      ///////////////////////////////////////////////////////////////////
+      ZYppCommitResult result( root() );
+      result.rTransaction() = pool_r.resolver().getTransaction();
+      result.rTransaction().order();
+      // steps: this is our todo-list
+      ZYppCommitResult::TransactionStepList & steps( result.rTransactionStepList() );
+      if ( policy_r.restrictToMedia() )
+      {
+       // Collect until the 1st package from an unwanted media occurs.
+        // Further collection could violate install order.
+       MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
+       for_( it, result.transaction().begin(), result.transaction().end() )
+       {
+         if ( makeResObject( *it )->mediaNr() > 1 )
+           break;
+         steps.push_back( *it );
+       }
+      }
+      else
+      {
+       result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() );
+      }
+      MIL << "Todo: " << result << endl;
+
+      ///////////////////////////////////////////////////////////////////
+      // First collect and display all messages
+      // associated with patches to be installed.
+      ///////////////////////////////////////////////////////////////////
+      if ( ! policy_r.dryRun() )
+      {
+        for_( it, steps.begin(), steps.end() )
+        {
+         if ( ! it->satSolvable().isKind<Patch>() )
+           continue;
+
+         PoolItem pi( *it );
+          if ( ! pi.status().isToBeInstalled() )
+            continue;
+
+          Patch::constPtr patch( asKind<Patch>(pi.resolvable()) );
+         if ( ! patch ||patch->message().empty()  )
+           continue;
+
+         MIL << "Show message for " << patch << endl;
+         callback::SendReport<target::PatchMessageReport> report;
+         if ( ! report->show( patch ) )
+         {
+           WAR << "commit aborted by the user" << endl;
+           ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) );
+          }
+        }
+      }
+      else
+      {
+        DBG << "dryRun: Not checking patch messages." << endl;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      // Remove/install packages.
+      ///////////////////////////////////////////////////////////////////
+      DBG << "commit log file is set to: " << HistoryLog::fname() << endl;
+      if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly )
+      {
+       // Prepare the package cache. Pass all items requiring download.
+        repo::RepoMediaAccess access;
+        RepoProvidePackage repoProvidePackage( access, pool_r );
+        CommitPackageCache packageCache( root() / "tmp", repoProvidePackage );
+       packageCache.setCommitList( steps.begin(), steps.end() );
+
+        bool miss = false;
+        if ( policy_r.downloadMode() != DownloadAsNeeded )
+        {
+          // Preload the cache. Until now this means pre-loading all packages.
+          // Once DownloadInHeaps is fully implemented, this will change and
+          // we may actually have more than one heap.
+          for_( it, steps.begin(), steps.end() )
+          {
+           switch ( it->stepType() )
+           {
+             case sat::Transaction::TRANSACTION_INSTALL:
+             case sat::Transaction::TRANSACTION_MULTIINSTALL:
+               // proceed: only install actionas may require download.
+               break;
+
+             default:
+               // next: no download for or non-packages and delete actions.
+               continue;
+               break;
+           }
+
+           PoolItem pi( *it );
+            if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() )
+            {
+              ManagedFile localfile;
+              try
+              {
+               // TODO: unify packageCache.get for Package and SrcPackage
+               if ( pi->isKind<Package>() )
+               {
+                 localfile = packageCache.get( pi );
+               }
+               else if ( pi->isKind<SrcPackage>() )
+               {
+                 repo::RepoMediaAccess access;
+                 repo::SrcPackageProvider prov( access );
+                 localfile = prov.provideSrcPackage( pi->asKind<SrcPackage>() );
+               }
+               else
+               {
+                 INT << "Don't know howto cache: Neither Package nor SrcPackage: " << pi << endl;
+                 continue;
+               }
+                localfile.resetDispose(); // keep the package file in the cache
+              }
+              catch ( const AbortRequestException & exp )
+              {
+               it->stepStage( sat::Transaction::STEP_ERROR );
+                miss = true;
+                WAR << "commit cache preload aborted by the user" << endl;
+                ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) );
+                break;
+              }
+              catch ( const SkipRequestException & exp )
+              {
+                ZYPP_CAUGHT( exp );
+               it->stepStage( sat::Transaction::STEP_ERROR );
+                miss = true;
+                WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
+                continue;
+              }
+              catch ( const Exception & exp )
+              {
+                // bnc #395704: missing catch causes abort.
+                // TODO see if packageCache fails to handle errors correctly.
+                ZYPP_CAUGHT( exp );
+               it->stepStage( sat::Transaction::STEP_ERROR );
+                miss = true;
+                INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
+                continue;
+              }
+            }
+          }
+        }
+
+        if ( miss )
+        {
+          ERR << "Some packages could not be provided. Aborting commit."<< endl;
+        }
+        else if ( ! policy_r.dryRun() )
+        {
+          commit( policy_r, packageCache, result );
+        }
+        else
+        {
+          DBG << "dryRun: Not installing/deleting anything." << endl;
+        }
+      }
+      else
+      {
+        DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
+      }
+
+      ///////////////////////////////////////////////////////////////////
+      // Try to rebuild solv file while rpm database is still in cache
+      ///////////////////////////////////////////////////////////////////
+      if ( ! policy_r.dryRun() )
+      {
+        buildCache();
+      }
+
+      // for DEPRECATED old ZyppCommitResult results:
+      ///////////////////////////////////////////////////////////////////
+      // build return statistics
+      ///////////////////////////////////////////////////////////////////
+      result._errors.clear();
+      result._remaining.clear();
+      result._srcremaining.clear();
+      unsigned toInstall = 0;
+      for_( step, steps.begin(), steps.end() )
+      {
+       if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
+       {
+         // For non-packages only products might have beed installed.
+         // All the rest is ignored.
+         if ( step->satSolvable().isSystem() || ! step->satSolvable().isKind<Product>() )
+           continue;
+       }
+       else if ( step->stepType() == sat::Transaction::TRANSACTION_ERASE )
+       {
+         continue;
+       }
+       // to be installed:
+       ++toInstall;
+       switch ( step->stepStage() )
+       {
+         case sat::Transaction::STEP_TODO:
+           if ( step->satSolvable().isKind<Package>() )
+             result._remaining.push_back( PoolItem( *step ) );
+           else if ( step->satSolvable().isKind<SrcPackage>() )
+             result._srcremaining.push_back( PoolItem( *step ) );
+           break;
+         case sat::Transaction::STEP_DONE:
+           // NOOP
+           break;
+         case sat::Transaction::STEP_ERROR:
+           result._errors.push_back( PoolItem( *step ) );
+           break;
+       }
+      }
+      result._result = (toInstall - result._remaining.size());
+      ///////////////////////////////////////////////////////////////////
+
+      MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
+      return result;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // COMMIT internal
+    //
+    ///////////////////////////////////////////////////////////////////
+    void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
+                            CommitPackageCache & packageCache_r,
+                            ZYppCommitResult & result_r )
+    {
+      // steps: this is our todo-list
+      ZYppCommitResult::TransactionStepList & steps( result_r.rTransactionStepList() );
+      MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
+
+      bool abort = false;
+      std::vector<sat::Solvable> successfullyInstalledPackages;
+      TargetImpl::PoolItemList remaining;
+
+      for_( step, steps.begin(), steps.end() )
+      {
+       PoolItem citem( *step );
+       if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
+       {
+         if ( citem->isKind<Package>() )
+         {
+           // for packages this means being obsoleted (by rpm)
+           // thius no additional action is needed.
+           step->stepStage( sat::Transaction::STEP_DONE );
+           continue;
+         }
+       }
+
+        if ( citem->isKind<Package>() )
+        {
+          Package::constPtr p = citem->asKind<Package>();
+          if ( citem.status().isToBeInstalled() )
+          {
+            ManagedFile localfile;
+            try
+            {
+             localfile = packageCache_r.get( citem );
+            }
+            catch ( const AbortRequestException &e )
+            {
+              WAR << "commit aborted by the user" << endl;
+              abort = true;
+             step->stepStage( sat::Transaction::STEP_ERROR );
+             break;
+            }
+            catch ( const SkipRequestException &e )
+            {
+              ZYPP_CAUGHT( e );
+              WAR << "Skipping package " << p << " in commit" << endl;
+             step->stepStage( sat::Transaction::STEP_ERROR );
+              continue;
+            }
+            catch ( const Exception &e )
+            {
+              // bnc #395704: missing catch causes abort.
+              // TODO see if packageCache fails to handle errors correctly.
+              ZYPP_CAUGHT( e );
+              INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
+             step->stepStage( sat::Transaction::STEP_ERROR );
+              continue;
+            }
+
+#warning Exception handling
+            // create a installation progress report proxy
+            RpmInstallPackageReceiver progress( citem.resolvable() );
+            progress.connect(); // disconnected on destruction.
+
+            bool success = false;
+            rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
+            // Why force and nodeps?
+            //
+            // Because zypp builds the transaction and the resolver asserts that
+            // everything is fine.
+            // We use rpm just to unpack and register the package in the database.
+            // We do this step by step, so rpm is not aware of the bigger context.
+            // So we turn off rpms internal checks, because we do it inside zypp.
+            flags |= rpm::RPMINST_NODEPS;
+            flags |= rpm::RPMINST_FORCE;
+            //
+            if (p->multiversionInstall())  flags |= rpm::RPMINST_NOUPGRADE;
+            if (policy_r.dryRun())         flags |= rpm::RPMINST_TEST;
+            if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
+            if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
+
+            try
+            {
+              progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
+             rpm().installPackage( localfile, flags );
+              HistoryLog().install(citem);
+
+              if ( progress.aborted() )
+              {
+                WAR << "commit aborted by the user" << endl;
+                localfile.resetDispose(); // keep the package file in the cache
+                abort = true;
+               step->stepStage( sat::Transaction::STEP_ERROR );
+                break;
+              }
+              else
+              {
+                success = true;
+               step->stepStage( sat::Transaction::STEP_DONE );
+              }
+            }
+            catch ( Exception & excpt_r )
+            {
+              ZYPP_CAUGHT(excpt_r);
+              localfile.resetDispose(); // keep the package file in the cache
+
+              if ( policy_r.dryRun() )
+              {
+                WAR << "dry run failed" << endl;
+               step->stepStage( sat::Transaction::STEP_ERROR );
+                break;
+              }
+              // else
+              if ( progress.aborted() )
+              {
+                WAR << "commit aborted by the user" << endl;
+                abort = true;
+              }
+              else
+              {
+                WAR << "Install failed" << endl;
+              }
+              step->stepStage( sat::Transaction::STEP_ERROR );
+              break; // stop
+            }
+
+            if ( success && !policy_r.dryRun() )
+            {
+              citem.status().resetTransact( ResStatus::USER );
+              successfullyInstalledPackages.push_back( citem.satSolvable() );
+             step->stepStage( sat::Transaction::STEP_DONE );
+            }
+          }
+          else
+          {
+            RpmRemovePackageReceiver progress( citem.resolvable() );
+            progress.connect(); // disconnected on destruction.
+
+            bool success = false;
+            rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
+            flags |= rpm::RPMINST_NODEPS;
+            if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
+            try
+            {
+             rpm().removePackage( p, flags );
+              HistoryLog().remove(citem);
+
+              if ( progress.aborted() )
+              {
+                WAR << "commit aborted by the user" << endl;
+                abort = true;
+               step->stepStage( sat::Transaction::STEP_ERROR );
+                break;
+              }
+              else
+              {
+                success = true;
+               step->stepStage( sat::Transaction::STEP_DONE );
+              }
+            }
+            catch (Exception & excpt_r)
+            {
+              ZYPP_CAUGHT( excpt_r );
+              if ( progress.aborted() )
+              {
+                WAR << "commit aborted by the user" << endl;
+                abort = true;
+               step->stepStage( sat::Transaction::STEP_ERROR );
+                break;
+              }
+              // else
+              WAR << "removal of " << p << " failed";
+             step->stepStage( sat::Transaction::STEP_ERROR );
+            }
+            if ( success && !policy_r.dryRun() )
+            {
+              citem.status().resetTransact( ResStatus::USER );
+             step->stepStage( sat::Transaction::STEP_DONE );
+            }
+          }
+        }
+        else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
+        {
+          // Status is changed as the buddy package buddy
+          // gets installed/deleted. Handle non-buddies only.
+          if ( ! citem.buddy() )
+          {
+            if ( citem->isKind<Product>() )
+            {
+              Product::constPtr p = citem->asKind<Product>();
+              if ( citem.status().isToBeInstalled() )
+              {
+                ERR << "Can't install orphan product without release-package! " << citem << endl;
+              }
+              else
+              {
+                // Deleting the corresponding product entry is all we con do.
+                // So the product will no longer be visible as installed.
+                std::string referenceFilename( p->referenceFilename() );
+                if ( referenceFilename.empty() )
+                {
+                  ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
+                }
+                else
+                {
+                  PathInfo referenceFile( Pathname::assertprefix( _root, Pathname( "/etc/products.d" ) ) / referenceFilename );
+                  if ( ! referenceFile.isFile() || filesystem::unlink( referenceFile.path() ) != 0 )
+                  {
+                    ERR << "Delete orphan product failed: " << referenceFile << endl;
+                  }
+                }
+              }
+            }
+            else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
+            {
+              // SrcPackage is install-only
+              SrcPackage::constPtr p = citem->asKind<SrcPackage>();
+              installSrcPackage( p );
+            }
+
+            citem.status().resetTransact( ResStatus::USER );
+           step->stepStage( sat::Transaction::STEP_DONE );
+          }
+
+        }  // other resolvables
+
+      } // for
+
+      // Check presence of update scripts/messages. If aborting,
+      // at least log omitted scripts.
+      if ( ! successfullyInstalledPackages.empty() )
+      {
+        if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
+                                 successfullyInstalledPackages, abort ) )
+        {
+          WAR << "Commit aborted by the user" << endl;
+          abort = true;
+        }
+        // send messages after scripts in case some script generates output,
+        // that should be kept in t %ghost message file.
+        RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
+                          successfullyInstalledPackages,
+                          result_r );
+      }
+
+      if ( abort )
+      {
+        ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) );
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    rpm::RpmDb & TargetImpl::rpm()
+    {
+      return _rpm;
+    }
+
+    bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
+    {
+      return _rpm.hasFile(path_str, name_str);
+    }
+
+
+    Date TargetImpl::timestamp() const
+    {
+      return _rpm.timestamp();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    Product::constPtr TargetImpl::baseProduct() const
+    {
+      ResPool pool(ResPool::instance());
+      for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
+      {
+        Product::constPtr p = (*it)->asKind<Product>();
+        if ( p->isTargetDistribution() )
+          return p;
+      }
+      return 0L;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    namespace
+    {
+      parser::ProductFileData baseproductdata( const Pathname & root_r )
+      {
+        PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
+        if ( baseproduct.isFile() )
+        {
+          try
+          {
+            return parser::ProductFileReader::scanFile( baseproduct.path() );
+          }
+          catch ( const Exception & excpt )
+          {
+            ZYPP_CAUGHT( excpt );
+          }
+        }
+        return parser::ProductFileData();
+      }
+
+      inline Pathname staticGuessRoot( const Pathname & root_r )
+      {
+        if ( root_r.empty() )
+        {
+          // empty root: use existing Target or assume "/"
+          Pathname ret ( ZConfig::instance().systemRoot() );
+          if ( ret.empty() )
+            return Pathname("/");
+          return ret;
+        }
+        return root_r;
+      }
+
+      inline std::string firstNonEmptyLineIn( const Pathname & file_r )
+      {
+        std::ifstream idfile( file_r.c_str() );
+        for( iostr::EachLine in( idfile ); in; in.next() )
+        {
+          std::string line( str::trim( *in ) );
+          if ( ! line.empty() )
+            return line;
+        }
+        return std::string();
+      }
+    }
+
+    std::string TargetImpl::targetDistribution() const
+    { return baseproductdata( _root ).registerTarget(); }
+    // static version:
+    std::string TargetImpl::targetDistribution( const Pathname & root_r )
+    { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
+
+    std::string TargetImpl::targetDistributionRelease() const
+    { return baseproductdata( _root ).registerRelease(); }
+    // static version:
+    std::string TargetImpl::targetDistributionRelease( const Pathname & root_r )
+    { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
+
+    Target::DistributionLabel TargetImpl::distributionLabel() const
+    {
+      Target::DistributionLabel ret;
+      parser::ProductFileData pdata( baseproductdata( _root ) );
+      ret.shortName = pdata.shortName();
+      ret.summary = pdata.summary();
+      return ret;
+    }
+    // static version:
+    Target::DistributionLabel TargetImpl::distributionLabel( const Pathname & root_r )
+    {
+      Target::DistributionLabel ret;
+      parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
+      ret.shortName = pdata.shortName();
+      ret.summary = pdata.summary();
+      return ret;
+    }
+
+    std::string TargetImpl::distributionVersion() const
+    {
+      if ( _distributionVersion.empty() )
+      {
+        _distributionVersion = TargetImpl::distributionVersion(root());
+        if ( !_distributionVersion.empty() )
+          MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
+      }
+      return _distributionVersion;
+    }
+    // static version
+    std::string TargetImpl::distributionVersion( const Pathname & root_r )
+    {
+      std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
+      if ( distributionVersion.empty() )
+      {
+        // ...But the baseproduct method is not expected to work on RedHat derivatives.
+        // On RHEL, Fedora and others the "product version" is determined by the first package
+        // providing 'redhat-release'. This value is not hardcoded in YUM and can be configured
+        // with the $distroverpkg variable.
+        scoped_ptr<rpm::RpmDb> tmprpmdb;
+        if ( ZConfig::instance().systemRoot() == Pathname() )
+        {
+          try
+          {
+              tmprpmdb.reset( new rpm::RpmDb );
+              tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
+          }
+          catch( ... )
+          {
+            return "";
+          }
+        }
+        rpm::librpmDb::db_const_iterator it;
+        if ( it.findByProvides( ZConfig::instance().distroverpkg() ) )
+          distributionVersion = it->tag_version();
+      }
+      return distributionVersion;
+    }
+
+
+    std::string TargetImpl::distributionFlavor() const
+    {
+      return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
+    }
+    // static version:
+    std::string TargetImpl::distributionFlavor( const Pathname & root_r )
+    {
+      return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    std::string TargetImpl::anonymousUniqueId() const
+    {
+      return firstNonEmptyLineIn( home() / "AnonymousUniqueId" );
+    }
+    // static version:
+    std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
+    {
+      return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/AnonymousUniqueId" );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
+    {
+      // provide on local disk
+      repo::RepoMediaAccess access_r;
+      repo::SrcPackageProvider prov( access_r );
+      ManagedFile localfile = prov.provideSrcPackage( srcPackage_r );
+      // install it
+      rpm().installPackage ( localfile );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/TargetImpl.h b/zypp/target/TargetImpl.h
new file mode 100644 (file)
index 0000000..189b9fc
--- /dev/null
@@ -0,0 +1,222 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/TargetImpl.h
+ *
+*/
+#ifndef ZYPP_TARGET_TARGETIMPL_H
+#define ZYPP_TARGET_TARGETIMPL_H
+
+#include <iosfwd>
+#include <set>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/PoolItem.h"
+#include "zypp/ZYppCommit.h"
+
+#include "zypp/Pathname.h"
+#include "zypp/media/MediaAccess.h"
+#include "zypp/Target.h"
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/target/TargetException.h"
+#include "zypp/target/RequestedLocalesFile.h"
+#include "zypp/target/SoftLocksFile.h"
+#include "zypp/target/HardLocksFile.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    DEFINE_PTR_TYPE(TargetImpl);
+    class CommitPackageCache;
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : TargetImpl
+    //
+    /** Base class for concrete Target implementations.
+     *
+     * Constructed by \ref TargetFactory. Public access via \ref Target
+     * interface.
+    */
+    class TargetImpl : public base::ReferenceCounted, private base::NonCopyable
+    {
+      friend std::ostream & operator<<( std::ostream & str, const TargetImpl & obj );
+
+    public:
+      /** list of pool items  */
+      typedef std::list<PoolItem> PoolItemList;
+
+      /** set of pool items  */
+      typedef std::set<PoolItem> PoolItemSet;
+
+    public:
+      /** Ctor. */
+      TargetImpl(const Pathname & root_r = "/", bool doRebuild_r = false );
+      /** Dtor. */
+      virtual ~TargetImpl();
+
+      /** Null implementation */
+      static TargetImpl_Ptr nullimpl();
+
+      /**
+       * generates the unique anonymous id which is called
+       * when creating the target
+       */
+      void createAnonymousId() const;
+
+      /**
+       * generates a cache of the last product flavor
+       */
+      void createLastDistributionFlavorCache() const;
+
+      /** \name Solv file handling.
+       * If target solv file is outdated, but (non-root-)user has
+       * no permission to  create it at the default location, we
+       * use a temporary one.
+       */
+      //@{
+    private:
+      /** The systems default solv file location. */
+      Pathname defaultSolvfilesPath() const;
+
+      /** The solv file location actually in use (default or temp). */
+      Pathname solvfilesPath() const
+      { return solvfilesPathIsTemp() ? _tmpSolvfilesPath : defaultSolvfilesPath(); }
+
+      /** Whether we're using a temp. solvfile. */
+      bool solvfilesPathIsTemp() const
+      { return ! _tmpSolvfilesPath.empty(); }
+
+      Pathname _tmpSolvfilesPath;
+
+    public:
+      void load();
+
+      void unload();
+
+      void clearCache();
+
+      void buildCache();
+      //@}
+
+    public:
+
+      /** The root set for this target */
+      Pathname root() const
+      { return _root; }
+
+      /** The directory to store things. */
+      Pathname home() const
+      { return _root / "/var/lib/zypp"; }
+
+      /** Commit changes in the pool */
+      ZYppCommitResult commit( ResPool pool_r, const ZYppCommitPolicy & policy_r );
+
+      /** Install a source package on the Target. */
+      void installSrcPackage( const SrcPackage_constPtr & srcPackage_r );
+
+      /** Overload to realize stream output. */
+      virtual std::ostream & dumpOn( std::ostream & str ) const
+      {
+        return str << "TargetImpl";
+      }
+
+      /** The RPM database */
+      rpm::RpmDb & rpm();
+
+      /** If the package is installed and provides the file
+      Needed to evaluate split provides during Resolver::Upgrade() */
+      bool providesFile (const std::string & path_str, const std::string & name_str) const;
+
+      /** Return name of package owning \a path_str
+       * or empty string if no installed package owns \a path_str. */
+      std::string whoOwnsFile (const std::string & path_str) const
+      { return _rpm.whoOwnsFile (path_str); }
+
+      /** return the last modification date of the target */
+      Date timestamp() const;
+
+      /** \copydoc Target::baseProduct() */
+      Product::constPtr baseProduct() const;
+
+
+      /** \copydoc Target::targetDistribution() */
+      std::string targetDistribution() const;
+      /** \overload */
+      static std::string targetDistribution( const Pathname & root_r );
+
+      /** \copydoc Target::targetDistributionRelease()*/
+      std::string targetDistributionRelease() const;
+      /** \overload */
+      static std::string targetDistributionRelease( const Pathname & root_r );
+
+      /** \copydoc Target::distributionVersion()*/
+      Target::DistributionLabel distributionLabel() const;
+      /** \overload */
+      static Target::DistributionLabel distributionLabel( const Pathname & root_r );
+
+      /** \copydoc Target::distributionVersion()*/
+      std::string distributionVersion() const;
+      /** \overload */
+      static std::string distributionVersion( const Pathname & root_r );
+
+      /** \copydoc Target::distributionFlavor() */
+      std::string distributionFlavor() const;
+      /** \overload */
+      static std::string distributionFlavor( const Pathname & root_r );
+
+      /** \copydoc Target::anonymousUniqueId() */
+      std::string anonymousUniqueId() const;
+      /** \overload */
+      static std::string anonymousUniqueId( const Pathname & root_r );
+
+    private:
+      /** Commit ordered changes (internal helper) */
+      void commit( const ZYppCommitPolicy & policy_r,
+                  CommitPackageCache & packageCache_r,
+                  ZYppCommitResult & result_r );
+
+    protected:
+      /** Path to the target */
+      Pathname _root;
+      /** RPM database */
+      rpm::RpmDb _rpm;
+      /** Requested Locales database */
+      RequestedLocalesFile _requestedLocalesFile;
+      /** Soft-locks database */
+      SoftLocksFile _softLocksFile;
+      /** Hard-Locks database */
+      HardLocksFile _hardLocksFile;
+      /** Cache distributionVersion */
+      mutable std::string _distributionVersion;
+
+    private:
+      /** Null implementation */
+      static TargetImpl_Ptr _nullimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates TargetImpl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const TargetImpl & obj )
+    {
+      return obj.dumpOn( str );
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_TARGETIMPL_H
diff --git a/zypp/target/hal/HalContext.cc b/zypp/target/hal/HalContext.cc
new file mode 100644 (file)
index 0000000..c80367d
--- /dev/null
@@ -0,0 +1,1255 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/hal/HalContext.cc
+ *
+ *  \brief Hardware abstaction layer library wrapper implementation.
+ */
+#include <zypp/target/hal/HalException.h>
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace target
+  { //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+    namespace hal
+    { ////////////////////////////////////////////////////////////////
+      NoHalException::NoHalException()
+        : Exception(_("Sorry, but this version of libzypp was built without HAL support."))
+      {}
+      ////////////////////////////////////////////////////////////////
+    } // namespace hal
+    //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+  } // namespace target
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#ifndef NO_HAL // disables zypp's HAL dependency
+
+#include <zypp/target/hal/HalContext.h>
+#include <zypp/thread/Mutex.h>
+#include <zypp/thread/MutexLock.h>
+#include <zypp/base/NonCopyable.h>
+#include <zypp/base/Logger.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Gettext.h>
+
+#include <hal/libhal.h>
+#include <hal/libhal-storage.h>
+
+#include <iostream>
+
+using namespace std;
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace target
+  { //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+    namespace hal
+    { ////////////////////////////////////////////////////////////////
+
+      using zypp::thread::Mutex;
+      using zypp::thread::MutexLock;
+
+      ////////////////////////////////////////////////////////////////
+      namespace // anonymous
+      { //////////////////////////////////////////////////////////////
+
+
+        //////////////////////////////////////////////////////////////
+        // STATIC
+        /**
+         ** hmm... currently a global one..
+        */
+        static Mutex g_Mutex;
+
+
+        //////////////////////////////////////////////////////////////
+        /**
+         * Internal hal (dbus ) error helper class.
+         */
+        class HalError
+        {
+        public:
+          DBusError error;
+
+          HalError()  { dbus_error_init(&error); }
+          ~HalError() { dbus_error_free(&error); }
+
+          inline bool         isSet() const
+          {
+            return dbus_error_is_set(&error);
+          }
+
+          inline HalException halException(const std::string &msg = std::string()) const
+          {
+            if( isSet() && error.name != NULL && error.message != NULL) {
+              return HalException(error.name, error.message);
+            }
+            else if( !msg.empty()) {
+              return HalException(msg);
+            }
+            else {
+              return HalException();
+            }
+          }
+        };
+
+
+        // -----------------------------------------------------------
+        inline void
+        VERIFY_CONTEXT(const zypp::RW_pointer<HalContext_Impl> &h)
+        {
+          if( !h)
+          {
+            ZYPP_THROW(HalException(_("HalContext not connected")));
+          }
+        }
+
+        // -----------------------------------------------------------
+        inline void
+        VERIFY_DRIVE(const zypp::RW_pointer<HalDrive_Impl> &d)
+        {
+          if( !d)
+          {
+            ZYPP_THROW(HalException(_("HalDrive not initialized")));
+          }
+        }
+
+        // -----------------------------------------------------------
+        inline void
+        VERIFY_VOLUME(const zypp::RW_pointer<HalVolume_Impl> &v)
+        {
+          if( !v)
+          {
+            ZYPP_THROW(HalException(_("HalVolume not initialized")));
+          }
+        }
+
+        //////////////////////////////////////////////////////////////
+      } // anonymous
+      ////////////////////////////////////////////////////////////////
+
+      ////////////////////////////////////////////////////////////////
+      std::ostream &
+      HalException::dumpOn( std::ostream & str ) const
+      {
+        if(!e_name.empty() && !e_msg.empty())
+          return str << msg() << ": " << e_msg << " (" << e_name << ")";
+        else if(!e_msg.empty())
+          return str << msg() << ": " << e_msg;
+        else
+          return str << msg();
+      }
+
+      ////////////////////////////////////////////////////////////////
+      class HalContext_Impl
+      {
+      public:
+        HalContext_Impl();
+        ~HalContext_Impl();
+
+        DBusConnection *conn;
+        LibHalContext  *hctx;
+        bool            pcon; // private connection
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+      class HalDrive_Impl
+      {
+      public:
+        zypp::RW_pointer<HalContext_Impl>  hal;
+        LibHalDrive                       *drv;
+
+        HalDrive_Impl()
+          : hal(), drv(NULL)
+        {
+        }
+
+        HalDrive_Impl(const zypp::RW_pointer<HalContext_Impl> &r,
+                      LibHalDrive *d)
+          : hal(r), drv(d)
+        {
+        }
+
+        ~HalDrive_Impl()
+        {
+          if( drv)
+            libhal_drive_free(drv);
+        }
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+      class HalVolume_Impl
+      {
+      public:
+        LibHalVolume *vol;
+
+        HalVolume_Impl(LibHalVolume *v=NULL)
+          : vol(v)
+        {
+        }
+
+        ~HalVolume_Impl()
+        {
+          if( vol)
+            libhal_volume_free(vol);
+        }
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+      HalContext_Impl::HalContext_Impl()
+        : conn(NULL)
+        , hctx(NULL)
+        , pcon(false) // we allways use shared connections at the moment
+      {
+        HalError err;
+
+        if( pcon)
+          conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err.error);
+        else
+          conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err.error);
+        if( !conn) {
+          ZYPP_THROW(err.halException(
+            _("Unable to create dbus connection")
+          ));
+        }
+
+        hctx = libhal_ctx_new();
+        if( !hctx)
+        {
+          if( pcon)
+              dbus_connection_close(conn);
+          dbus_connection_unref(conn);
+          conn = NULL;
+
+          ZYPP_THROW(HalException(
+            _("libhal_ctx_new: Can't create libhal context")
+          ));
+        }
+
+        if( !libhal_ctx_set_dbus_connection(hctx, conn))
+        {
+          libhal_ctx_free(hctx);
+          hctx = NULL;
+
+          if( pcon)
+            dbus_connection_close(conn);
+          dbus_connection_unref(conn);
+          conn = NULL;
+
+          ZYPP_THROW(HalException(
+            _("libhal_set_dbus_connection: Can't set dbus connection")
+          ));
+        }
+
+        if( !libhal_ctx_init(hctx, &err.error))
+        {
+          libhal_ctx_free(hctx);
+          hctx = NULL;
+
+          if( pcon)
+            dbus_connection_close(conn);
+          dbus_connection_unref(conn);
+          conn = NULL;
+
+          ZYPP_THROW(err.halException(
+            _("Unable to initalize HAL context -- hald not running?")
+          ));
+        }
+      }
+
+      // -------------------------------------------------------------
+      HalContext_Impl::~HalContext_Impl()
+      {
+        if( hctx)
+        {
+          HalError err;
+          libhal_ctx_shutdown(hctx, &err.error);
+          libhal_ctx_free( hctx);
+        }
+        if( conn)
+        {
+          if( pcon)
+            dbus_connection_close(conn);
+          dbus_connection_unref(conn);
+        }
+      }
+
+
+      ////////////////////////////////////////////////////////////////
+      HalContext::HalContext(bool autoconnect)
+        : h_impl( NULL)
+      {
+        MutexLock lock(g_Mutex);
+
+        if( autoconnect)
+          h_impl.reset( new HalContext_Impl());
+      }
+
+      // -------------------------------------------------------------
+      HalContext::HalContext(const HalContext &context)
+        : h_impl( NULL)
+      {
+        MutexLock lock(g_Mutex);
+
+        zypp::RW_pointer<HalContext_Impl>(context.h_impl).swap(h_impl);
+      }
+
+      // -------------------------------------------------------------
+      HalContext::~HalContext()
+      {
+        MutexLock  lock(g_Mutex);
+
+        h_impl.reset();
+      }
+
+      // --------------------------------------------------------------
+      HalContext &
+      HalContext::operator=(const HalContext &context)
+      {
+        MutexLock  lock(g_Mutex);
+
+        if( this == &context)
+          return *this;
+
+        zypp::RW_pointer<HalContext_Impl>(context.h_impl).swap(h_impl);
+        return *this;
+      }
+
+      // --------------------------------------------------------------
+      HalContext::operator HalContext::bool_type() const
+      {
+        MutexLock  lock(g_Mutex);
+
+        return h_impl;
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::connect()
+      {
+        MutexLock lock(g_Mutex);
+
+        if( !h_impl)
+          h_impl.reset( new HalContext_Impl());
+      }
+
+      // --------------------------------------------------------------
+      std::vector<std::string>
+      HalContext::getAllDevices() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError   err;
+        char     **names;
+        int        count = 0;
+
+        names = libhal_get_all_devices( h_impl->hctx, &count, &err.error);
+        if( !names)
+        {
+          ZYPP_THROW(err.halException());
+        }
+
+        std::vector<std::string> ret(names, names + count);
+        libhal_free_string_array(names);
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      HalDrive
+      HalContext::getDriveFromUDI(const std::string &udi) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        LibHalDrive *drv = libhal_drive_from_udi(h_impl->hctx, udi.c_str());
+        if( drv != NULL)
+          return HalDrive(new HalDrive_Impl( h_impl, drv));
+        else
+          return HalDrive();
+      }
+
+      // --------------------------------------------------------------
+      HalVolume
+      HalContext::getVolumeFromUDI(const std::string &udi) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        LibHalVolume *vol = libhal_volume_from_udi(h_impl->hctx, udi.c_str());
+        if( vol)
+          return HalVolume( new HalVolume_Impl(vol));
+        else
+          return HalVolume();
+      }
+
+      // --------------------------------------------------------------
+      HalVolume
+      HalContext::getVolumeFromDeviceFile(const std::string &device_file) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        LibHalVolume *vol = libhal_volume_from_device_file(h_impl->hctx,
+                                                           device_file.c_str());
+        if( vol)
+          return HalVolume( new HalVolume_Impl(vol));
+        else
+          return HalVolume();
+      }
+
+      // --------------------------------------------------------------
+      std::vector<std::string>
+      HalContext::findDevicesByCapability(const std::string &capability) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError   err;
+        char     **names;
+        int        count = 0;
+
+        names = libhal_find_device_by_capability(h_impl->hctx,
+                                                 capability.c_str(),
+                                                 &count, &err.error);
+        if( !names)
+        {
+          ZYPP_THROW(err.halException());
+        }
+
+        std::vector<std::string> ret(names, names + count);
+        libhal_free_string_array(names);
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      bool
+      HalContext::getDevicePropertyBool  (const std::string &udi,
+                                          const std::string &key) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_get_property_bool  (h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                &err.error);
+        if( err.isSet())
+        {
+          ZYPP_THROW(err.halException());
+        }
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      int32_t
+      HalContext::getDevicePropertyInt32 (const std::string &udi,
+                                          const std::string &key) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_int32_t  ret;
+
+        ret = libhal_device_get_property_int   (h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                &err.error);
+        if( err.isSet())
+        {
+          ZYPP_THROW(err.halException());
+        }
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      uint64_t
+      HalContext::getDevicePropertyUInt64(const std::string &udi,
+                                          const std::string &key) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_uint64_t ret;
+
+        ret = libhal_device_get_property_uint64(h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                &err.error);
+        if( err.isSet())
+        {
+          ZYPP_THROW(err.halException());
+        }
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      double
+      HalContext::getDevicePropertyDouble(const std::string &udi,
+                                          const std::string &key) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        double        ret;
+
+        ret = libhal_device_get_property_bool  (h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                &err.error);
+        if( err.isSet())
+        {
+          ZYPP_THROW(err.halException());
+        }
+        return ret;
+      }
+
+
+      // --------------------------------------------------------------
+      std::string
+      HalContext::getDevicePropertyString(const std::string &udi,
+                                          const std::string &key) const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        std::string   ret;
+        char         *ptr;
+
+        ptr = libhal_device_get_property_string(h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                &err.error);
+        if( err.isSet())
+        {
+          ZYPP_THROW(err.halException());
+        }
+        if( ptr != NULL)
+        {
+          ret = ptr;
+          free(ptr);
+        }
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::setDevicePropertyBool  (const std::string &udi,
+                                          const std::string &key,
+                                          bool               value)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_set_property_bool  (h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                value ? 1 : 0,
+                                                &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::setDevicePropertyInt32 (const std::string &udi,
+                                          const std::string &key,
+                                          int32_t            value)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_set_property_int   (h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                value,
+                                                &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::setDevicePropertyUInt64(const std::string &udi,
+                                          const std::string &key,
+                                          uint64_t           value)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_set_property_uint64(h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                value,
+                                                &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::setDevicePropertyDouble(const std::string &udi,
+                                          const std::string &key,
+                                          double             value)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_set_property_double(h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                value,
+                                                &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::setDevicePropertyString(const std::string &udi,
+                                          const std::string &key,
+                                          const std::string &value)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_set_property_string(h_impl->hctx,
+                                                udi.c_str(),
+                                                key.c_str(),
+                                                value.c_str(),
+                                                &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      // --------------------------------------------------------------
+      void
+      HalContext::removeDeviceProperty(const std::string &udi,
+                                       const std::string &key)
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_CONTEXT(h_impl);
+
+        HalError      err;
+        dbus_bool_t   ret;
+
+        ret = libhal_device_remove_property(h_impl->hctx,
+                                            udi.c_str(),
+                                            key.c_str(),
+                                            &err.error);
+        if( !ret)
+        {
+          ZYPP_THROW(err.halException());
+        }
+      }
+
+      ////////////////////////////////////////////////////////////////
+      HalDrive::HalDrive()
+        : d_impl( NULL)
+      {
+      }
+
+      // --------------------------------------------------------------
+      HalDrive::HalDrive(HalDrive_Impl *impl)
+        : d_impl( NULL)
+      {
+        MutexLock  lock(g_Mutex);
+
+        d_impl.reset(impl);
+      }
+
+      // --------------------------------------------------------------
+      HalDrive::HalDrive(const HalDrive &drive)
+        : d_impl( NULL)
+      {
+        MutexLock  lock(g_Mutex);
+
+        zypp::RW_pointer<HalDrive_Impl>(drive.d_impl).swap(d_impl);
+      }
+
+      // --------------------------------------------------------------
+      HalDrive::~HalDrive()
+      {
+        MutexLock  lock(g_Mutex);
+
+        d_impl.reset();
+      }
+
+      // --------------------------------------------------------------
+      HalDrive &
+      HalDrive::operator=(const HalDrive &drive)
+      {
+        MutexLock  lock(g_Mutex);
+
+        if( this == &drive)
+          return *this;
+
+        zypp::RW_pointer<HalDrive_Impl>(drive.d_impl).swap(d_impl);
+        return *this;
+      }
+
+      // --------------------------------------------------------------
+      HalDrive::operator HalDrive::bool_type() const
+      {
+        MutexLock  lock(g_Mutex);
+
+        return d_impl;
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalDrive::getUDI() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        const char *ptr = libhal_drive_get_udi(d_impl->drv);
+        return std::string(ptr ? ptr : "");
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalDrive::getTypeName() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        const char *ptr = libhal_drive_get_type_textual(d_impl->drv);
+        return std::string(ptr ? ptr : "");
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalDrive::getDeviceFile() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        return std::string(libhal_drive_get_device_file(d_impl->drv));
+      }
+
+      // --------------------------------------------------------------
+      unsigned int
+      HalDrive::getDeviceMajor() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        return libhal_drive_get_device_major(d_impl->drv);
+      }
+
+      // --------------------------------------------------------------
+      unsigned int
+      HalDrive::getDeviceMinor() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        return libhal_drive_get_device_minor(d_impl->drv);
+      }
+
+      // --------------------------------------------------------------
+      bool
+      HalDrive::usesRemovableMedia() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        return libhal_drive_uses_removable_media(d_impl->drv);
+      }
+
+      // --------------------------------------------------------------
+      std::vector<std::string>
+      HalDrive::getCdromCapabilityNames() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        std::vector<std::string> ret;
+        LibHalDriveCdromCaps     caps;
+
+        /*
+        ** FIXME: there is no textual variant :-(
+        **        using property key names...
+        */
+        caps = libhal_drive_get_cdrom_caps(d_impl->drv);
+
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_CDROM)
+          ret.push_back("cdrom");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_CDR)
+          ret.push_back("cdr");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_CDRW)
+          ret.push_back("cdrw");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDRAM)
+          ret.push_back("dvdram");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDROM)
+          ret.push_back("dvd");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDR)
+          ret.push_back("dvdr");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDRW)
+          ret.push_back("dvdrw");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDPLUSR)
+          ret.push_back("dvdplusr");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDPLUSRW)
+          ret.push_back("dvdplusrw");
+        if(caps & LIBHAL_DRIVE_CDROM_CAPS_DVDPLUSRDL)
+          ret.push_back("dvdplusrdl");
+
+        return ret;
+
+#if 0
+        if( libhal_drive_get_type(d_impl->drv) != LIBHAL_DRIVE_TYPE_CDROM)
+          ZYPP_THROW(HalException(_("Not a CDROM drive")));
+
+        /*
+        ** FIXME: we use property keys matching
+        **          "storage.cdrom.cd*"
+        **          "storage.cdrom.dvd*"
+        ** but this may print other bool keys,
+        ** that are not CDROM caps.
+        */
+        LibHalPropertySet         *props;
+        HalError                   err;
+
+        props = libhal_device_get_all_properties(d_impl->hal->hctx,
+                                                 getUDI().c_str(),
+                                                 &err.error);
+        if( !props)
+          ZYPP_THROW(err.halException());
+
+        std::vector<std::string>   ret(1, getTypeName());
+        std::string                key;
+        std::string                dvd("storage.cdrom.dvd");
+        std::string                cd ("storage.cdrom.cd");
+
+        LibHalPropertySetIterator  it;
+        for(libhal_psi_init(&it, props);
+            libhal_psi_has_more(&it);
+            libhal_psi_next(&it))
+        {
+          if( libhal_psi_get_type(&it) == LIBHAL_PROPERTY_TYPE_BOOLEAN &&
+              libhal_psi_get_bool(&it))
+          {
+            key = libhal_psi_get_key(&it);
+            if( key.compare(0, cd.size(), cd) == 0)
+            {
+              ret.push_back(key.substr(sizeof("storage.cdrom.")-1));
+            }
+            else
+            if( key.compare(0, dvd.size(), dvd) == 0)
+            {
+              ret.push_back(key.substr(sizeof("storage.cdrom.")-1));
+            }
+          }
+        }
+        libhal_free_property_set(props);
+
+        return ret;
+#endif
+      }
+
+      // --------------------------------------------------------------
+      std::vector<std::string>
+      HalDrive::findAllVolumes() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_DRIVE(d_impl);
+
+        char     **names;
+        int        count = 0;
+
+        names = libhal_drive_find_all_volumes(d_impl->hal->hctx,
+                                              d_impl->drv,
+                                              &count);
+
+        std::vector<std::string> ret;
+        ret.assign(names, names + count);
+        libhal_free_string_array(names);
+        return ret;
+      }
+
+
+      ////////////////////////////////////////////////////////////////
+      HalVolume::HalVolume()
+        : v_impl( NULL)
+      {}
+
+      HalVolume::HalVolume(HalVolume_Impl *impl)
+        : v_impl( NULL)
+      {
+        MutexLock  lock(g_Mutex);
+
+        v_impl.reset(impl);
+      }
+
+      // --------------------------------------------------------------
+      HalVolume::HalVolume(const HalVolume &volume)
+        : v_impl( NULL)
+      {
+        MutexLock  lock(g_Mutex);
+
+        zypp::RW_pointer<HalVolume_Impl>(volume.v_impl).swap(v_impl);
+      }
+
+      // --------------------------------------------------------------
+      HalVolume::~HalVolume()
+      {
+        MutexLock  lock(g_Mutex);
+
+        v_impl.reset();
+      }
+
+      // --------------------------------------------------------------
+      HalVolume &
+      HalVolume::operator=(const HalVolume &volume)
+      {
+        MutexLock  lock(g_Mutex);
+
+        if( this == &volume)
+          return *this;
+
+        zypp::RW_pointer<HalVolume_Impl>(volume.v_impl).swap(v_impl);
+        return *this;
+      }
+
+      // --------------------------------------------------------------
+      HalVolume::operator HalVolume::bool_type() const
+      {
+        MutexLock  lock(g_Mutex);
+
+        return v_impl;
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalVolume::getUDI() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        const char *ptr = libhal_volume_get_udi(v_impl->vol);
+        return std::string(ptr ? ptr : "");
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalVolume::getDeviceFile() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return std::string(libhal_volume_get_device_file(v_impl->vol));
+      }
+
+      // --------------------------------------------------------------
+      unsigned int
+      HalVolume::getDeviceMajor() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return libhal_volume_get_device_major(v_impl->vol);
+      }
+
+      // --------------------------------------------------------------
+      unsigned int
+      HalVolume::getDeviceMinor() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return libhal_volume_get_device_minor(v_impl->vol);
+      }
+
+      // --------------------------------------------------------------
+      bool
+      HalVolume::isDisc() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return libhal_volume_is_disc(v_impl->vol);
+      }
+
+      // --------------------------------------------------------------
+      bool
+      HalVolume::isPartition() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return libhal_volume_is_partition(v_impl->vol);
+      }
+
+      // --------------------------------------------------------------
+      bool
+      HalVolume::isMounted() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return libhal_volume_is_mounted(v_impl->vol);
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalVolume::getFSType() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        return std::string( libhal_volume_get_fstype(v_impl->vol));
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalVolume::getFSUsage() const
+      {
+        MutexLock  lock(g_Mutex);
+        VERIFY_VOLUME(v_impl);
+
+        LibHalVolumeUsage usage( libhal_volume_get_fsusage(v_impl->vol));
+        std::string       ret;
+        switch( usage)
+        {
+          case  LIBHAL_VOLUME_USAGE_MOUNTABLE_FILESYSTEM:
+            ret = "filesystem";
+          break;
+          case LIBHAL_VOLUME_USAGE_PARTITION_TABLE:
+            ret = "partitiontable";
+          break;
+          case LIBHAL_VOLUME_USAGE_RAID_MEMBER:
+            return "raid";
+          break;
+          case LIBHAL_VOLUME_USAGE_CRYPTO:
+            ret = "crypto";
+          break;
+          case LIBHAL_VOLUME_USAGE_UNKNOWN:
+          default:
+          break;
+        }
+        return ret;
+      }
+
+      // --------------------------------------------------------------
+      std::string
+      HalVolume::getMountPoint() const
+      {
+        VERIFY_VOLUME(v_impl);
+
+        return std::string( libhal_volume_get_mount_point(v_impl->vol));
+      }
+
+
+      ////////////////////////////////////////////////////////////////
+    } // namespace hal
+    //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+  } // namespace target
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+#else // NO_HAL
+#include <zypp/target/hal/HalContext.h>
+#include <zypp/target/hal/HalException.h>
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace target
+  { //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+    namespace hal
+    { ////////////////////////////////////////////////////////////////
+
+      std::ostream &
+      HalException::dumpOn( std::ostream & str ) const
+      { return str; }
+
+      // --------------------------------------------------------------
+      class HalContext_Impl
+      {};
+      class HalDrive_Impl
+      {};
+      class HalVolume_Impl
+      {};
+
+      // --------------------------------------------------------------
+      HalContext::HalContext(bool)
+      { ZYPP_THROW( NoHalException() ); }
+      HalContext::~HalContext()
+      {}
+      HalContext &
+      HalContext::operator=(const HalContext &)
+      { return *this; }
+      HalContext::operator HalContext::bool_type() const
+      { return 0; }
+      void
+      HalContext::connect()
+      {}
+      std::vector<std::string>
+      HalContext::getAllDevices() const
+      { return std::vector<std::string>(); }
+      HalDrive
+      HalContext::getDriveFromUDI(const std::string &) const
+      { return HalDrive(); }
+      HalVolume
+      HalContext::getVolumeFromUDI(const std::string &) const
+      { return HalVolume(); }
+      HalVolume
+      HalContext::getVolumeFromDeviceFile(const std::string &) const
+      { return HalVolume(); }
+      std::vector<std::string>
+      HalContext::findDevicesByCapability(const std::string &) const
+      { return std::vector<std::string>(); }
+      bool
+      HalContext::getDevicePropertyBool(const std::string &, const std::string &) const
+      { return false; }
+      void
+      HalContext::setDevicePropertyBool  (const std::string &, const std::string &, bool value)
+      {}
+      void
+      HalContext::removeDeviceProperty(const std::string &, const std::string &)
+      {}
+      std::string
+      HalContext::getDevicePropertyString(const std::string &, const std::string &) const
+      { return ""; }
+      // --------------------------------------------------------------
+      HalDrive::HalDrive()
+      { ZYPP_THROW( NoHalException() ); }
+      HalDrive::~HalDrive()
+      {}
+      HalDrive &
+      HalDrive::operator=(const HalDrive &)
+      { return *this; }
+      HalDrive::operator HalDrive::bool_type() const
+      { return 0; }
+      std::string
+      HalDrive::getUDI() const
+      { return std::string(); }
+      std::string
+      HalDrive::getTypeName() const
+      { return std::string(); }
+      std::string
+      HalDrive::getDeviceFile() const
+      { return std::string(); }
+      unsigned int
+      HalDrive::getDeviceMinor() const
+      { return 0; }
+      unsigned int
+      HalDrive::getDeviceMajor() const
+      { return 0; }
+      bool
+      HalDrive::usesRemovableMedia() const
+      { return false; }
+      std::vector<std::string>
+      HalDrive::getCdromCapabilityNames() const
+      { return std::vector<std::string>(); }
+      std::vector<std::string>
+      HalDrive::findAllVolumes() const
+      { return std::vector<std::string>(); }
+
+      // --------------------------------------------------------------
+      HalVolume::HalVolume()
+      { ZYPP_THROW( NoHalException() ); }
+      HalVolume::~HalVolume()
+      {}
+      HalVolume &
+      HalVolume::operator=(const HalVolume &)
+      { return *this; }
+      HalVolume::operator HalVolume::bool_type() const
+      { return 0; }
+      std::string
+      HalVolume::getUDI() const
+      { return std::string(); }
+      std::string
+      HalVolume::getDeviceFile() const
+      { return std::string(); }
+      unsigned int
+      HalVolume::getDeviceMinor() const
+      { return 0; }
+      unsigned int
+      HalVolume::getDeviceMajor() const
+      { return 0; }
+      bool
+      HalVolume::isDisc() const
+      { return false; }
+      bool
+      HalVolume::isPartition() const
+      { return false; }
+      bool
+      HalVolume::isMounted() const
+      { return false; }
+      std::string
+      HalVolume::getFSType() const
+      { return std::string(); }
+      std::string
+      HalVolume::getFSUsage() const
+      { return std::string(); }
+      std::string
+      HalVolume::getMountPoint() const
+      { return std::string(); }
+
+      ////////////////////////////////////////////////////////////////
+    } // namespace hal
+    //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+  } // namespace target
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+#endif // NO_HAL
+
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/target/hal/HalContext.h b/zypp/target/hal/HalContext.h
new file mode 100644 (file)
index 0000000..9ad75d6
--- /dev/null
@@ -0,0 +1,346 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/hal/HalContext.h
+ *
+ *  \brief Hardware abstaction layer library wrapper.
+ */
+#ifndef ZYPP_TARGET_HAL_HALCONTEXT_H
+#define ZYPP_TARGET_HAL_HALCONTEXT_H
+
+#include <zypp/target/hal/HalException.h>
+#include <zypp/base/PtrTypes.h>
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace target
+  { //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+    namespace hal
+    { ////////////////////////////////////////////////////////////////
+
+
+      // -------------------------------------------------------------
+      /** @{
+       * Forward declarations.
+       */
+      class HalDrive;
+      class HalVolume;
+
+      class HalDrive_Impl;
+      class HalVolume_Impl;
+      class HalContext_Impl;
+      /** }@ */
+
+
+      ////////////////////////////////////////////////////////////////
+      //
+      // CLASS NAME : HalContext
+      //
+      /** Hardware abstaction layer context.
+       *
+       * Hal context wrapper. It manages the dbus connection and is
+       * the entry point to query drives, volumes and other information.
+       *
+       * @todo: wrap more functions.
+       */
+      class HalContext
+      {
+      public:
+        typedef
+        zypp::RW_pointer<HalContext_Impl>::unspecified_bool_type  bool_type;
+
+        HalContext(bool autoconnect=false);
+        HalContext(const HalContext &context);
+        ~HalContext();
+
+        HalContext&
+        operator=(const HalContext &context);
+
+        /**
+         * Verifies if the context is initialized.
+         */
+        operator bool_type() const;
+
+        /**
+         */
+        void
+        connect();
+
+        /**
+         * Retrieve UDI's of all devices.
+         * \return Vector with device UDI's.
+         */
+        std::vector<std::string>
+        getAllDevices() const;
+
+        /**
+         * Construct a HalDrive object for the specified UDI.
+         * \param  The \p udi of the drive.
+         * \return The HalDrive object.
+         */
+        HalDrive
+        getDriveFromUDI(const std::string &udi) const;
+
+        /**
+         * Construct a HalVolume object for the specified UDI.
+         * \param  The \p udi of the volume.
+         * \return The HalVolume object.
+         */
+        HalVolume
+        getVolumeFromUDI(const std::string &udi) const;
+
+        HalVolume
+        getVolumeFromDeviceFile(const std::string &device_file) const;
+
+        /**
+         * Retrieve UDI's of all devices with a capability.
+         * \param  The \p capability name
+         * \return Vector with device UDI's.
+         */
+        std::vector<std::string>
+        findDevicesByCapability(const std::string &capability) const;
+
+        bool
+        getDevicePropertyBool  (const std::string &udi,
+                                const std::string &key) const;
+
+        int32_t
+        getDevicePropertyInt32 (const std::string &udi,
+                                const std::string &key) const;
+
+        uint64_t
+        getDevicePropertyUInt64(const std::string &udi,
+                                const std::string &key) const;
+
+        double
+        getDevicePropertyDouble(const std::string &udi,
+                                const std::string &key) const;
+
+        std::string
+        getDevicePropertyString(const std::string &udi,
+                                const std::string &key) const;
+
+        void
+        setDevicePropertyBool  (const std::string &udi,
+                                const std::string &key,
+                                bool               value);
+
+        void
+        setDevicePropertyInt32 (const std::string &udi,
+                                const std::string &key,
+                                int32_t            value);
+
+        void
+        setDevicePropertyUInt64(const std::string &udi,
+                                const std::string &key,
+                                uint64_t           value);
+
+        void
+        setDevicePropertyDouble(const std::string &udi,
+                                const std::string &key,
+                                double             value);
+
+        void
+        setDevicePropertyString(const std::string &udi,
+                                const std::string &key,
+                                const std::string &value);
+
+        void
+        removeDeviceProperty(const std::string &udi,
+                             const std::string &key);
+
+      private:
+
+        zypp::RW_pointer<HalContext_Impl> h_impl;
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+      //
+      // CLASS NAME : HalDrive
+      //
+      /** Hardware abstaction layer storage drive object.
+       *
+       * @todo: wrap more functions.
+       */
+      class HalDrive
+      {
+      public:
+        typedef
+        zypp::RW_pointer<HalDrive_Impl>::unspecified_bool_type    bool_type;
+
+        HalDrive();
+        HalDrive(const HalDrive &drive);
+        ~HalDrive();
+
+        HalDrive&
+        operator=(const HalDrive &drive);
+
+        operator bool_type() const;
+
+        std::string
+        getUDI() const;
+
+        std::string
+        getTypeName() const;
+
+        /**
+         * \return The drive's device file name.
+         */
+        std::string
+        getDeviceFile() const;
+
+        /**
+         * \return The drive's device file major number.
+         */
+        unsigned int
+        getDeviceMajor() const;
+
+        /**
+         * \return The drive's device minor number.
+         */
+        unsigned int
+        getDeviceMinor() const;
+
+        /**
+         * \return True, if drive uses removable media.
+         */
+        bool
+        usesRemovableMedia() const;
+
+        /*
+        ** Returns the media type names supported by the drive.
+        **
+        ** Since hal does not implement a textual form here, we
+        ** are using the drive type and property names from
+        ** "storage.cdrom.*" namespace:
+        **   cdrom, cdr, cdrw, dvd, dvdr, dvdrw, dvdram,
+        **   dvdplusr, dvdplusrw, dvdplusrdl
+        **
+        ** FIXME: Should we provide own LibHalDriveCdromCaps?
+        */
+        std::vector<std::string>
+        getCdromCapabilityNames() const;
+
+        /**
+         * Retrieve UDI's of all volumes of this drive.
+         * \return Vector with volume UDI's.
+         */
+        std::vector<std::string>
+        findAllVolumes() const;
+
+      private:
+        friend class HalContext;
+
+        HalDrive(HalDrive_Impl *impl);
+
+        zypp::RW_pointer<HalDrive_Impl>   d_impl;
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+      //
+      // CLASS NAME : HalVolume
+      //
+      /** Hardware abstaction layer storage volume object.
+       *
+       * @todo: wrap more functions.
+       */
+      class HalVolume
+      {
+      public:
+        typedef
+        zypp::RW_pointer<HalVolume_Impl>::unspecified_bool_type   bool_type;
+
+        HalVolume();
+        HalVolume(const HalVolume &volume);
+        ~HalVolume();
+
+        HalVolume&
+        operator=(const HalVolume &volume);
+
+        operator bool_type() const;
+
+        std::string
+        getUDI() const;
+
+        /**
+         * \return The Volume drive's device file name.
+         */
+        std::string
+        getDeviceFile() const;
+
+        /**
+         * \return The Volume drive's device major number.
+         */
+        unsigned int
+        getDeviceMajor() const;
+
+        /**
+         * \return The Volume drive's device minor number.
+         */
+        unsigned int
+        getDeviceMinor() const;
+
+        bool
+        isDisc() const;
+
+        bool
+        isPartition() const;
+
+        bool
+        isMounted() const;
+
+        /**
+         * \return The filesystem name on the volume.
+         */
+        std::string
+        getFSType() const;
+
+        /**
+         * \return The filesystem usage purpose.
+         */
+        std::string
+        getFSUsage() const;
+
+        /**
+         * \return The mount point of the volume.
+         */
+        std::string
+        getMountPoint() const;
+
+      private:
+        friend class HalContext;
+        friend class HalDrive;
+        HalVolume(HalVolume_Impl *impl);
+
+        zypp::RW_pointer<HalVolume_Impl>  v_impl;
+      };
+
+
+      ////////////////////////////////////////////////////////////////
+    } // namespace hal
+    //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+  } // namespace target
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_TARGET_HAL_HALCONTEXT_H
+
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/target/hal/HalException.h b/zypp/target/hal/HalException.h
new file mode 100644 (file)
index 0000000..4b957b8
--- /dev/null
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/hal/HalException.h
+ *
+ *  \brief Hardware abstaction layer library wrapper.
+ */
+#ifndef ZYPP_TARGET_HAL_HALEXCEPTION_H
+#define ZYPP_TARGET_HAL_HALEXCEPTION_H
+
+#include <zypp/base/Exception.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/String.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace target
+  { //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+    namespace hal
+    { ////////////////////////////////////////////////////////////////
+
+
+      ////////////////////////////////////////////////////////////////
+      //
+      // CLASS NAME : HalException
+      //
+      /** Hardware abstaction layer exception.
+       * Just inherits Exception to separate hal exceptions.
+       */
+      class HalException: public zypp::Exception
+      {
+      public:
+        /** Default constructor.
+         * Use \ref ZYPP_THROW to throw exceptions.
+         */
+        HalException()
+          : zypp::Exception(_("Hal Exception"))
+        {}
+
+        /** Constructor taking complete hal error message.
+         * This constructor is used to generate custom error
+         * messages, in case, that no DBUS error is avaliable.
+         * Use \ref ZYPP_THROW to throw exceptions.
+         */
+        HalException(const std::string &msg_r)
+          : zypp::Exception(_("Hal Exception"))
+          , e_name()
+          , e_msg(msg_r)
+        {}
+
+        /** Constructor taking HAL (DBUS) error message components.
+         * Use \ref ZYPP_THROW to throw exceptions.
+         */
+        HalException(const std::string &err_name, const std::string &err_msg)
+          : zypp::Exception(_("Hal Exception"))
+          , e_name(err_name)
+          , e_msg(err_msg)
+        {}
+
+        /** Destructor.
+         */
+        virtual ~HalException() throw() {};
+
+        /**
+         * \return The HAL (DBUS) error name component.
+         */
+        const std::string & errorName() const
+        {
+          return e_name;
+        }
+
+        /**
+         * \return The HAL (DBUS) error message component.
+         */
+        const std::string & errorMessage() const
+        {
+          return e_msg;
+        }
+
+      protected:
+        virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+      private:
+        std::string e_name;
+        std::string e_msg;
+      };
+
+      /** Exception thrown if we were built without hal support (-DNO_HAL).
+       */
+      struct NoHalException: public Exception
+      { NoHalException(); };
+
+      ////////////////////////////////////////////////////////////////
+    } // namespace hal
+    //////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////
+  } // namespace target
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_TARGET_HAL_HALEXCEPTION_H
+
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/target/modalias/Modalias.cc b/zypp/target/modalias/Modalias.cc
new file mode 100644 (file)
index 0000000..a1b1605
--- /dev/null
@@ -0,0 +1,288 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/modalias/Modalias.cc
+ *
+*/
+extern "C"
+{
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fnmatch.h>
+}
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cerrno>
+
+#include <iostream>
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "MODALIAS"
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/modalias/Modalias.h"
+#include "zypp/PathInfo.h"
+
+
+using std::endl;
+using std::string;
+
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+struct modalias_list {
+       char *modalias;
+       struct modalias_list *next;
+};
+
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    { /////////////////////////////////////////////////////////////////
+
+/*
+ * For each file in the directory PATH other than . and .., call
+ * FUNC with the arguments PATH, the file's name, and ARG.
+ *
+ * If FUNC returns a non-zero return value, stop reading the directory
+ * and return that value. Returns -1 if an error occurs.
+ */
+
+int
+foreach_file_recursive(const char *path_rec, int (*func)(const char *, const char *, void *),
+            void *arg)
+{
+       DIR *dir;
+       struct dirent *dirent;
+       char path_tmp[PATH_MAX];
+       int ret = 0;
+
+       if (!(dir = opendir(path_rec)))
+               return -1;
+       while ((dirent = readdir(dir)) != NULL) {
+
+               if (strcmp(dirent->d_name, ".") == 0 ||
+                   strcmp(dirent->d_name, "..") == 0)
+                       continue;
+               snprintf(path_tmp, sizeof(path_tmp), "%s/%s", path_rec, dirent->d_name);
+
+               PathInfo path(path_tmp, PathInfo::LSTAT);
+
+               if (path.isLink ()) {
+                       continue;
+               }
+               if (path.isDir ()){
+                       (void) foreach_file_recursive(path_tmp, func, arg);
+               }else if (path.isFile ()){
+                       if ((ret = func(path_rec, dirent->d_name, arg)) != 0)
+                               break;
+               }else{
+                       continue;
+               }
+       }
+       if (closedir(dir) != 0)
+               return -1;
+       return ret;
+}
+
+/*
+ * If DIR/FILE/modalias exists, remember this modalias on the linked modalias list
+ * passed in in ARG. Never returns an error.
+ */
+int
+read_modalias(const char *dir, const char *file, void *arg)
+{
+       char path[PATH_MAX];
+       int fd;
+       ssize_t len;
+       char modalias[PATH_MAX];
+       struct modalias_list **list = (struct modalias_list **)arg, *entry;
+
+       if (strcmp(file, "modalias") != 0){
+               return 0;
+       }
+       snprintf(path, sizeof(path), "%s/%s", dir, file);
+       if ((fd = open(path, O_RDONLY)) == -1)
+               return 0;
+       len = read(fd, modalias, sizeof(modalias) - 1);
+       if (len < 0)
+               goto out;
+       while (len > 0 && modalias[len - 1] == '\n')
+               len--;
+       modalias[len] = 0;
+
+       if ((entry = (struct modalias_list *)malloc(sizeof(*entry))) == NULL)
+               goto out;
+       if ((entry->modalias = strdup(modalias)) == NULL) {
+               free(entry);
+               goto out;
+       }
+       entry->next = *list;
+       *list = entry;
+       XXX << "system modalias: " << entry->modalias << endl;
+
+out:
+       (void) close(fd);
+       return 0;
+}
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Modalias::Impl
+//
+/** Modalias implementation. */
+struct Modalias::Impl
+{
+    struct modalias_list *_modaliases;
+
+    /** Ctor. */
+    Impl()
+       : _modaliases(0)
+    {
+       const char *dir;
+       char path[PATH_MAX];
+
+       dir = getenv("ZYPP_MODALIAS_SYSFS");
+       if (!dir)
+               dir = "/sys";
+       DBG << "Using /sys directory : " << dir << endl;
+
+       snprintf(path, sizeof(path), "%s", dir);
+       foreach_file_recursive( path, read_modalias, &_modaliases );
+
+    }
+
+    /** Dtor. */
+    ~Impl()
+    {
+       while (_modaliases != NULL) {
+           struct modalias_list *l = _modaliases;
+           _modaliases = _modaliases->next;
+           free(l->modalias);
+           free(l);
+       }
+    }
+
+    /*
+     * Check if a device on the system matches a modalias PATTERN.
+     *
+     * Returns NULL if no matching device is found, and the modalias
+     * of the first matching device otherwise. (More than one device
+     * may match a given pattern.)
+     *
+     * On a system that has the following device,
+     *
+     *   pci:v00008086d0000265Asv00008086sd00004556bc0Csc03i00
+     *
+     * modalias_matches("pci:v00008086d0000265Asv*sd*bc*sc*i*") will
+     * return a non-NULL value.
+     */
+    bool query( const char * cap_r ) const
+    {
+        if ( cap_r )
+        {
+          struct modalias_list *l;
+          for (l = _modaliases; l; l = l->next) {
+            if ( fnmatch( cap_r, l->modalias, 0 ) == 0 )
+              return true;
+          }
+        }
+        return false;
+    }
+
+  public:
+     /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+       static shared_ptr<Impl> _nullimpl( new Impl );
+       return _nullimpl;
+    }
+
+};  // struct Modalias::Impl
+
+///////////////////////////////////////////////////////////////////
+
+/** \relates Modalias::Impl Stream output
+     * And maybe std::ostream & operator<< Modalias::Impl below too.
+     * return libhal version or something like that.
+ */
+inline std::ostream & operator<<( std::ostream & str, const Modalias::Impl & obj )
+{
+  return str << "Modalias::Impl";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Modalias
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : Modalias::Modalias
+//     METHOD TYPE : Ctor
+//
+Modalias::Modalias()
+: _pimpl( Impl::nullimpl() )
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : Modalias::~Modalias
+//     METHOD TYPE : Dtor
+//
+Modalias::~Modalias()
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : Modalias::instance
+//     METHOD TYPE : Modalias &
+//
+Modalias & Modalias::instance()
+{
+  static Modalias _singleton;
+  return _singleton;
+}
+
+///////////////////////////////////////////////////////////////////
+// Foreward to implenemtation
+///////////////////////////////////////////////////////////////////
+
+bool Modalias::query( const char * cap_r ) const
+{ return _pimpl->query( cap_r ); }
+
+/******************************************************************
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : std::ostream &
+*/
+std::ostream & operator<<( std::ostream & str, const Modalias & obj )
+{
+  return str << *obj._pimpl;
+}
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/target/modalias/Modalias.h b/zypp/target/modalias/Modalias.h
new file mode 100644 (file)
index 0000000..35688bb
--- /dev/null
@@ -0,0 +1,92 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/modalias/Modalias.h
+ *
+*/
+#ifndef ZYPP_TARGET_MODALIAS_MODALIAS_H
+#define ZYPP_TARGET_MODALIAS_MODALIAS_H
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/IdString.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Modalias
+    //
+    /** Hardware abstaction layer singleton.
+     */
+    class Modalias
+    {
+      friend std::ostream & operator<<( std::ostream & str, const Modalias & obj );
+
+      public:
+        /** Implementation  */
+        class Impl;
+
+      public:
+        /** Singleton access. */
+        static Modalias & instance();
+
+        /** Dtor */
+        ~Modalias();
+
+      public:
+
+        /** Checks if a device on the system matches a modalias pattern.
+         *
+         * Returns \c false if no matching device is found, and the modalias
+         * of the first matching device otherwise. (More than one device
+         * may match a given pattern.)
+         *
+         * On a system that has the following device,
+         * \code
+         *   pci:v00008086d0000265Asv00008086sd00004556bc0Csc03i00
+         * \endcode
+         * the following query will return \c true:
+         * \code
+         *   modalias_matches("pci:v00008086d0000265Asv*sd*bc*sc*i*")
+         * \endcode
+         */
+        bool query( IdString cap_r ) const
+        { return query( cap_r.c_str() ); }
+        /** \overload */
+        bool query( const char * cap_r ) const;
+        /** \overload */
+        bool query( const std::string & cap_r ) const
+        { return query( cap_r.c_str() ); }
+
+      private:
+        /** Singleton ctor. */
+        Modalias();
+
+        /** Pointer to implementation */
+        RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Modalias Stream output */
+    std::ostream & operator<<( std::ostream & str, const Modalias & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_MODALIAS_MODALIAS_H
diff --git a/zypp/target/rpm/BinHeader.cc b/zypp/target/rpm/BinHeader.cc
new file mode 100644 (file)
index 0000000..8ee23e0
--- /dev/null
@@ -0,0 +1,410 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.cc
+ *
+*/
+#include "librpm.h"
+extern "C"
+{
+#ifdef _RPM_5
+#undef RPM_NULL_TYPE
+#define RPM_NULL_TYPE rpmTagType(0)
+typedef rpmuint32_t rpm_count_t;
+#else
+#ifdef _RPM_4_4
+typedef int32_t rpm_count_t;
+#endif
+#endif
+}
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/NonCopyable.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+
+using namespace std;
+
+#undef Y2LOG
+#define Y2LOG "BinHeader"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+  /** Helper for header data retieval.
+   * With \c _RPM_4_X use \c ::headerGet; with older \c _RPM_4_4
+   * use the meanwhile deprecated \c ::headerGetEntry.
+   * \ingroup g_RAII
+   */
+  struct HeaderEntryGetter : private base::NonCopyable
+  {
+    public:
+      HeaderEntryGetter( const Header & h_r, rpmTag & tag_r );
+      ~HeaderEntryGetter();
+      rpmTagType  type();
+      rpm_count_t cnt();
+      void *      val();
+    private:
+#ifdef _RPM_4_X
+     ::rpmtd           _rpmtd;
+#else
+      rpmTagType       _type;
+      rpm_count_t      _cnt;
+      void *           _val;
+#endif //_RPM_4_X
+ };
+
+#ifdef _RPM_4_X
+  inline HeaderEntryGetter::HeaderEntryGetter( const Header & h_r, rpmTag & tag_r )
+    : _rpmtd( ::rpmtdNew() )
+  { ::headerGet( h_r, tag_r, _rpmtd, HEADERGET_DEFAULT ); }
+  inline HeaderEntryGetter::~HeaderEntryGetter()
+  { ::rpmtdFreeData( _rpmtd ); ::rpmtdFree( _rpmtd ); }
+  inline rpmTagType    HeaderEntryGetter::type()       { return rpmtdType( _rpmtd ); }
+  inline rpm_count_t   HeaderEntryGetter::cnt()        { return _rpmtd->count; }
+  inline void *                HeaderEntryGetter::val()        { return _rpmtd->data; }
+#else
+  inline HeaderEntryGetter::HeaderEntryGetter( const Header & h_r, rpmTag & tag_r )
+    : _type( RPM_NULL_TYPE )
+    , _cnt( 0 )
+    , _val( 0 )
+  { ::headerGetEntry( h_r, tag_r, hTYP_t(&_type), &_val, &_cnt ); }
+  inline HeaderEntryGetter::~HeaderEntryGetter()
+  { if ( _val && _type == RPM_STRING_ARRAY_TYPE ) free( _val ); }
+  inline rpmTagType    HeaderEntryGetter::type()       { return _type; }
+  inline rpm_count_t   HeaderEntryGetter::cnt()        { return _cnt; }
+  inline void *                HeaderEntryGetter::val()        { return _val; }
+#endif //_RPM_4_X
+
+///////////////////////////////////////////////////////////////////
+//
+//        CLASS NAME : BinHeader::intList
+//
+///////////////////////////////////////////////////////////////////
+
+unsigned BinHeader::intList::set( void * val_r, unsigned cnt_r, rpmTagType type_r )
+{
+  if ( val_r )
+    switch ( _type )
+    {
+#if RPM_CHAR_TYPE != RPM_INT8_TYPE
+      case RPM_CHAR_TYPE:
+       std::vector<long>( (char*)val_r, ((char*)val_r)+cnt_r ).swap( _data );
+       break;
+#endif
+      case RPM_INT8_TYPE:
+       std::vector<long>( (int8_t*)val_r, ((int8_t*)val_r)+cnt_r ).swap( _data );
+       break;
+      case RPM_INT16_TYPE:
+       std::vector<long>( (int16_t*)val_r, ((int16_t*)val_r)+cnt_r ).swap( _data );
+       break;
+      case RPM_INT32_TYPE:
+       std::vector<long>( (int32_t*)val_r, ((int32_t*)val_r)+cnt_r ).swap( _data );
+       break;
+       #ifdef _RPM_4_X
+      case RPM_INT64_TYPE:
+       std::vector<long>( (int64_t*)val_r, ((int64_t*)val_r)+cnt_r ).swap( _data );
+       break;
+       #endif
+      default:
+       std::vector<long>( cnt_r, 0L ).swap( _data );
+       break;
+    }
+  else
+    _data.clear();
+  return _data.size();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//        CLASS NAME : BinHeader::stringList
+//
+///////////////////////////////////////////////////////////////////
+
+unsigned BinHeader::stringList::set( char ** val_r, unsigned cnt_r )
+{
+  if ( val_r )
+    std::vector<std::string>( val_r, val_r+cnt_r ).swap( _data );
+  else
+    _data.clear();
+  return _data.size();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//        CLASS NAME : BinHeader
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::BinHeader
+//        METHOD TYPE : Constructor
+//
+BinHeader::BinHeader( Header h_r )
+    : _h( h_r )
+{
+  if ( _h )
+  {
+    headerLink( _h );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::BinHeader
+//        METHOD TYPE : Constructor
+//
+BinHeader::BinHeader( BinHeader::Ptr & rhs )
+{
+  INT << "INJECT from " << rhs;
+  if ( ! (rhs && rhs->_h) )
+  {
+    _h = 0;
+  }
+  else
+  {
+    _h = rhs->_h;  // ::headerLink already done in rhs
+    rhs->_h = 0;
+  }
+  INT << ": " << *this << "   (" << rhs << ")" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::~BinHeader
+//        METHOD TYPE : Destructor
+//
+BinHeader::~BinHeader()
+{
+  if ( _h )
+  {
+    headerFree( _h );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::assertHeader
+//        METHOD TYPE : void
+//
+bool BinHeader::assertHeader()
+{
+  if ( !_h )
+  {
+    _h = ::headerNew();
+    if ( !_h )
+    {
+      INT << "OOPS: NULL HEADER created!" << endl;
+      return false;
+    }
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::has_tag
+//        METHOD TYPE : bool
+//
+//        DESCRIPTION :
+//
+bool BinHeader::has_tag( tag tag_r ) const
+{
+  return( !empty() && ::headerIsEntry( _h, tag_r ) );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::int_list
+//        METHOD TYPE : unsigned
+//
+//        DESCRIPTION :
+//
+unsigned BinHeader::int_list( tag tag_r, intList & lst_r ) const
+{
+  if ( !empty() )
+  {
+    HeaderEntryGetter headerget( _h, tag_r );
+
+    if ( headerget.val() )
+    {
+      switch ( headerget.type() )
+      {
+      case RPM_NULL_TYPE:
+        return lst_r.set( 0, 0, headerget.type() );
+#if RPM_CHAR_TYPE != RPM_INT8_TYPE
+      case RPM_CHAR_TYPE:
+#endif
+      case RPM_INT8_TYPE:
+      case RPM_INT16_TYPE:
+      case RPM_INT32_TYPE:
+        return lst_r.set( headerget.val(), headerget.cnt(), headerget.type() );
+
+      default:
+        INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << headerget.type() << endl;
+      }
+    }
+  }
+  return lst_r.set( 0, 0, RPM_NULL_TYPE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::string_list
+//        METHOD TYPE : unsigned
+//
+//        DESCRIPTION :
+//
+unsigned BinHeader::string_list( tag tag_r, stringList & lst_r ) const
+{
+  if ( !empty() )
+  {
+    HeaderEntryGetter headerget( _h, tag_r );
+
+    if ( headerget.val() )
+    {
+      switch ( headerget.type() )
+      {
+      case RPM_NULL_TYPE:
+        return lst_r.set( 0, 0 );
+      case RPM_STRING_ARRAY_TYPE:
+        return lst_r.set( (char**)headerget.val(), headerget.cnt() );
+
+      default:
+        INT << "RPM_TAG MISSMATCH: RPM_STRING_ARRAY_TYPE " << tag_r << " got type " << headerget.type() << endl;
+      }
+    }
+  }
+  return lst_r.set( 0, 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::int_val
+//        METHOD TYPE : int
+//
+//        DESCRIPTION :
+//
+int BinHeader::int_val( tag tag_r ) const
+{
+  if ( !empty() )
+  {
+    HeaderEntryGetter headerget( _h, tag_r );
+
+    if ( headerget.val() )
+    {
+      switch ( headerget.type() )
+      {
+      case RPM_NULL_TYPE:
+        return 0;
+#if RPM_CHAR_TYPE != RPM_INT8_TYPE
+      case RPM_CHAR_TYPE:
+        return *((char*)headerget.val());
+#endif
+      case RPM_INT8_TYPE:
+        return *((int8_t*)headerget.val());
+      case RPM_INT16_TYPE:
+        return *((int16_t*)headerget.val());
+      case RPM_INT32_TYPE:
+        return *((int32_t*)headerget.val());
+
+      default:
+        INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << headerget.type() << endl;
+      }
+    }
+  }
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::string_val
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string BinHeader::string_val( tag tag_r ) const
+{
+  if ( !empty() )
+  {
+    HeaderEntryGetter headerget( _h, tag_r );
+
+    if ( headerget.val() )
+    {
+      switch ( headerget.type() )
+      {
+      case RPM_NULL_TYPE:
+        return "";
+      case RPM_STRING_TYPE:
+        return (char*)headerget.val();
+
+     default:
+        INT << "RPM_TAG MISSMATCH: RPM_STRING_TYPE " << tag_r << " got type " << headerget.type() << endl;
+      }
+    }
+  }
+  return "";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : BinHeader::stringList_val
+//        METHOD TYPE : std::list<std::string>
+//
+//        DESCRIPTION :
+//
+std::list<std::string> BinHeader::stringList_val( tag tag_r ) const
+{
+  std::list<std::string> ret;
+
+  if ( !empty() )
+  {
+    stringList lines;
+    unsigned count = string_list( tag_r, lines );
+    for ( unsigned i = 0; i < count; ++i )
+    {
+      ret.push_back( lines[i] );
+    }
+  }
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//      METHOD NAME : BinHeader::dumpOn
+//      METHOD TYPE : ostream &
+//
+//      DESCRIPTION :
+//
+ostream & BinHeader::dumpOn( ostream & str ) const
+{
+  ReferenceCounted::dumpOn( str );
+  return str << '{' << (void*)_h << '}';
+}
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/BinHeader.h b/zypp/target/rpm/BinHeader.h
new file mode 100644 (file)
index 0000000..1dc636c
--- /dev/null
@@ -0,0 +1,168 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_BINHEADER_H
+#define ZYPP_TARGET_RPM_BINHEADER_H
+
+extern "C"
+{
+#include <stdint.h>
+}
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <list>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/target/rpm/librpm.h"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : BinHeader
+/**
+ *
+ **/
+class BinHeader : public base::ReferenceCounted, private base::NonCopyable
+{
+
+public:
+
+  typedef intrusive_ptr<BinHeader> Ptr;
+
+  typedef intrusive_ptr<const BinHeader> constPtr;
+
+  typedef rpmTag tag;
+
+  class intList;
+
+  class stringList;
+
+private:
+
+  Header _h;
+
+  bool assertHeader();
+
+public:
+
+  BinHeader( Header h_r = 0 );
+
+  /**
+   * <B>Dangerous!<\B> This one takes the header out of rhs
+   * and leaves rhs empty.
+   **/
+  BinHeader( BinHeader::Ptr & rhs );
+
+  virtual ~BinHeader();
+
+public:
+
+  bool empty() const
+  {
+    return( _h == NULL );
+  }
+
+  bool has_tag( tag tag_r ) const;
+
+  unsigned int_list( tag tag_r, intList & lst_r ) const;
+
+  unsigned string_list( tag tag_r, stringList & lst_r ) const;
+
+  int int_val( tag tag_r ) const;
+
+  std::string string_val( tag tag_r ) const;
+
+public:
+
+  std::list<std::string> stringList_val( tag tag_r ) const;
+
+public:
+
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : BinHeader::intList
+/**
+ *
+ **/
+class BinHeader::intList : private base::NonCopyable
+{
+  public:
+    intList()
+      : _type( RPM_NULL_TYPE )
+    {}
+
+    bool empty() const
+    { return _data.empty(); }
+
+    unsigned size() const
+    { return _data.size(); }
+
+    long operator[]( const unsigned idx_r ) const
+    { return idx_r < _data.size() ? _data[idx_r] : 0; }
+
+  private:
+    friend class BinHeader;
+    unsigned set( void * val_r, unsigned cnt_r, rpmTagType type_r );
+
+  private:
+    std::vector<long> _data;
+    rpmTagType _type;
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : BinHeader::stringList
+/**
+ *
+ **/
+class BinHeader::stringList : private base::NonCopyable
+{
+  public:
+    bool empty() const
+    { return _data.empty(); }
+
+    unsigned size() const
+    { return _data.size(); }
+
+    std::string operator[]( const unsigned idx_r ) const
+    { return idx_r < _data.size() ? _data[idx_r] : std::string(); }
+
+  private:
+    friend class BinHeader;
+    unsigned set( char ** val_r, unsigned cnt_r );
+
+  private:
+    std::vector<std::string> _data;
+};
+
+///////////////////////////////////////////////////////////////////
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_BINHEADER_H
diff --git a/zypp/target/rpm/RpmCallbacks.cc b/zypp/target/rpm/RpmCallbacks.cc
new file mode 100644 (file)
index 0000000..691ded3
--- /dev/null
@@ -0,0 +1,17 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmCallbacks.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/target/rpm/RpmCallbacks.h"
+
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/rpm/RpmCallbacks.h b/zypp/target/rpm/RpmCallbacks.h
new file mode 100644 (file)
index 0000000..6fd0071
--- /dev/null
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmCallbacks.h
+ *
+*/
+
+#ifndef ZYPP_TARGET_RPM_RPMCALLBACKS_H
+#define ZYPP_TARGET_RPM_RPMCALLBACKS_H
+
+#include <iosfwd>
+
+#include "zypp/Url.h"
+#include "zypp/Callback.h"
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+///////////////////////////////////////////////////////////////////
+// Reporting progress of package removing
+///////////////////////////////////////////////////////////////////
+struct RpmRemoveReport : public callback::ReportBase
+{
+
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,   // retry
+    IGNORE   // ignore
+  };
+
+  /** Start the operation */
+  virtual void start( const std::string & name )
+  {}
+  /**
+   * Inform about progress
+   * Return true on abort
+   */
+  virtual bool progress( unsigned percent )
+  { return false; }
+
+  virtual Action problem( Exception & excpt_r )
+  { return ABORT; }
+
+  /** Additional rpm output to be reported in \ref finish in case of success. */
+  virtual void finishInfo( const std::string & info_r )
+  {}
+
+  /** Finish operation in case of success */
+  virtual void finish()
+  {}
+  /** Finish operation in case of fail, report fail exception */
+  virtual void finish( Exception & excpt_r )
+  {}
+};
+
+///////////////////////////////////////////////////////////////////
+// Reporting progress of package installation
+///////////////////////////////////////////////////////////////////
+struct RpmInstallReport : public callback::ReportBase
+{
+
+  enum Action {
+    ABORT,  // abort and return error
+    RETRY,   // retry
+    IGNORE   // ignore
+  };
+
+  /** Start the operation */
+  virtual void start( const Pathname & name )
+  {}
+  /**
+   * Inform about progress
+   * Return false on abort
+   */
+  virtual bool progress( unsigned percent )
+  { return true; }
+
+  /** Additional rpm output to be reported in \ref finish in case of success. */
+  virtual void finishInfo( const std::string & info_r )
+  {}
+
+  /** Finish operation in case of success */
+  virtual void finish()
+  {}
+
+  virtual Action problem( Exception & excpt_r )
+  { return ABORT; }
+
+  /** Finish operation in case of fail, report fail exception */
+  virtual void finish( Exception & excpt_r )
+  {}
+};
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMCALLBACKS_H
diff --git a/zypp/target/rpm/RpmDb.cc b/zypp/target/rpm/RpmDb.cc
new file mode 100644 (file)
index 0000000..a76ddac
--- /dev/null
@@ -0,0 +1,2144 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmDb.cc
+ *
+*/
+#include "librpm.h"
+
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include <boost/format.hpp>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+
+#include "zypp/Date.h"
+#include "zypp/Pathname.h"
+#include "zypp/PathInfo.h"
+#include "zypp/PublicKey.h"
+
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/target/rpm/RpmCallbacks.h"
+
+#include "zypp/HistoryLog.h"
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmException.h"
+#include "zypp/TmpPath.h"
+#include "zypp/KeyRing.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ZConfig.h"
+
+using namespace std;
+using namespace zypp::filesystem;
+
+#define WARNINGMAILPATH                "/var/log/YaST2/"
+#define FILEFORBACKUPFILES     "YaSTBackupModifiedFiles"
+#define MAXRPMMESSAGELINES     10000
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+namespace
+{
+#if 1 // No more need to escape whitespace since rpm-4.4.2.3
+const char* quoteInFilename_m = "\'\"";
+#else
+const char* quoteInFilename_m = " \t\'\"";
+#endif
+inline string rpmQuoteFilename( const Pathname & path_r )
+{
+  string path( path_r.asString() );
+  for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
+        pos != string::npos;
+        pos = path.find_first_of( quoteInFilename_m, pos ) )
+  {
+    path.insert( pos, "\\" );
+    pos += 2; // skip '\\' and the quoted char.
+  }
+  return path;
+}
+}
+
+struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
+{
+  KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
+  {
+    connect();
+  }
+
+  ~KeyRingSignalReceiver()
+  {
+    disconnect();
+  }
+
+  virtual void trustedKeyAdded( const PublicKey &key )
+  {
+    MIL << "trusted key added to zypp Keyring. Importing" << endl;
+    // now import the key in rpm
+    try
+    {
+      _rpmdb.importPubkey( key );
+    }
+    catch (RpmException &e)
+    {
+      ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
+    }
+  }
+
+  virtual void trustedKeyRemoved( const PublicKey &key  )
+  {
+    MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
+
+    // remove the key from rpm
+    try
+    {
+      _rpmdb.removePubkey( key );
+    }
+    catch (RpmException &e)
+    {
+      ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
+    }
+  }
+
+  RpmDb &_rpmdb;
+};
+
+static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
+
+unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
+{
+  const char* argv[] =
+    {
+      "diff",
+      "-u",
+      file1.c_str(),
+      file2.c_str(),
+      NULL
+    };
+  ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+  //if(!prog)
+  //return 2;
+
+  string line;
+  int count = 0;
+  for (line = prog.receiveLine(), count=0;
+       !line.empty();
+       line = prog.receiveLine(), count++ )
+  {
+    if (maxlines<0?true:count<maxlines)
+      out+=line;
+  }
+
+  return prog.close();
+}
+
+
+
+/******************************************************************
+ **
+ **
+ **    FUNCTION NAME : stringPath
+ **    FUNCTION TYPE : inline string
+*/
+inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
+{
+  return librpmDb::stringPath( root_r, sub_r );
+}
+
+/******************************************************************
+ **
+ **
+ **    FUNCTION NAME : operator<<
+ **    FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
+{
+  if ( obj == RpmDb::DbSI_NO_INIT )
+  {
+    str << "NO_INIT";
+  }
+  else
+  {
+#define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << ")";
+#undef ENUM_OUT
+  }
+  return str;
+}
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmDb
+//
+///////////////////////////////////////////////////////////////////
+
+#define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::RpmDb
+//     METHOD TYPE : Constructor
+//
+RpmDb::RpmDb()
+    : _dbStateInfo( DbSI_NO_INIT )
+#warning Check for obsolete memebers
+    , _backuppath ("/var/adm/backup")
+    , _packagebackups(false)
+    , _warndirexists(false)
+{
+  process = 0;
+  exit_code = -1;
+  librpmDb::globalInit();
+  // Some rpm versions are patched not to abort installation if
+  // symlink creation failed.
+  setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
+  sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::~RpmDb
+//     METHOD TYPE : Destructor
+//
+RpmDb::~RpmDb()
+{
+  MIL << "~RpmDb()" << endl;
+  closeDatabase();
+  delete process;
+  MIL  << "~RpmDb() end" << endl;
+  sKeyRingReceiver.reset();
+}
+
+Date RpmDb::timestamp() const
+{
+  Date ts_rpm;
+
+  Pathname db_path;
+  if ( dbPath().empty() )
+    db_path = "/var/lib/rpm";
+  else
+    db_path = dbPath();
+
+  PathInfo rpmdb_info(root() + db_path + "/Packages");
+
+  if ( rpmdb_info.isExist() )
+    return rpmdb_info.mtime();
+  else
+    return Date::now();
+}
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::dumpOn
+//     METHOD TYPE : ostream &
+//
+ostream & RpmDb::dumpOn( ostream & str ) const
+{
+  str << "RpmDb[";
+
+  if ( _dbStateInfo == DbSI_NO_INIT )
+  {
+    str << "NO_INIT";
+  }
+  else
+  {
+#define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << "): " << stringPath( _root, _dbPath );
+#undef ENUM_OUT
+  }
+  return str << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::initDatabase
+//     METHOD TYPE : PMError
+//
+void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
+{
+  ///////////////////////////////////////////////////////////////////
+  // Check arguments
+  ///////////////////////////////////////////////////////////////////
+  bool quickinit( root_r.empty() );
+
+  if ( root_r.empty() )
+    root_r = "/";
+
+  if ( dbPath_r.empty() )
+    dbPath_r = "/var/lib/rpm";
+
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+  {
+    ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+
+  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
+      << ( doRebuild_r ? " (rebuilddb)" : "" )
+      << ( quickinit ? " (quickinit)" : "" ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether already initialized
+  ///////////////////////////////////////////////////////////////////
+  if ( initialized() )
+  {
+    if ( root_r == _root && dbPath_r == _dbPath )
+    {
+      return;
+    }
+    else
+    {
+      ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // init database
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::unblockAccess();
+
+  if ( quickinit )
+  {
+    MIL << "QUICK initDatabase (no systemRoot set)" << endl;
+    return;
+  }
+
+  DbStateInfoBits info = DbSI_NO_INIT;
+  try
+  {
+    internal_initDatabase( root_r, dbPath_r, info );
+  }
+  catch (const RpmException & excpt_r)
+  {
+    ZYPP_CAUGHT(excpt_r);
+    librpmDb::blockAccess();
+    ERR << "Cleanup on error: state " << info << endl;
+
+    if ( dbsi_has( info, DbSI_MADE_V4 ) )
+    {
+      // remove the newly created rpm4 database and
+      // any backup created on conversion.
+      removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+    }
+    ZYPP_RETHROW(excpt_r);
+  }
+  if ( dbsi_has( info, DbSI_HAVE_V3 ) )
+  {
+    if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
+    {
+      // Move obsolete rpm3 database beside.
+      MIL << "Cleanup: state " << info << endl;
+      removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+      dbsi_clr( info, DbSI_HAVE_V3 );
+    }
+    else
+    {
+      // Performing an update: Keep the original rpm3 database
+      // and wait if the rpm4 database gets modified by installing
+      // or removing packages. Cleanup in modifyDatabase or closeDatabase.
+      MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
+    }
+  }
+#warning CHECK: notify root about conversion backup.
+
+  _root   = root_r;
+  _dbPath = dbPath_r;
+  _dbStateInfo = info;
+
+  if ( doRebuild_r )
+  {
+    if (      dbsi_has( info, DbSI_HAVE_V4 )
+         && ! dbsi_has( info, DbSI_MADE_V4 ) )
+    {
+      rebuildDatabase();
+    }
+  }
+
+  MIL << "Syncronizing keys with zypp keyring" << endl;
+  // we do this one by one now.
+  importZyppKeyRingTrustedKeys();
+  exportTrustedKeysInZyppKeyRing();
+
+  // Close the database in case any write acces (create/convert)
+  // happened during init. This should drop any lock acquired
+  // by librpm. On demand it will be reopened readonly and should
+  // not hold any lock.
+  librpmDb::dbRelease( true );
+
+  MIL << "InitDatabase: " << *this << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::internal_initDatabase
+//     METHOD TYPE : PMError
+//
+void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+                                   DbStateInfoBits & info_r )
+{
+  info_r = DbSI_NO_INIT;
+
+  ///////////////////////////////////////////////////////////////////
+  // Get info about the desired database dir
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
+
+  if ( dbInfo.illegalArgs() )
+  {
+    // should not happen (checked in initDatabase)
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+  if ( ! dbInfo.usableArgs() )
+  {
+    ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+
+  if ( dbInfo.hasDbV4() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V4 );
+    MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
+  }
+  else
+  {
+    MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
+  }
+
+  if ( dbInfo.hasDbV3() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V3 );
+  }
+  if ( dbInfo.hasDbV3ToV4() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
+  }
+
+  DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Access database, create if needed
+  ///////////////////////////////////////////////////////////////////
+
+  // creates dbdir and empty rpm4 database if not present
+  librpmDb::dbAccess( root_r, dbPath_r );
+
+  if ( ! dbInfo.hasDbV4() )
+  {
+    dbInfo.restat();
+    if ( dbInfo.hasDbV4() )
+    {
+      dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
+    }
+  }
+
+  DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether to convert something. Create backup but do
+  // not remove anything here
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::constPtr dbptr;
+  librpmDb::dbAccess( dbptr );
+  bool dbEmpty = dbptr->empty();
+  if ( dbEmpty )
+  {
+    MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
+  }
+
+  if ( dbInfo.hasDbV3() )
+  {
+    MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
+
+    if ( dbEmpty )
+    {
+      extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
+      convertV3toV4( dbInfo.dbV3().path(), dbptr );
+
+      // create a backup copy
+      int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
+      if ( res )
+      {
+        WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
+      }
+      else
+      {
+        dbInfo.restat();
+        if ( dbInfo.hasDbV3ToV4() )
+        {
+          MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
+          dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
+        }
+      }
+
+    }
+    else
+    {
+
+      WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
+      // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
+      dbsi_set( info_r, DbSI_MODIFIED_V4 );
+
+    }
+
+    DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+    librpmDb::dumpState( DBG ) << endl;
+  }
+
+  if ( dbInfo.hasDbV3ToV4() )
+  {
+    MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removeV4
+//     METHOD TYPE : void
+//
+void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * v3backup = "packages.rpm3";
+  const char * master = "Packages";
+  const char * index[] =
+    {
+      "Basenames",
+      "Conflictname",
+      "Depends",
+      "Dirnames",
+      "Filemd5s",
+      "Group",
+      "Installtid",
+      "Name",
+      "Providename",
+      "Provideversion",
+      "Pubkeys",
+      "Requirename",
+      "Requireversion",
+      "Sha1header",
+      "Sigmd5",
+      "Triggername",
+      // last entry!
+      NULL
+    };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() )
+  {
+    ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
+    return;
+  }
+
+  for ( const char ** f = index; *f; ++f )
+  {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() )
+    {
+      filesystem::unlink( pi.path() );
+    }
+  }
+
+  pi( dbdir_r + master );
+  if ( pi.isFile() )
+  {
+    MIL << "Removing rpm4 database " << pi << endl;
+    filesystem::unlink( pi.path() );
+  }
+
+  if ( v3backup_r )
+  {
+    pi( dbdir_r + v3backup );
+    if ( pi.isFile() )
+    {
+      MIL << "Removing converted rpm3 database backup " << pi << endl;
+      filesystem::unlink( pi.path() );
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removeV3
+//     METHOD TYPE : void
+//
+void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * master = "packages.rpm";
+  const char * index[] =
+    {
+      "conflictsindex.rpm",
+      "fileindex.rpm",
+      "groupindex.rpm",
+      "nameindex.rpm",
+      "providesindex.rpm",
+      "requiredby.rpm",
+      "triggerindex.rpm",
+      // last entry!
+      NULL
+    };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() )
+  {
+    ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
+    return;
+  }
+
+  for ( const char ** f = index; *f; ++f )
+  {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() )
+    {
+      filesystem::unlink( pi.path() );
+    }
+  }
+
+#warning CHECK: compare vs existing v3 backup. notify root
+  pi( dbdir_r + master );
+  if ( pi.isFile() )
+  {
+    Pathname m( pi.path() );
+    if ( v3backup_r )
+    {
+      // backup was already created
+      filesystem::unlink( m );
+      Pathname b( m.extend( "3" ) );
+      pi( b ); // stat backup
+    }
+    else
+    {
+      Pathname b( m.extend( ".deleted" ) );
+      pi( b );
+      if ( pi.isFile() )
+      {
+        // rempve existing backup
+        filesystem::unlink( b );
+      }
+      filesystem::rename( m, b );
+      pi( b ); // stat backup
+    }
+    MIL << "(Re)moved rpm3 database to " << pi << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::modifyDatabase
+//     METHOD TYPE : void
+//
+void RpmDb::modifyDatabase()
+{
+  if ( ! initialized() )
+    return;
+
+  // tag database as modified
+  dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
+
+  // Move outdated rpm3 database beside.
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
+  {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::closeDatabase
+//     METHOD TYPE : PMError
+//
+void RpmDb::closeDatabase()
+{
+  if ( ! initialized() )
+  {
+    return;
+  }
+
+  MIL << "Calling closeDatabase: " << *this << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Block further database access
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::blockAccess();
+
+  ///////////////////////////////////////////////////////////////////
+  // Check fate if old version database still present
+  ///////////////////////////////////////////////////////////////////
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
+  {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
+    {
+      // Move outdated rpm3 database beside.
+      removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
+    }
+    else
+    {
+      // Remove unmodified rpm4 database
+      removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Uninit
+  ///////////////////////////////////////////////////////////////////
+  _root = _dbPath = Pathname();
+  _dbStateInfo = DbSI_NO_INIT;
+
+  MIL << "closeDatabase: " << *this << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::rebuildDatabase
+//     METHOD TYPE : PMError
+//
+void RpmDb::rebuildDatabase()
+{
+  callback::SendReport<RebuildDBReport> report;
+
+  report->start( root() + dbPath() );
+
+  try
+  {
+    doRebuildDatabase(report);
+  }
+  catch (RpmException & excpt_r)
+  {
+    report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
+    ZYPP_RETHROW(excpt_r);
+  }
+  report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
+}
+
+void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
+{
+  FAILIFNOTINITIALIZED;
+
+  MIL << "RpmDb::rebuildDatabase" << *this << endl;
+  // FIXME  Timecount _t( "RpmDb::rebuildDatabase" );
+
+  PathInfo dbMaster( root() + dbPath() + "Packages" );
+  PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
+
+  // run rpm
+  RpmArgVec opts;
+  opts.push_back("--rebuilddb");
+  opts.push_back("-vv");
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+  // progress report: watch this file growing
+  PathInfo newMaster( root()
+                      + dbPath().extend( str::form( "rebuilddb.%d",
+                                                    process?process->getpid():0) )
+                      + "Packages" );
+
+  string       line;
+  string       errmsg;
+
+  while ( systemReadLine( line ) )
+  {
+    if ( newMaster() )
+    { // file is removed at the end of rebuild.
+      // current size should be upper limit for new db
+      if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
+      {
+        WAR << "User requested abort." << endl;
+        systemKill();
+        filesystem::recursive_rmdir( newMaster.path().dirname() );
+      }
+    }
+
+    if ( line.compare( 0, 2, "D:" ) )
+    {
+      errmsg += line + '\n';
+      //      report.notify( line );
+      WAR << line << endl;
+    }
+  }
+
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 )
+  {
+    //TranslatorExplanation after semicolon is error message
+    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
+               (errmsg.empty() ? error_message: errmsg))));
+  }
+  else
+  {
+    report->progress( 100, root() + dbPath() ); // 100%
+  }
+}
+
+void RpmDb::importZyppKeyRingTrustedKeys()
+{
+  MIL << "Importing zypp trusted keyring" << std::endl;
+
+  std::list<PublicKey> zypp_keys;
+  zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
+  /* The pubkeys() call below is expensive.  It calls gpg2 for each
+     gpg-pubkey in the rpm db.  Useless if we don't have any keys in
+     zypp yet.  */
+  if (zypp_keys.empty())
+    return;
+
+  std::list<PublicKey> rpm_keys = pubkeys();
+  for_( it, zypp_keys.begin(), zypp_keys.end() )
+    {
+      // we find only the left part of the long gpg key, as rpm does not support long ids
+      std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
+      if ( ik != rpm_keys.end() )
+        {
+          MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
+        }
+      else
+        {
+          // now import the key in rpm
+          try
+            {
+              importPubkey( *it );
+              MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
+            }
+          catch (RpmException &e)
+            {
+              ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
+            }
+        }
+    }
+}
+
+void RpmDb::exportTrustedKeysInZyppKeyRing()
+{
+  MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
+  librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
+
+  set<Edition>    rpm_keys( pubkeyEditions() );
+  list<PublicKey> zypp_keys( getZYpp()->keyRing()->trustedPublicKeys() );
+
+  // Temporarily disconnect to prevent the attemt to re-import the exported keys.
+  callback::TempConnect<KeyRingSignals> tempDisconnect;
+
+  TmpFile tmpfile( getZYpp()->tmpPath() );
+  {
+    ofstream tmpos( tmpfile.path().c_str() );
+    for_( it, rpm_keys.begin(), rpm_keys.end() )
+    {
+      // search the zypp key into the rpm keys
+      // long id is edition version + release
+      string id = str::toUpper( (*it).version() + (*it).release());
+      list<PublicKey>::iterator ik( find( zypp_keys.begin(), zypp_keys.end(), id) );
+      if ( ik != zypp_keys.end() )
+      {
+       MIL << "Key " << (*it) << " is already in zypp database." << endl;
+      }
+      else
+      {
+       // we export the rpm key into a file
+       RpmHeader::constPtr result( new RpmHeader() );
+       getData( string("gpg-pubkey"), *it, result );
+       MIL <<  "Will export trusted key " << (*it) << " to zypp keyring." << endl;
+       tmpos << result->tag_description() << endl;
+      }
+    }
+  }
+  try
+  {
+    getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
+  }
+  catch (Exception &e)
+  {
+    ERR << "Could not import keys into in zypp keyring" << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::importPubkey
+//     METHOD TYPE : PMError
+//
+void RpmDb::importPubkey( const PublicKey & pubkey_r )
+{
+  FAILIFNOTINITIALIZED;
+
+  // check if the key is already in the rpm database and just
+  // return if it does.
+  set<Edition> rpm_keys = pubkeyEditions();
+  string keyshortid = pubkey_r.id().substr(8,8);
+  MIL << "Comparing '" << keyshortid << "' to: ";
+  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
+  {
+    string id = str::toUpper( (*it).version() );
+    MIL <<  ", '" << id << "'";
+    if ( id == keyshortid )
+    {
+        // they match id
+        // now check if timestamp is different
+        Date date = Date(str::strtonum<Date::ValueType>("0x" + (*it).release()));
+        if (  date == pubkey_r.created() )
+        {
+
+            MIL << endl << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
+            return;
+        }
+        else
+        {
+            MIL << endl << "Key " << pubkey_r << " has another version in keyring. ( " << date << " & " << pubkey_r.created() << ")" << endl;
+
+        }
+
+    }
+  }
+  // key does not exists, lets import it
+  MIL <<  endl;
+
+  RpmArgVec opts;
+  opts.push_back ( "--import" );
+  opts.push_back ( "--" );
+  opts.push_back ( pubkey_r.path().asString().c_str() );
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  string line;
+  while ( systemReadLine( line ) )
+  {
+    if ( line.substr( 0, 6 ) == "error:" )
+    {
+      WAR << line << endl;
+    }
+    else
+    {
+      DBG << line << endl;
+    }
+  }
+
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 )
+  {
+    //TranslatorExplanation first %s is file name, second is error message
+    ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
+        _("Failed to import public key from file %s: %s"))
+        % pubkey_r.asString() % error_message)));
+  }
+  else
+  {
+    MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePubkey
+//     METHOD TYPE : PMError
+//
+void RpmDb::removePubkey( const PublicKey & pubkey_r )
+{
+  FAILIFNOTINITIALIZED;
+
+  // check if the key is in the rpm database and just
+  // return if it does not.
+  set<Edition> rpm_keys = pubkeyEditions();
+
+  // search the key
+  set<Edition>::const_iterator found_edition = rpm_keys.end();
+
+  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
+  {
+    string id = str::toUpper( (*it).version() );
+    string keyshortid = pubkey_r.id().substr(8,8);
+    MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
+    if ( id == keyshortid )
+    {
+       found_edition = it;
+       break;
+    }
+  }
+
+  // the key does not exist, cannot be removed
+  if (found_edition == rpm_keys.end())
+  {
+      WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
+      return;
+  }
+
+  string rpm_name("gpg-pubkey-" + found_edition->asString());
+
+  RpmArgVec opts;
+  opts.push_back ( "-e" );
+  opts.push_back ( "--" );
+  opts.push_back ( rpm_name.c_str() );
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  string line;
+  while ( systemReadLine( line ) )
+  {
+    if ( line.substr( 0, 6 ) == "error:" )
+    {
+      WAR << line << endl;
+    }
+    else
+    {
+      DBG << line << endl;
+    }
+  }
+
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 )
+  {
+    //TranslatorExplanation first %s is key name, second is error message
+    ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
+        _("Failed to remove public key %s: %s")) % pubkey_r.asString()
+        % error_message)));
+  }
+  else
+  {
+    MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::pubkeys
+//     METHOD TYPE : set<Edition>
+//
+list<PublicKey> RpmDb::pubkeys() const
+{
+  list<PublicKey> ret;
+
+  librpmDb::db_const_iterator it;
+  for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
+  {
+    Edition edition = it->tag_edition();
+    if (edition != Edition::noedition)
+    {
+      // we export the rpm key into a file
+      RpmHeader::constPtr result = new RpmHeader();
+      getData( string("gpg-pubkey"), edition, result );
+      TmpFile file(getZYpp()->tmpPath());
+      ofstream os;
+      try
+      {
+        os.open(file.path().asString().c_str());
+        // dump rpm key into the tmp file
+        os << result->tag_description();
+        //MIL << "-----------------------------------------------" << endl;
+        //MIL << result->tag_description() <<endl;
+        //MIL << "-----------------------------------------------" << endl;
+        os.close();
+        // read the public key from the dumped file
+        PublicKey key(file);
+        ret.push_back(key);
+      }
+      catch (exception &e)
+      {
+        ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
+        // just ignore the key
+      }
+    }
+  }
+  return ret;
+}
+
+set<Edition> RpmDb::pubkeyEditions() const
+  {
+    set<Edition> ret;
+
+    librpmDb::db_const_iterator it;
+    for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
+    {
+      Edition edition = it->tag_edition();
+      if (edition != Edition::noedition)
+        ret.insert( edition );
+    }
+    return ret;
+  }
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::fileList
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+list<FileInfo>
+RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
+{
+  list<FileInfo> result;
+
+  librpmDb::db_const_iterator it;
+  bool found;
+  if (edition_r == Edition::noedition)
+  {
+    found = it.findPackage( name_r );
+  }
+  else
+  {
+    found = it.findPackage( name_r, edition_r );
+  }
+  if (!found)
+    return result;
+
+  return result;
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasFile
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
+{
+  librpmDb::db_const_iterator it;
+  bool res;
+  do
+  {
+    res = it.findByFile( file_r );
+    if (!res) break;
+    if (!name_r.empty())
+    {
+      res = (it->tag_name() == name_r);
+    }
+    ++it;
+  }
+  while (res && *it);
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::whoOwnsFile
+//     METHOD TYPE : string
+//
+//     DESCRIPTION :
+//
+string RpmDb::whoOwnsFile( const string & file_r) const
+{
+  librpmDb::db_const_iterator it;
+  if (it.findByFile( file_r ))
+  {
+    return it->tag_name();
+  }
+  return "";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasProvides
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasProvides( const string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByProvides( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasRequiredBy
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasRequiredBy( const string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByRequiredBy( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasConflicts
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasConflicts( const string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByConflicts( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasPackage
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasPackage( const string & name_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findPackage( name_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasPackage
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findPackage( name_r, ed_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::getData
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+void RpmDb::getData( const string & name_r,
+                     RpmHeader::constPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r );
+  result_r = *it;
+  if (it.dbError())
+    ZYPP_THROW(*(it.dbError()));
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::getData
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void RpmDb::getData( const string & name_r, const Edition & ed_r,
+                     RpmHeader::constPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r, ed_r  );
+  result_r = *it;
+  if (it.dbError())
+    ZYPP_THROW(*(it.dbError()));
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : RpmDb::checkPackage
+//     METHOD TYPE : RpmDb::checkPackageResult
+//
+RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
+{
+  PathInfo file( path_r );
+  if ( ! file.isFile() )
+  {
+    ERR << "Not a file: " << file << endl;
+    return CHK_ERROR;
+  }
+
+  FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+  if ( fd == 0 || ::Ferror(fd) )
+  {
+    ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+    if ( fd )
+      ::Fclose( fd );
+    return CHK_ERROR;
+  }
+
+  rpmts ts = ::rpmtsCreate();
+  ::rpmtsSetRootDir( ts, root().asString().c_str() );
+  ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
+  int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
+  ts = rpmtsFree(ts);
+
+  ::Fclose( fd );
+
+  switch ( res )
+  {
+  case RPMRC_OK:
+    return CHK_OK;
+    break;
+  case RPMRC_NOTFOUND:
+    WAR << "Signature is unknown type. " << file << endl;
+    return CHK_NOTFOUND;
+    break;
+  case RPMRC_FAIL:
+    WAR << "Signature does not verify. " << file << endl;
+    return CHK_FAIL;
+    break;
+  case RPMRC_NOTTRUSTED:
+    WAR << "Signature is OK, but key is not trusted. " << file << endl;
+    return CHK_NOTTRUSTED;
+    break;
+  case RPMRC_NOKEY:
+    WAR << "Public key is unavailable. " << file << endl;
+    return CHK_NOKEY;
+    break;
+  }
+  ERR << "Error reading header." << file << endl;
+  return CHK_ERROR;
+}
+
+// determine changed files of installed package
+bool
+RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
+{
+  bool ok = true;
+
+  fileList.clear();
+
+  if ( ! initialized() ) return false;
+
+  RpmArgVec opts;
+
+  opts.push_back ("-V");
+  opts.push_back ("--nodeps");
+  opts.push_back ("--noscripts");
+  opts.push_back ("--nomd5");
+  opts.push_back ("--");
+  opts.push_back (packageName.c_str());
+
+  run_rpm (opts, ExternalProgram::Discard_Stderr);
+
+  if ( process == NULL )
+    return false;
+
+  /* from rpm manpage
+   5      MD5 sum
+   S      File size
+   L      Symlink
+   T      Mtime
+   D      Device
+   U      User
+   G      Group
+   M      Mode (includes permissions and file type)
+  */
+
+  string line;
+  while (systemReadLine(line))
+  {
+    if (line.length() > 12 &&
+        (line[0] == 'S' || line[0] == 's' ||
+         (line[0] == '.' && line[7] == 'T')))
+    {
+      // file has been changed
+      string filename;
+
+      filename.assign(line, 11, line.length() - 11);
+      fileList.insert(filename);
+    }
+  }
+
+  systemStatus();
+  // exit code ignored, rpm returns 1 no matter if package is installed or
+  // not
+
+  return ok;
+}
+
+
+
+/****************************************************************/
+/* private member-functions                                    */
+/****************************************************************/
+
+/*--------------------------------------------------------------*/
+/* Run rpm with the specified arguments, handling stderr       */
+/* as specified  by disp                                       */
+/*--------------------------------------------------------------*/
+void
+RpmDb::run_rpm (const RpmArgVec& opts,
+                ExternalProgram::Stderr_Disposition disp)
+{
+  if ( process )
+  {
+    delete process;
+    process = NULL;
+  }
+  exit_code = -1;
+
+  if ( ! initialized() )
+  {
+    ZYPP_THROW(RpmDbNotOpenException());
+  }
+
+  RpmArgVec args;
+
+  // always set root and dbpath
+  args.push_back("rpm");
+  args.push_back("--root");
+  args.push_back(_root.asString().c_str());
+  args.push_back("--dbpath");
+  args.push_back(_dbPath.asString().c_str());
+
+  const char* argv[args.size() + opts.size() + 1];
+
+  const char** p = argv;
+  p = copy (args.begin (), args.end (), p);
+  p = copy (opts.begin (), opts.end (), p);
+  *p = 0;
+
+  // Invalidate all outstanding database handles in case
+  // the database gets modified.
+  librpmDb::dbRelease( true );
+
+  // Launch the program with default locale
+  process = new ExternalProgram(argv, disp, false, -1, true);
+  return;
+}
+
+/*--------------------------------------------------------------*/
+/* Read a line from the rpm process                            */
+/*--------------------------------------------------------------*/
+bool RpmDb::systemReadLine( string & line )
+{
+  line.erase();
+
+  if ( process == NULL )
+    return false;
+
+  if ( process->inputFile() )
+  {
+    process->setBlocking( false );
+    FILE * inputfile = process->inputFile();
+    int    inputfileFd = ::fileno( inputfile );
+    do
+    {
+      /* Watch inputFile to see when it has input. */
+      fd_set rfds;
+      FD_ZERO( &rfds );
+      FD_SET( inputfileFd, &rfds );
+
+      /* Wait up to 5 seconds. */
+      struct timeval tv;
+      tv.tv_sec = 5;
+      tv.tv_usec = 0;
+
+      int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
+
+      if ( retval == -1 )
+      {
+       ERR << "select error: " << strerror(errno) << endl;
+       if ( errno != EINTR )
+         return false;
+      }
+      else if ( retval )
+      {
+       // Data is available now.
+       static size_t linebuffer_size = 0;      // static because getline allocs
+       static char * linebuffer = 0;           // and reallocs if buffer is too small
+       ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
+       if ( nread == -1 )
+       {
+         if ( ::feof( inputfile ) )
+           return line.size(); // in case of pending output
+       }
+       else
+       {
+         if ( nread > 0 )
+         {
+           if ( linebuffer[nread-1] == '\n' )
+             --nread;
+           line += string( linebuffer, nread );
+         }
+
+         if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
+           return true; // complete line
+       }
+       clearerr( inputfile );
+      }
+      else
+      {
+       // No data within time.
+       if ( ! process->running() )
+         return false;
+      }
+    } while ( true );
+  }
+
+  return false;
+}
+
+/*--------------------------------------------------------------*/
+/* Return the exit status of the rpm process, closing the      */
+/* connection if not already done                              */
+/*--------------------------------------------------------------*/
+int
+RpmDb::systemStatus()
+{
+  if ( process == NULL )
+    return -1;
+
+  exit_code = process->close();
+  if (exit_code == 0)
+    error_message = "";
+  else
+    error_message = process->execError();
+  process->kill();
+  delete process;
+  process = 0;
+
+  //   DBG << "exit code " << exit_code << endl;
+
+  return exit_code;
+}
+
+/*--------------------------------------------------------------*/
+/* Forcably kill the rpm process                               */
+/*--------------------------------------------------------------*/
+void
+RpmDb::systemKill()
+{
+  if (process) process->kill();
+}
+
+
+// generate diff mails for config files
+void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
+{
+  string msg = line.substr(9);
+  string::size_type pos1 = string::npos;
+  string::size_type pos2 = string::npos;
+  string file1s, file2s;
+  Pathname file1;
+  Pathname file2;
+
+  pos1 = msg.find (typemsg);
+  for (;;)
+  {
+    if ( pos1 == string::npos )
+      break;
+
+    pos2 = pos1 + strlen (typemsg);
+
+    if (pos2 >= msg.length() )
+      break;
+
+    file1 = msg.substr (0, pos1);
+    file2 = msg.substr (pos2);
+
+    file1s = file1.asString();
+    file2s = file2.asString();
+
+    if (!_root.empty() && _root != "/")
+    {
+      file1 = _root + file1;
+      file2 = _root + file2;
+    }
+
+    string out;
+    int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
+    if (ret)
+    {
+      Pathname file = _root + WARNINGMAILPATH;
+      if (filesystem::assert_dir(file) != 0)
+      {
+        ERR << "Could not create " << file.asString() << endl;
+        break;
+      }
+      file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
+      ofstream notify(file.asString().c_str(), ios::out|ios::app);
+      if (!notify)
+      {
+        ERR << "Could not open " <<  file << endl;
+        break;
+      }
+
+      // Translator: %s = name of an rpm package. A list of diffs follows
+      // this message.
+      notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
+      if (ret>1)
+      {
+        ERR << "diff failed" << endl;
+        notify << str::form(difffailmsg,
+                            file1s.c_str(), file2s.c_str()) << endl;
+      }
+      else
+      {
+        notify << str::form(diffgenmsg,
+                            file1s.c_str(), file2s.c_str()) << endl;
+
+        // remove root for the viewer's pleasure (#38240)
+        if (!_root.empty() && _root != "/")
+        {
+          if (out.substr(0,4) == "--- ")
+          {
+            out.replace(4, file1.asString().length(), file1s);
+          }
+          string::size_type pos = out.find("\n+++ ");
+          if (pos != string::npos)
+          {
+            out.replace(pos+5, file2.asString().length(), file2s);
+          }
+        }
+        notify << out << endl;
+      }
+      notify.close();
+      notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
+      notify.close();
+    }
+    else
+    {
+      WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
+    }
+    break;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::installPackage
+//     METHOD TYPE : PMError
+//
+void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
+{
+  callback::SendReport<RpmInstallReport> report;
+
+  report->start(filename);
+
+  do
+    try
+    {
+      doInstallPackage(filename, flags, report);
+      report->finish();
+      break;
+    }
+    catch (RpmException & excpt_r)
+    {
+      RpmInstallReport::Action user = report->problem( excpt_r );
+
+      if ( user == RpmInstallReport::ABORT )
+      {
+        report->finish( excpt_r );
+        ZYPP_RETHROW(excpt_r);
+      }
+      else if ( user == RpmInstallReport::IGNORE )
+      {
+        break;
+      }
+    }
+  while (true);
+}
+
+void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
+{
+  FAILIFNOTINITIALIZED;
+  HistoryLog historylog;
+
+  MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
+
+
+  // backup
+  if ( _packagebackups )
+  {
+    // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+    if ( ! backupPackage( filename ) )
+    {
+      ERR << "backup of " << filename.asString() << " failed" << endl;
+    }
+    // FIXME status handling
+    report->progress( 0 ); // allow 1% for backup creation.
+  }
+  else
+  {
+    report->progress( 100 );
+  }
+
+  // run rpm
+  RpmArgVec opts;
+  if (flags & RPMINST_NOUPGRADE)
+    opts.push_back("-i");
+  else
+    opts.push_back("-U");
+
+  opts.push_back("--percent");
+
+  // ZConfig defines cross-arch installation
+  if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
+    opts.push_back("--ignorearch");
+
+  if (flags & RPMINST_NODIGEST)
+    opts.push_back("--nodigest");
+  if (flags & RPMINST_NOSIGNATURE)
+    opts.push_back("--nosignature");
+  if (flags & RPMINST_EXCLUDEDOCS)
+    opts.push_back ("--excludedocs");
+  if (flags & RPMINST_NOSCRIPTS)
+    opts.push_back ("--noscripts");
+  if (flags & RPMINST_FORCE)
+    opts.push_back ("--force");
+  if (flags & RPMINST_NODEPS)
+    opts.push_back ("--nodeps");
+  if (flags & RPMINST_IGNORESIZE)
+    opts.push_back ("--ignoresize");
+  if (flags & RPMINST_JUSTDB)
+    opts.push_back ("--justdb");
+  if (flags & RPMINST_TEST)
+    opts.push_back ("--test");
+
+  opts.push_back("--");
+
+  // rpm requires additional quoting of special chars:
+  string quotedFilename( rpmQuoteFilename( filename ) );
+  opts.push_back ( quotedFilename.c_str() );
+
+  modifyDatabase(); // BEFORE run_rpm
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  string line;
+  string rpmmsg;
+  vector<string> configwarnings;
+
+  unsigned linecnt = 0;
+  while (systemReadLine(line))
+  {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else
+      continue;
+
+    if (line.substr(0,2)=="%%")
+    {
+      int percent;
+      sscanf (line.c_str () + 2, "%d", &percent);
+      report->progress( percent );
+    }
+    else
+      rpmmsg += line+'\n';
+
+    if ( line.substr(0,8) == "warning:" )
+    {
+      configwarnings.push_back(line);
+    }
+  }
+  if ( linecnt > MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
+
+  int rpm_status = systemStatus();
+
+  // evaluate result
+  for (vector<string>::iterator it = configwarnings.begin();
+       it != configwarnings.end(); ++it)
+  {
+    processConfigFiles(*it, Pathname::basename(filename), " saved as ",
+                       // %s = filenames
+                       _("rpm saved %s as %s, but it was impossible to determine the difference"),
+                       // %s = filenames
+                       _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
+    processConfigFiles(*it, Pathname::basename(filename), " created as ",
+                       // %s = filenames
+                       _("rpm created %s as %s, but it was impossible to determine the difference"),
+                       // %s = filenames
+                       _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
+  }
+
+  if ( rpm_status != 0 )
+  {
+    historylog.comment(
+        str::form("%s install failed", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    ostringstream sstr;
+    sstr << "rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+    // TranslatorExplanation the colon is followed by an error message
+    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
+               (rpmmsg.empty() ? error_message : rpmmsg)));
+  }
+  else if ( ! rpmmsg.empty() )
+  {
+    historylog.comment(
+        str::form("%s installed ok", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePackage
+//     METHOD TYPE : PMError
+//
+void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
+{
+  // 'rpm -e' does not like epochs
+  return removePackage( package->name()
+                        + "-" + package->edition().version()
+                        + "-" + package->edition().release()
+                        + "." + package->arch().asString(), flags );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePackage
+//     METHOD TYPE : PMError
+//
+void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
+{
+  callback::SendReport<RpmRemoveReport> report;
+
+  report->start( name_r );
+
+  do
+    try
+    {
+      doRemovePackage(name_r, flags, report);
+      report->finish();
+      break;
+    }
+    catch (RpmException & excpt_r)
+    {
+      RpmRemoveReport::Action user = report->problem( excpt_r );
+
+      if ( user == RpmRemoveReport::ABORT )
+      {
+        report->finish( excpt_r );
+        ZYPP_RETHROW(excpt_r);
+      }
+      else if ( user == RpmRemoveReport::IGNORE )
+      {
+        break;
+      }
+    }
+  while (true);
+}
+
+
+void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
+{
+  FAILIFNOTINITIALIZED;
+  HistoryLog historylog;
+
+  MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
+
+  // backup
+  if ( _packagebackups )
+  {
+    // FIXME solve this status report somehow
+    //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+    if ( ! backupPackage( name_r ) )
+    {
+      ERR << "backup of " << name_r << " failed" << endl;
+    }
+    report->progress( 0 );
+  }
+  else
+  {
+    report->progress( 100 );
+  }
+
+  // run rpm
+  RpmArgVec opts;
+  opts.push_back("-e");
+  opts.push_back("--allmatches");
+
+  if (flags & RPMINST_NOSCRIPTS)
+    opts.push_back("--noscripts");
+  if (flags & RPMINST_NODEPS)
+    opts.push_back("--nodeps");
+  if (flags & RPMINST_JUSTDB)
+    opts.push_back("--justdb");
+  if (flags & RPMINST_TEST)
+    opts.push_back ("--test");
+  if (flags & RPMINST_FORCE)
+  {
+    WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
+  }
+
+  opts.push_back("--");
+  opts.push_back(name_r.c_str());
+
+  modifyDatabase(); // BEFORE run_rpm
+  run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+  string line;
+  string rpmmsg;
+
+  // got no progress from command, so we fake it:
+  // 5  - command started
+  // 50 - command completed
+  // 100 if no error
+  report->progress( 5 );
+  unsigned linecnt = 0;
+  while (systemReadLine(line))
+  {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else
+      continue;
+    rpmmsg += line+'\n';
+  }
+  if ( linecnt > MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
+  report->progress( 50 );
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 )
+  {
+    historylog.comment(
+        str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
+    ostringstream sstr;
+    sstr << "rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+    // TranslatorExplanation the colon is followed by an error message
+    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
+               (rpmmsg.empty() ? error_message: rpmmsg)));
+  }
+  else if ( ! rpmmsg.empty() )
+  {
+    historylog.comment(
+        str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
+
+    ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::backupPackage
+//     METHOD TYPE : bool
+//
+bool RpmDb::backupPackage( const Pathname & filename )
+{
+  RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
+  if ( ! h )
+    return false;
+
+  return backupPackage( h->tag_name() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::backupPackage
+//     METHOD TYPE : bool
+//
+bool RpmDb::backupPackage(const string& packageName)
+{
+  HistoryLog progresslog;
+  bool ret = true;
+  Pathname backupFilename;
+  Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
+
+  if (_backuppath.empty())
+  {
+    INT << "_backuppath empty" << endl;
+    return false;
+  }
+
+  FileList fileList;
+
+  if (!queryChangedFiles(fileList, packageName))
+  {
+    ERR << "Error while getting changed files for package " <<
+    packageName << endl;
+    return false;
+  }
+
+  if (fileList.size() <= 0)
+  {
+    DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
+    return true;
+  }
+
+  if (filesystem::assert_dir(_root + _backuppath) != 0)
+  {
+    return false;
+  }
+
+  {
+    // build up archive name
+    time_t currentTime = time(0);
+    struct tm *currentLocalTime = localtime(&currentTime);
+
+    int date = (currentLocalTime->tm_year + 1900) * 10000
+               + (currentLocalTime->tm_mon + 1) * 100
+               + currentLocalTime->tm_mday;
+
+    int num = 0;
+    do
+    {
+      backupFilename = _root + _backuppath
+                       + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
+
+    }
+    while ( PathInfo(backupFilename).isExist() && num++ < 1000);
+
+    PathInfo pi(filestobackupfile);
+    if (pi.isExist() && !pi.isFile())
+    {
+      ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
+      return false;
+    }
+
+    ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
+
+    if (!fp)
+    {
+      ERR << "could not open " << filestobackupfile.asString() << endl;
+      return false;
+    }
+
+    for (FileList::const_iterator cit = fileList.begin();
+         cit != fileList.end(); ++cit)
+    {
+      string name = *cit;
+      if ( name[0] == '/' )
+      {
+        // remove slash, file must be relative to -C parameter of tar
+        name = name.substr( 1 );
+      }
+      DBG << "saving file "<< name << endl;
+      fp << name << endl;
+    }
+    fp.close();
+
+    const char* const argv[] =
+      {
+        "tar",
+        "-czhP",
+        "-C",
+        _root.asString().c_str(),
+        "--ignore-failed-read",
+        "-f",
+        backupFilename.asString().c_str(),
+        "-T",
+        filestobackupfile.asString().c_str(),
+        NULL
+      };
+
+    // execute tar in inst-sys (we dont know if there is a tar below _root !)
+    ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+
+    string tarmsg;
+
+    // TODO: its probably possible to start tar with -v and watch it adding
+    // files to report progress
+    for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
+    {
+      tarmsg+=output;
+    }
+
+    int ret = tar.close();
+
+    if ( ret != 0)
+    {
+      ERR << "tar failed: " << tarmsg << endl;
+      ret = false;
+    }
+    else
+    {
+      MIL << "tar backup ok" << endl;
+      progresslog.comment(
+          str::form(_("created backup %s"), backupFilename.asString().c_str())
+          , /*timestamp*/true);
+    }
+
+    filesystem::unlink(filestobackupfile);
+  }
+
+  return ret;
+}
+
+void RpmDb::setBackupPath(const Pathname& path)
+{
+  _backuppath = path;
+}
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/RpmDb.h b/zypp/target/rpm/RpmDb.h
new file mode 100644 (file)
index 0000000..23666e3
--- /dev/null
@@ -0,0 +1,534 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmDb.h
+ *
+*/
+
+// -*- C++ -*-
+
+#ifndef ZYPP_TARGET_RPM_RPMDB_H
+#define ZYPP_TARGET_RPM_RPMDB_H
+
+#include <iosfwd>
+#include <list>
+#include <vector>
+#include <string>
+
+#include "zypp/Pathname.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/Package.h"
+#include "zypp/KeyRing.h"
+
+#include "zypp/target/rpm/RpmFlags.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/target/rpm/RpmCallbacks.h"
+#include "zypp/ZYppCallbacks.h"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmDb
+/**
+ * @short Interface to the rpm program
+ **/
+class RpmDb : public base::ReferenceCounted, private base::NonCopyable
+{
+public:
+
+  /**
+   * Default error class
+   **/
+  typedef class InstTargetError Error;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // INITALISATION
+  //
+  ///////////////////////////////////////////////////////////////////
+private:
+
+  enum DbStateInfoBits {
+    DbSI_NO_INIT       = 0x0000,
+    DbSI_HAVE_V4       = 0x0001,
+    DbSI_MADE_V4       = 0x0002,
+    DbSI_MODIFIED_V4   = 0x0004,
+    DbSI_HAVE_V3       = 0x0008,
+    DbSI_HAVE_V3TOV4   = 0x0010,
+    DbSI_MADE_V3TOV4   = 0x0020
+  };
+
+  friend std::ostream & operator<<( std::ostream & str, const DbStateInfoBits & obj );
+
+  void dbsi_set( DbStateInfoBits & val_r, const unsigned & bits_r ) const
+  {
+    val_r = (DbStateInfoBits)(val_r | bits_r);
+  }
+  void dbsi_clr( DbStateInfoBits & val_r, const unsigned & bits_r ) const
+  {
+    val_r = (DbStateInfoBits)(val_r & ~bits_r);
+  }
+  bool dbsi_has( const DbStateInfoBits & val_r, const unsigned & bits_r ) const
+  {
+    return( (val_r & bits_r) == bits_r );
+  }
+
+  /**
+   * Internal state info
+   **/
+  DbStateInfoBits _dbStateInfo;
+
+  /**
+   * Root directory for all operations.
+   **/
+  Pathname _root;
+
+  /**
+   * Directory that contains the rpmdb.
+   **/
+  Pathname _dbPath;
+
+  /**
+   * Internal helper for @ref initDatabase.
+   *
+   * \throws RpmException
+   *
+   **/
+  void internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+                              DbStateInfoBits & info_r );
+
+  /**
+   * Remove the rpm4 database in dbdir_r and optionally any backup created
+   * on conversion.
+   **/
+  static void removeV4( const Pathname & dbdir_r, bool v3backup_r );
+
+  /**
+   * Remove the rpm3 database in dbdir_r. Create a backup copy named
+   * packages.rpm3 if it does not already exist.
+   **/
+  static void removeV3( const Pathname & dbdir_r, bool v3backup_r );
+
+  /**
+   * Called before the database is modified by installPackage/removePackage.
+   * Invalidates Packages list and moves away any old database.
+   **/
+  void modifyDatabase();
+
+public:
+
+  /**
+   * Constructor. There's no rpmdb access until @ref initDatabase
+   * was called.
+   **/
+  RpmDb();
+
+  /**
+   * Destructor.
+   **/
+  ~RpmDb();
+
+  /**
+   * timestamp of the rpm database (last modification)
+   */
+  Date timestamp() const;
+
+  /**
+   * @return Root directory for all operations (empty if not initialized).
+   **/
+  const Pathname & root() const
+  {
+    return _root;
+  }
+
+  /**
+   * @return Directory that contains the rpmdb (empty if not initialized).
+   **/
+  const Pathname & dbPath() const
+  {
+    return _dbPath;
+  }
+
+  /**
+   * @return Whether we are initialized.
+   **/
+  bool initialized() const
+  {
+    return( ! _root.empty() );
+  }
+
+  /**
+   * Prepare access to the rpm database. Optional arguments may denote the
+   * root directory for all operations and the directory (below root) that
+   * contains the rpmdb (usg. you won't need to set this).
+   *
+   * On empty Pathnames the default is used:
+   * <PRE>
+   *     root:   /
+   *     dbPath: /var/lib/rpm
+   * </PRE>
+   *
+   * Calling initDatabase a second time with different arguments will return
+   * an error but leave the database in it's original state.
+   *
+   * Converting an old batabase is done if necessary. On update: The converted
+   * database will be removed by @ref closeDatabase, if it was not modified
+   * (no packages were installed or deleted). Otherwise the new database
+   * is kept, and the old one is removed.
+   *
+   * If the  database alredy exists and \c doRebuild_r is true, \ref rebuildDatabase
+   * is called.
+   *
+   * \throws RpmException
+   *
+   **/
+  void initDatabase( Pathname root_r = Pathname(),
+                     Pathname dbPath_r = Pathname(),
+                     bool doRebuild_r = false );
+
+  /**
+   * Block further access to the rpm database and go back to uninitialized
+   * state. On update: Decides what to do with any converted database
+   * (see @ref initDatabase).
+   *
+   * \throws RpmException
+   *
+   **/
+  void closeDatabase();
+
+  /**
+   * Rebuild the rpm database (rpm --rebuilddb).
+   *
+   * \throws RpmException
+   *
+   **/
+  void rebuildDatabase();
+
+  /**
+   * Import ascii armored public key in file pubkey_r.
+   *
+   * \throws RpmException
+   *
+   **/
+  void importPubkey( const PublicKey & pubkey_r );
+
+  /**
+   * Remove a public key from the rpm database
+   *
+   * \throws RpmException
+   *
+   **/
+  void removePubkey( const PublicKey & pubkey_r );
+
+  /**
+   * Return the long ids of all installed public keys.
+   **/
+  std::list<PublicKey> pubkeys() const;
+
+  /**
+   * Return the edition of all installed public keys.
+   **/
+  std::set<Edition> pubkeyEditions() const;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  // Direct RPM database retrieval via librpm.
+  //
+  ///////////////////////////////////////////////////////////////////
+public:
+
+  /**
+   * return complete file list for installed package name_r (in FileInfo.filename)
+   * if edition_r != Edition::noedition, check for exact edition
+   * if full==true, fill all attributes of FileInfo
+   **/
+  std::list<FileInfo> fileList( const std::string & name_r, const Edition & edition_r ) const;
+
+  /**
+   * Return true if at least one package owns a certain file (name_r empty)
+   * Return true if package name_r owns file file_r (name_r nonempty).
+   **/
+  bool hasFile( const std::string & file_r, const std::string & name_r = "" ) const;
+
+  /**
+   * Return name of package owning file
+   * or empty string if no installed package owns file
+   **/
+  std::string whoOwnsFile( const std::string & file_r ) const;
+
+  /**
+   * Return true if at least one package provides a certain tag.
+   **/
+  bool hasProvides( const std::string & tag_r ) const;
+
+  /**
+   * Return true if at least one package requires a certain tag.
+   **/
+  bool hasRequiredBy( const std::string & tag_r ) const;
+
+  /**
+   * Return true if at least one package conflicts with a certain tag.
+   **/
+  bool hasConflicts( const std::string & tag_r ) const;
+
+  /**
+   * Return true if package is installed.
+   **/
+  bool hasPackage( const std::string & name_r ) const;
+
+  /**
+   * Return true if package is installed in a certain edition.
+   **/
+  bool hasPackage( const std::string & name_r, const Edition & ed_r ) const;
+
+  /**
+   * Get an installed packages data from rpmdb. Package is
+   * identified by name. Data returned via result are NULL,
+   * if packge is not installed (PMError is not set), or RPM database
+   * could not be read (PMError is set).
+   *
+   * \throws RpmException
+   *
+   * FIXME this and following comment
+   *
+   **/
+  void getData( const std::string & name_r,
+                RpmHeader::constPtr & result_r ) const;
+
+  /**
+   * Get an installed packages data from rpmdb. Package is
+   * identified by name and edition. Data returned via result are NULL,
+   * if packge is not installed (PMError is not set), or RPM database
+   * could not be read (PMError is set).
+   *
+   * \throws RpmException
+   *
+   **/
+  void getData( const std::string & name_r, const Edition & ed_r,
+                RpmHeader::constPtr & result_r ) const;
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  ///////////////////////////////////////////////////////////////////
+public:
+  /**
+   * iterates through zypp keyring and import all non existant keys
+   * into rpm keyring
+   */
+  void importZyppKeyRingTrustedKeys();
+  /**
+   * insert all rpm trusted keys into zypp trusted keyring
+   */
+  void exportTrustedKeysInZyppKeyRing();
+private:
+  /**
+   * The connection to the rpm process.
+  */
+  ExternalProgram *process;
+
+  typedef std::vector<const char*> RpmArgVec;
+
+  /**
+   * Run rpm with the specified arguments and handle stderr.
+   * @param n_opts The number of arguments
+   * @param options Array of the arguments, @ref n_opts elements
+   * @param stderr_disp How to handle stderr, merged with stdout by default
+   *
+   * \throws RpmException
+   *
+   **/
+  void run_rpm( const RpmArgVec& options,
+                ExternalProgram::Stderr_Disposition stderr_disp =
+                  ExternalProgram::Stderr_To_Stdout);
+
+
+  /**
+   * Read a line from the general rpm query
+  */
+  bool systemReadLine(std::string &line);
+
+  /**
+   * Return the exit status of the general rpm process,
+   * closing the connection if not already done.
+  */
+  int systemStatus();
+
+  /**
+   * Forcably kill the system process
+  */
+  void systemKill();
+
+  /**
+   * The exit code of the rpm process, or -1 if not yet known.
+  */
+  int exit_code;
+
+  /**
+   * Error message from running rpm as external program.
+   * Use only if something fail.
+   */
+  std::string error_message;
+
+  /** /var/adm/backup */
+  Pathname _backuppath;
+
+  /** create package backups? */
+  bool _packagebackups;
+
+  /** whether <_root>/<WARNINGMAILPATH> was already created */
+  bool _warndirexists;
+
+  /**
+   * handle rpm messages like "/etc/testrc saved as /etc/testrc.rpmorig"
+   *
+   * @param line rpm output starting with warning:
+   * @param name name of package, appears in subject line
+   * @param typemsg " saved as " or " created as "
+   * @param difffailmsg what to put into mail if diff failed, must contain two %s for the two files
+   * @param diffgenmsg what to put into mail if diff succeeded, must contain two %s for the two files
+   * */
+  void processConfigFiles(const std::string& line,
+                          const std::string& name,
+                          const char* typemsg,
+                          const char* difffailmsg,
+                          const char* diffgenmsg);
+
+
+public:
+
+  typedef std::set<std::string> FileList;
+
+  /**
+   * checkPackage result
+   * @see checkPackage
+   * */
+  enum checkPackageResult
+  {
+    CHK_OK            = 0, /*!< Signature is OK. */
+    CHK_NOTFOUND      = 1, /*!< Signature is unknown type. */
+    CHK_FAIL          = 2, /*!< Signature does not verify. */
+    CHK_NOTTRUSTED    = 3, /*!< Signature is OK, but key is not trusted. */
+    CHK_NOKEY         = 4, /*!< Public key is unavailable. */
+    CHK_ERROR         = 5  /*!< File does not exist or can't be opened. */
+  };
+
+  /**
+   * Check signature of rpm file on disk.
+   *
+   * @param filename which file to check
+   *
+   * @return checkPackageResult
+  */
+  checkPackageResult checkPackage( const Pathname & path_r );
+
+  /** install rpm package
+   *
+   * @param filename file to install
+   * @param flags which rpm options to use
+   *
+   * @return success
+   *
+   * \throws RpmException
+   *
+   * */
+  void installPackage ( const Pathname & filename, RpmInstFlags flags = RPMINST_NONE );
+
+  /** remove rpm package
+   *
+   * @param name_r Name of the rpm package to remove.
+   * @param iflags which rpm options to use
+   *
+   * @return success
+   *
+   * \throws RpmException
+   *
+   * */
+  void removePackage( const std::string & name_r, RpmInstFlags flags = RPMINST_NONE );
+  void removePackage( Package::constPtr package, RpmInstFlags flags = RPMINST_NONE );
+
+  /**
+   * get backup dir for rpm config files
+   *
+   * */
+  Pathname getBackupPath (void)
+  {
+    return _backuppath;
+  }
+
+  /**
+   * create tar.gz of all changed files in a Package
+   *
+   * @param packageName name of the Package to backup
+   *
+   * @see setBackupPath
+   * */
+  bool backupPackage(const std::string& packageName);
+
+  /**
+   * queries file for name and then calls above backupPackage
+   * function. For convenience.
+   *
+   * @param filename rpm file that is about to be installed
+   * */
+  bool backupPackage(const Pathname& filename);
+
+  /**
+   * set path where package backups are stored
+   *
+   * @see backupPackage
+   * */
+  void setBackupPath(const Pathname& path);
+
+  /**
+   * whether to create package backups during install or
+   * removal
+   *
+   * @param yes true or false
+   * */
+  void createPackageBackups(bool yes)
+  {
+    _packagebackups = yes;
+  }
+
+  /**
+   * determine which files of an installed package have been
+   * modified.
+   *
+   * @param fileList (output) where to store modified files
+   * @param packageName name of package to query
+   *
+   * @return false if package couln't be queried for some
+   * reason
+   * */
+  bool queryChangedFiles(FileList & fileList, const std::string& packageName);
+
+public:
+
+  /**
+   * Dump debug info.
+   **/
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+protected:
+  void doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report );
+  void doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report );
+  void doRebuildDatabase(callback::SendReport<RebuildDBReport> & report);
+};
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMDB_H
diff --git a/zypp/target/rpm/RpmException.cc b/zypp/target/rpm/RpmException.cc
new file mode 100644 (file)
index 0000000..90a29be
--- /dev/null
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmException.cc
+ *
+*/
+
+#include <string>
+#include <iostream>
+
+#include "zypp/target/rpm/RpmException.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace target
+{
+/////////////////////////////////////////////////////////////////
+namespace rpm
+{
+/////////////////////////////////////////////////////////////////
+
+std::ostream & RpmInvalidRootException::dumpOn( std::ostream & str ) const
+{
+  return str << "Illegal root " << _root
+         << " or dbPath " << _dbpath << endl;
+}
+
+std::ostream & RpmAccessBlockedException::dumpOn( std::ostream & str ) const
+{
+  return str << "Access is blocked: Root: " << _root
+         << " dbPath: " << _dbpath << endl;
+}
+
+std::ostream & RpmSubprocessException::dumpOn( std::ostream & str ) const
+{
+  return str << "Subprocess failed. Error: " << _errmsg << endl;
+}
+
+std::ostream & RpmInitException::dumpOn( std::ostream & str) const
+{
+  return str << "Failed to initialize database: Root: " << _root
+         << " dbPath: " << _dbpath << endl;
+}
+
+std::ostream & RpmDbOpenException::dumpOn( std::ostream & str) const
+{
+  return str << "Failed to open database: Root: " << _root
+         << " dbPath: " << _dbpath << endl;
+}
+
+std::ostream & RpmDbAlreadyOpenException::dumpOn( std::ostream & str) const
+{
+  return str << "Can't switch to " << _new_root << " " << _new_dbpath
+         << " while accessing " << _old_root << " " << _old_dbpath << endl;
+}
+
+std::ostream & RpmDbNotOpenException::dumpOn( std::ostream & str) const
+{
+  return str << "RPM database not open" << endl;
+}
+
+std::ostream & RpmDbConvertException::dumpOn( std::ostream & str) const
+{
+  return str << "RPM database conversion failed" << endl;
+}
+
+std::ostream & RpmNullDatabaseException::dumpOn( std::ostream & str) const
+{
+  return str << "NULL rpmV4 database passed as argument!" << endl;
+}
+
+/////////////////////////////////////////////////////////////////
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/target/rpm/RpmException.h b/zypp/target/rpm/RpmException.h
new file mode 100644 (file)
index 0000000..9f9284c
--- /dev/null
@@ -0,0 +1,249 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmException.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMEXCEPTION_H
+#define ZYPP_TARGET_RPM_RPMEXCEPTION_H
+
+#include <iosfwd>
+
+#include <string>
+
+#include "zypp/base/Exception.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace target
+{
+///////////////////////////////////////////////////////////////
+namespace rpm
+{
+///////////////////////////////////////////////////////////////
+//
+//        CLASS NAME : RpmException
+/** Just inherits Exception to separate media exceptions
+ *
+ **/
+class RpmException : public Exception
+{
+public:
+  /** Ctor taking message.
+   * Use \ref ZYPP_THROW to throw exceptions.
+  */
+  RpmException()
+      : Exception( "Rpm Exception" )
+  {}
+  /** Ctor taking message.
+   * Use \ref ZYPP_THROW to throw exceptions.
+  */
+  RpmException( const std::string & msg_r )
+      : Exception( msg_r )
+  {}
+  /** Dtor. */
+  virtual ~RpmException() throw()
+  {};
+};
+
+class GlobalRpmInitException : public RpmException
+{
+public:
+  /** Ctor taking message.
+   * Use \ref ZYPP_THROW to throw exceptions.
+  */
+  GlobalRpmInitException()
+      : RpmException("Global RPM initialization failed")
+  {}
+  /** Dtor. */
+  virtual ~GlobalRpmInitException() throw()
+  {};
+private:
+};
+
+class RpmInvalidRootException : public RpmException
+{
+public:
+  /** Ctor taking message.
+   * Use \ref ZYPP_THROW to throw exceptions.
+  */
+  RpmInvalidRootException( const Pathname & root_r,
+                           const Pathname & dbpath_r )
+      : RpmException()
+      , _root(root_r.asString())
+      , _dbpath(dbpath_r.asString())
+  {}
+  /** Dtor. */
+  virtual ~RpmInvalidRootException() throw()
+  {};
+  std::string root() const
+  {
+    return _root;
+  }
+  std::string dbpath() const
+  {
+    return _dbpath;
+  }
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _root;
+  std::string _dbpath;
+};
+
+class RpmAccessBlockedException : public RpmException
+{
+public:
+  RpmAccessBlockedException( const Pathname & root_r,
+                             const Pathname & dbpath_r )
+      : RpmException()
+      , _root(root_r.asString())
+      , _dbpath(dbpath_r.asString())
+  {}
+  virtual ~RpmAccessBlockedException() throw()
+  {};
+  std::string root() const
+  {
+    return _root;
+  }
+  std::string dbpath() const
+  {
+    return _dbpath;
+  }
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _root;
+  std::string _dbpath;
+};
+
+class RpmSubprocessException : public RpmException
+{
+public:
+  RpmSubprocessException(const std::string & errmsg_r)
+      : RpmException()
+      , _errmsg(errmsg_r)
+  {}
+  virtual ~RpmSubprocessException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _errmsg;
+};
+
+class RpmInitException : public RpmException
+{
+public:
+  RpmInitException(const Pathname & root_r,
+                   const Pathname & dbpath_r)
+      : RpmException()
+      , _root(root_r.asString())
+      , _dbpath(dbpath_r.asString())
+  {}
+  virtual ~RpmInitException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _root;
+  std::string _dbpath;
+};
+
+class RpmDbOpenException : public RpmException
+{
+public:
+  RpmDbOpenException(const Pathname & root_r,
+                     const Pathname & dbpath_r)
+      : RpmException()
+      , _root(root_r.asString())
+      , _dbpath(dbpath_r.asString())
+  {}
+  virtual ~RpmDbOpenException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _root;
+  std::string _dbpath;
+};
+
+class RpmDbAlreadyOpenException : public RpmException
+{
+public:
+  RpmDbAlreadyOpenException(const Pathname & old_root_r,
+                            const Pathname & old_dbpath_r,
+                            const Pathname & new_root_r,
+                            const Pathname & new_dbpath_r)
+      : RpmException()
+      , _old_root(old_root_r.asString())
+      , _old_dbpath(old_dbpath_r.asString())
+      , _new_root(new_root_r.asString())
+      , _new_dbpath(new_dbpath_r.asString())
+  {}
+  virtual ~RpmDbAlreadyOpenException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+  std::string _old_root;
+  std::string _old_dbpath;
+  std::string _new_root;
+  std::string _new_dbpath;
+};
+
+class RpmDbNotOpenException : public RpmException
+{
+public:
+  RpmDbNotOpenException()
+      : RpmException()
+  {}
+  virtual ~RpmDbNotOpenException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+};
+
+class RpmDbConvertException : public RpmException
+{
+public:
+  RpmDbConvertException()
+      : RpmException()
+  {}
+  virtual ~RpmDbConvertException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+};
+
+class RpmNullDatabaseException : public RpmException
+{
+public:
+  RpmNullDatabaseException()
+      : RpmException()
+  {}
+  virtual ~RpmNullDatabaseException() throw()
+  {};
+protected:
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+private:
+};
+
+
+
+/////////////////////////////////////////////////////////////////
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_RPM_RPMEXCEPTION_H
diff --git a/zypp/target/rpm/RpmFlags.h b/zypp/target/rpm/RpmFlags.h
new file mode 100644 (file)
index 0000000..3d89fe4
--- /dev/null
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/target/rpm/RpmFlags.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMFLAGS_H
+#define ZYPP_TARGET_RPM_RPMFLAGS_H
+
+#include <iosfwd>
+
+#include "zypp/base/Flags.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace target
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace rpm
+    { /////////////////////////////////////////////////////////////////
+
+      /**
+       * Bits representing rpm installation options.
+       *
+       * Use \ref RpmInstFlags as a type-safe way of
+       * storing OR-combinations.
+       *
+       * @see RpmDb::installPackage(), RpmDb::removePackage(),
+       */
+      enum RpmInstFlag
+      {
+        RPMINST_NONE       = 0x0000,
+        RPMINST_EXCLUDEDOCS= 0x0001,
+        RPMINST_NOSCRIPTS  = 0x0002,
+        RPMINST_FORCE      = 0x0004,
+        RPMINST_NODEPS     = 0x0008,
+        RPMINST_IGNORESIZE = 0x0010,
+        RPMINST_JUSTDB     = 0x0020,
+        RPMINST_NODIGEST   = 0x0040,
+        RPMINST_NOSIGNATURE= 0x0080,
+        RPMINST_NOUPGRADE  = 0x0100,
+        RPMINST_TEST      = 0x0200
+      };
+
+      /** \relates RpmInstFlag Type-safe way of storing OR-combinations. */
+      ZYPP_DECLARE_FLAGS_AND_OPERATORS( RpmInstFlags, RpmInstFlag );
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace rpm
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace target
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_TARGET_RPM_RPMFLAGS_H
diff --git a/zypp/target/rpm/RpmHeader.cc b/zypp/target/rpm/RpmHeader.cc
new file mode 100644 (file)
index 0000000..b83bcba
--- /dev/null
@@ -0,0 +1,1062 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.cc
+ *
+*/
+#include "librpm.h"
+#ifdef _RPM_4_4
+#include <rpm/ugid.h>
+#else
+////////////////////////////////////////////////////////////////////
+// unameToUid and gnameToGid are shamelessly stolen from rpm-4.4.
+// (rpmio/ugid.c) Those functions were dropped in RPM_4_7
+extern "C"
+{
+#include <pwd.h>
+#include <grp.h>
+}
+/* unameToUid(), uidTouname() and the group variants are really poorly
+   implemented. They really ought to use hash tables. I just made the
+   guess that most files would be owned by root or the same person/group
+   who owned the last file. Those two values are cached, everything else
+   is looked up via getpw() and getgr() functions.  If this performs
+   too poorly I'll have to implement it properly :-( */
+
+int unameToUid(const char * thisUname, uid_t * uid)
+{
+/*@only@*/ static char * lastUname = NULL;
+    static size_t lastUnameLen = 0;
+    static size_t lastUnameAlloced;
+    static uid_t lastUid;
+    struct passwd * pwent;
+    size_t thisUnameLen;
+
+    if (!thisUname) {
+       lastUnameLen = 0;
+       return -1;
+    } else if (strcmp(thisUname, "root") == 0) {
+/*@-boundswrite@*/
+       *uid = 0;
+/*@=boundswrite@*/
+       return 0;
+    }
+
+    thisUnameLen = strlen(thisUname);
+    if (lastUname == NULL || thisUnameLen != lastUnameLen ||
+       strcmp(thisUname, lastUname) != 0)
+    {
+       if (lastUnameAlloced < thisUnameLen + 1) {
+           lastUnameAlloced = thisUnameLen + 10;
+           lastUname = (char *)realloc(lastUname, lastUnameAlloced);   /* XXX memory leak */
+       }
+/*@-boundswrite@*/
+       strcpy(lastUname, thisUname);
+/*@=boundswrite@*/
+
+       pwent = getpwnam(thisUname);
+       if (pwent == NULL) {
+           /*@-internalglobs@*/ /* FIX: shrug */
+           endpwent();
+           /*@=internalglobs@*/
+           pwent = getpwnam(thisUname);
+           if (pwent == NULL) return -1;
+       }
+
+       lastUid = pwent->pw_uid;
+    }
+
+/*@-boundswrite@*/
+    *uid = lastUid;
+/*@=boundswrite@*/
+
+    return 0;
+}
+
+int gnameToGid(const char * thisGname, gid_t * gid)
+{
+/*@only@*/ static char * lastGname = NULL;
+    static size_t lastGnameLen = 0;
+    static size_t lastGnameAlloced;
+    static gid_t lastGid;
+    size_t thisGnameLen;
+    struct group * grent;
+
+    if (thisGname == NULL) {
+       lastGnameLen = 0;
+       return -1;
+    } else if (strcmp(thisGname, "root") == 0) {
+/*@-boundswrite@*/
+       *gid = 0;
+/*@=boundswrite@*/
+       return 0;
+    }
+
+    thisGnameLen = strlen(thisGname);
+    if (lastGname == NULL || thisGnameLen != lastGnameLen ||
+       strcmp(thisGname, lastGname) != 0)
+    {
+       if (lastGnameAlloced < thisGnameLen + 1) {
+           lastGnameAlloced = thisGnameLen + 10;
+           lastGname = (char *)realloc(lastGname, lastGnameAlloced);   /* XXX memory leak */
+       }
+/*@-boundswrite@*/
+       strcpy(lastGname, thisGname);
+/*@=boundswrite@*/
+
+       grent = getgrnam(thisGname);
+       if (grent == NULL) {
+           /*@-internalglobs@*/ /* FIX: shrug */
+           endgrent();
+           /*@=internalglobs@*/
+           grent = getgrnam(thisGname);
+           if (grent == NULL) {
+               /* XXX The filesystem package needs group/lock w/o getgrnam. */
+               if (strcmp(thisGname, "lock") == 0) {
+/*@-boundswrite@*/
+                   *gid = lastGid = 54;
+/*@=boundswrite@*/
+                   return 0;
+               } else
+               if (strcmp(thisGname, "mail") == 0) {
+/*@-boundswrite@*/
+                   *gid = lastGid = 12;
+/*@=boundswrite@*/
+                   return 0;
+               } else
+               return -1;
+           }
+       }
+       lastGid = grent->gr_gid;
+    }
+
+/*@-boundswrite@*/
+    *gid = lastGid;
+/*@=boundswrite@*/
+
+    return 0;
+}
+////////////////////////////////////////////////////////////////////
+#endif
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/Package.h"
+#include "zypp/PathInfo.h"
+
+using std::endl;
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::RpmHeader
+//        METHOD TYPE : Constructor
+//
+//        DESCRIPTION :
+//
+RpmHeader::RpmHeader( Header h_r )
+    : BinHeader( h_r )
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::RpmHeader
+//        METHOD TYPE : Constructor
+//
+RpmHeader::RpmHeader( BinHeader::Ptr & rhs )
+    : BinHeader( rhs )
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::~RpmHeader
+//        METHOD TYPE : Destructor
+//
+//        DESCRIPTION :
+//
+RpmHeader::~RpmHeader()
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::readPackage
+//        METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeader::readPackage( const Pathname & path_r,
+                                            VERIFICATION verification_r )
+{
+  PathInfo file( path_r );
+  if ( ! file.isFile() )
+  {
+    ERR << "Not a file: " << file << endl;
+    return (RpmHeader*)0;
+  }
+
+  FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+  if ( fd == 0 || ::Ferror(fd) )
+  {
+    ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+    if ( fd )
+      ::Fclose( fd );
+    return (RpmHeader*)0;
+  }
+
+  librpmDb::globalInit();
+  rpmts ts = ::rpmtsCreate();
+  unsigned vsflag = RPMVSF_DEFAULT;
+  if ( verification_r & NODIGEST )
+    vsflag |= _RPMVSF_NODIGESTS;
+  if ( verification_r & NOSIGNATURE )
+    vsflag |= _RPMVSF_NOSIGNATURES;
+  ::rpmtsSetVSFlags( ts, rpmVSFlags(vsflag) );
+
+  Header nh = 0;
+  int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), &nh );
+
+  ts = rpmtsFree(ts);
+
+  ::Fclose( fd );
+
+  if ( ! nh )
+  {
+    WAR << "Error reading header from " << path_r << " error(" << res << ")" << endl;
+    return (RpmHeader*)0;
+  }
+
+  RpmHeader::constPtr h( new RpmHeader( nh ) );
+  headerFree( nh ); // clear the reference set in ReadPackageFile
+
+  MIL << h << " from " << path_r << endl;
+  return h;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::dumpOn
+//        METHOD TYPE : std::ostream &
+//
+//        DESCRIPTION :
+//
+std::ostream & RpmHeader::dumpOn( std::ostream & str ) const
+{
+  return BinHeader::dumpOn( str ) << '{' << tag_name() << "-"
+         << (tag_epoch()==0?"":(tag_epoch()+":"))
+         << tag_version()
+         << (tag_release().empty()?"":(std::string("-")+tag_release()))
+         << ( isSrc() ? ".src}" : "}");
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::isSrc
+//        METHOD TYPE : bool
+//
+bool RpmHeader::isSrc() const
+{
+  return has_tag( RPMTAG_SOURCEPACKAGE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_name
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_name() const
+{
+  return string_val( RPMTAG_NAME );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_epoch
+//        METHOD TYPE : Edition::epoch_t
+//
+//        DESCRIPTION :
+//
+Edition::epoch_t RpmHeader::tag_epoch() const
+{
+  return int_val ( RPMTAG_EPOCH );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_version
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_version() const
+{
+  return string_val ( RPMTAG_VERSION );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_release
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_release() const
+{
+  return string_val( RPMTAG_RELEASE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_edition
+//        METHOD TYPE : Edition
+//
+//        DESCRIPTION :
+//
+Edition RpmHeader::tag_edition () const
+{
+  return Edition( tag_version(), tag_release(), tag_epoch() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_arch
+//        METHOD TYPE : Arch
+//
+//        DESCRIPTION :
+//
+Arch RpmHeader::tag_arch() const
+{
+  return Arch( string_val( RPMTAG_ARCH ) );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_installtime
+//        METHOD TYPE : Date
+//
+//        DESCRIPTION :
+//
+Date RpmHeader::tag_installtime() const
+{
+  return int_val( RPMTAG_INSTALLTIME );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_buildtime
+//        METHOD TYPE : Date
+//
+//        DESCRIPTION :
+//
+Date RpmHeader::tag_buildtime() const
+{
+  return int_val( RPMTAG_BUILDTIME );
+}
+#warning CHECK IF FILE REQUIRES HANDLING IS OBSOLETE
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::PkgRelList_val
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::PkgRelList_val( tag tag_r, bool pre, std::set<std::string> * freq_r ) const
+  {
+    CapabilitySet ret;
+
+    rpmTag  kindFlags   = rpmTag(0);
+    rpmTag  kindVersion = rpmTag(0);
+
+    switch ( tag_r )
+    {
+    case RPMTAG_REQUIRENAME:
+      kindFlags   = RPMTAG_REQUIREFLAGS;
+      kindVersion = RPMTAG_REQUIREVERSION;
+      break;
+    case RPMTAG_PROVIDENAME:
+      kindFlags   = RPMTAG_PROVIDEFLAGS;
+      kindVersion = RPMTAG_PROVIDEVERSION;
+      break;
+    case RPMTAG_OBSOLETENAME:
+      kindFlags   = RPMTAG_OBSOLETEFLAGS;
+      kindVersion = RPMTAG_OBSOLETEVERSION;
+      break;
+    case RPMTAG_CONFLICTNAME:
+      kindFlags   = RPMTAG_CONFLICTFLAGS;
+      kindVersion = RPMTAG_CONFLICTVERSION;
+      break;
+    case RPMTAG_ENHANCESNAME:
+      kindFlags   = RPMTAG_ENHANCESFLAGS;
+      kindVersion = RPMTAG_ENHANCESVERSION;
+      break;
+    case RPMTAG_SUGGESTSNAME:
+      kindFlags   = RPMTAG_SUGGESTSFLAGS;
+      kindVersion = RPMTAG_SUGGESTSVERSION;
+      break;
+    default:
+      INT << "Illegal RPMTAG_dependencyNAME " << tag_r << endl;
+      return ret;
+      break;
+    }
+
+    stringList names;
+    unsigned count = string_list( tag_r, names );
+    if ( !count )
+      return ret;
+
+    intList  flags;
+    int_list( kindFlags, flags );
+
+    stringList versions;
+    string_list( kindVersion, versions );
+
+    for ( unsigned i = 0; i < count; ++i )
+    {
+
+      std::string n( names[i] );
+
+      Rel op = Rel::ANY;
+      int32_t f = flags[i];
+      std::string v = versions[i];
+
+      if ( n[0] == '/' )
+      {
+        if ( freq_r )
+        {
+          freq_r->insert( n );
+        }
+      }
+      else
+      {
+        if ( v.size() )
+        {
+          switch ( f & RPMSENSE_SENSEMASK )
+          {
+          case RPMSENSE_LESS:
+            op = Rel::LT;
+            break;
+          case RPMSENSE_LESS|RPMSENSE_EQUAL:
+            op = Rel::LE;
+            break;
+          case RPMSENSE_GREATER:
+            op = Rel::GT;
+            break;
+          case RPMSENSE_GREATER|RPMSENSE_EQUAL:
+            op = Rel::GE;
+            break;
+          case RPMSENSE_EQUAL:
+            op = Rel::EQ;
+            break;
+          }
+        }
+      }
+      if ((pre && (f & RPMSENSE_PREREQ))
+          || ((! pre) && !(f & RPMSENSE_PREREQ)))
+      {
+        try
+        {
+          ret.insert( Capability( n, op, Edition(v) ) );
+        }
+        catch (Exception & excpt_r)
+        {
+          ZYPP_CAUGHT(excpt_r);
+          WAR << "Invalid capability: " << n << " " << op << " "
+          << v << endl;
+        }
+      }
+    }
+
+    return ret;
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_provides
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_provides( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_PROVIDENAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_requires
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_requires( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_REQUIRENAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_requires
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_prerequires( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_REQUIRENAME, true, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_conflicts
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_conflicts( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_CONFLICTNAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_obsoletes
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_obsoletes( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_OBSOLETENAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_enhances
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_enhances( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_ENHANCESNAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_suggests
+//        METHOD TYPE : CapabilitySet
+//
+//        DESCRIPTION :
+//
+CapabilitySet RpmHeader::tag_suggests( std::set<std::string> * freq_r ) const
+  {
+    return PkgRelList_val( RPMTAG_SUGGESTSNAME, false, freq_r );
+  }
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_size
+//        METHOD TYPE : ByteCount
+//
+//        DESCRIPTION :
+//
+ByteCount RpmHeader::tag_size() const
+{
+  return int_val( RPMTAG_SIZE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_archivesize
+//        METHOD TYPE : ByteCount
+//
+//        DESCRIPTION :
+//
+ByteCount RpmHeader::tag_archivesize() const
+{
+  return int_val( RPMTAG_ARCHIVESIZE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_summary
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_summary() const
+{
+  return string_val( RPMTAG_SUMMARY );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_description
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_description() const
+{
+  return string_val( RPMTAG_DESCRIPTION );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_group
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_group() const
+{
+  return string_val( RPMTAG_GROUP );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_vendor
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_vendor() const
+{
+  return string_val( RPMTAG_VENDOR );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_distribution
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_distribution() const
+{
+  return string_val( RPMTAG_DISTRIBUTION );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_license
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_license() const
+{
+  return string_val( RPMTAG_LICENSE );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_buildhost
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_buildhost() const
+{
+  return string_val( RPMTAG_BUILDHOST );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_packager
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_packager() const
+{
+  return string_val( RPMTAG_PACKAGER );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_url
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_url() const
+{
+  return string_val( RPMTAG_URL );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_os
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_os() const
+{
+  return string_val( RPMTAG_OS );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_prein
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_prein() const
+{
+  return string_val( RPMTAG_PREIN );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_postin
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_postin() const
+{
+  return string_val( RPMTAG_POSTIN );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_preun
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_preun() const
+{
+  return string_val( RPMTAG_PREUN );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_postun
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_postun() const
+{
+  return string_val( RPMTAG_POSTUN );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_sourcerpm
+//        METHOD TYPE : std::string
+//
+//        DESCRIPTION :
+//
+std::string RpmHeader::tag_sourcerpm() const
+{
+  return string_val( RPMTAG_SOURCERPM );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_filenames
+//        METHOD TYPE : std::list<std::string>
+//
+//        DESCRIPTION :
+//
+std::list<std::string> RpmHeader::tag_filenames() const
+{
+  std::list<std::string> ret;
+
+  stringList basenames;
+  if ( string_list( RPMTAG_BASENAMES, basenames ) )
+  {
+    stringList dirnames;
+    string_list( RPMTAG_DIRNAMES, dirnames );
+    intList  dirindexes;
+    int_list( RPMTAG_DIRINDEXES, dirindexes );
+    for ( unsigned i = 0; i < basenames.size(); ++ i )
+    {
+      ret.push_back( dirnames[dirindexes[i]] + basenames[i] );
+    }
+  }
+
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_fileinfos
+//        METHOD TYPE : std::list<FileInfo>
+//
+//        DESCRIPTION :
+//
+std::list<FileInfo> RpmHeader::tag_fileinfos() const
+{
+  std::list<FileInfo> ret;
+
+  stringList basenames;
+  if ( string_list( RPMTAG_BASENAMES, basenames ) )
+  {
+    stringList dirnames;
+    string_list( RPMTAG_DIRNAMES, dirnames );
+    intList  dirindexes;
+    int_list( RPMTAG_DIRINDEXES, dirindexes );
+    intList filesizes;
+    int_list( RPMTAG_FILESIZES, filesizes );
+    stringList md5sums;
+    string_list( RPMTAG_FILEMD5S, md5sums );
+    stringList usernames;
+    string_list( RPMTAG_FILEUSERNAME, usernames );
+    stringList groupnames;
+    string_list( RPMTAG_FILEGROUPNAME, groupnames );
+    intList uids;
+    int_list( RPMTAG_FILEUIDS, uids );
+    intList gids;
+    int_list( RPMTAG_FILEGIDS, gids );
+    intList filemodes;
+    int_list( RPMTAG_FILEMODES, filemodes );
+    intList filemtimes;
+    int_list( RPMTAG_FILEMTIMES, filemtimes );
+    intList fileflags;
+    int_list( RPMTAG_FILEFLAGS, fileflags );
+    stringList filelinks;
+    string_list( RPMTAG_FILELINKTOS, filelinks );
+
+    for ( unsigned i = 0; i < basenames.size(); ++ i )
+    {
+      uid_t uid;
+      if (uids.empty())
+      {
+        uid = unameToUid( usernames[i].c_str(), &uid );
+      }
+      else
+      {
+        uid =uids[i];
+      }
+
+      gid_t gid;
+      if (gids.empty())
+      {
+        gid = gnameToGid( groupnames[i].c_str(), &gid );
+      }
+      else
+      {
+        gid = gids[i];
+      }
+
+      FileInfo info = {
+                        dirnames[dirindexes[i]] + basenames[i],
+                        filesizes[i],
+                        md5sums[i],
+                        uid,
+                        gid,
+                        filemodes[i],
+                        filemtimes[i],
+                        fileflags[i] & RPMFILE_GHOST,
+                        filelinks[i]
+                      };
+
+      ret.push_back( info );
+    }
+  }
+
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_changelog
+//        METHOD TYPE : Changelog
+//
+//        DESCRIPTION :
+//
+Changelog RpmHeader::tag_changelog() const
+{
+  Changelog ret;
+
+  intList times;
+  if ( int_list( RPMTAG_CHANGELOGTIME, times ) )
+  {
+    stringList names;
+    string_list( RPMTAG_CHANGELOGNAME, names );
+    stringList texts;
+    string_list( RPMTAG_CHANGELOGTEXT, texts );
+    for ( unsigned i = 0; i < times.size(); ++ i )
+    {
+      ret.push_back( ChangelogEntry( times[i], names[i], texts[i] ) );
+    }
+  }
+
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//        METHOD NAME : RpmHeader::tag_du
+//        METHOD TYPE : PkgDu &
+//
+//        DESCRIPTION :
+//
+DiskUsage & RpmHeader::tag_du( DiskUsage & dudata_r ) const
+{
+  dudata_r.clear();
+  stringList basenames;
+  if ( string_list( RPMTAG_BASENAMES, basenames ) )
+  {
+    stringList dirnames;
+    string_list( RPMTAG_DIRNAMES, dirnames );
+    intList dirindexes;
+    int_list( RPMTAG_DIRINDEXES, dirindexes );
+
+    intList filedevices;
+    int_list( RPMTAG_FILEDEVICES, filedevices );
+    intList fileinodes;
+    int_list( RPMTAG_FILEINODES, fileinodes );
+    intList filesizes;
+    int_list( RPMTAG_FILESIZES, filesizes );
+    intList filemodes;
+    int_list( RPMTAG_FILEMODES, filemodes );
+
+    ///////////////////////////////////////////////////////////////////
+    // Create and collect Entries by index. devino_cache is used to
+    // filter out hardliks ( different name but same device and inode ).
+    ///////////////////////////////////////////////////////////////////
+    filesystem::DevInoCache trace;
+    std::vector<DiskUsage::Entry> entries;
+    entries.resize( dirnames.size() );
+    for ( unsigned i = 0; i < dirnames.size(); ++i )
+    {
+      entries[i] = DiskUsage::Entry( dirnames[i] );
+
+      // cut off deeper directory levels in DiskUsage::Entry
+      unsigned level             = 3; // number of '/' incl. a trailing one
+      std::string::size_type pos = 0; // we know rpm stores absolute pathnames
+      while ( --level && pos != std::string::npos )
+      {
+        pos = entries[i].path.find( "/", pos+1 );
+      }
+      if ( pos != std::string::npos )
+      {
+        entries[i].path.erase( pos+1 );
+      }
+    }
+
+    for ( unsigned i = 0; i < basenames.size(); ++ i )
+    {
+      filesystem::StatMode mode( filemodes[i] );
+      if ( mode.isFile() )
+      {
+        if ( trace.insert( filedevices[i], fileinodes[i] ) )
+        {
+          // Count full 1K blocks
+          entries[dirindexes[i]]._size += ByteCount( filesizes[i] ).blocks( ByteCount::K );
+          ++(entries[dirindexes[i]]._files);
+        }
+        // else: hardlink; already counted this device/inode
+      }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // Collect all enties. We first unify the duplicate entries that
+    // were created by cutting off deeper levels. Then the size of each
+    // directory must also be added to each of it's parent directories.
+    ///////////////////////////////////////////////////////////////////
+    DiskUsage tmpdata;
+    for ( unsigned i = 0; i < entries.size(); ++i )
+    {
+      if ( entries[i]._size )
+        tmpdata.add( entries[i] );
+    }
+
+    for_( it, tmpdata.begin(), tmpdata.end() )
+    {
+      DiskUsage::Entry ent( *it );
+
+      do {
+        dudata_r.add( ent );
+        if ( ent.path.size() <= 1 ) // "" or "/"
+          break;
+
+        // set path to parent dir. Note that DiskUsage::Entry
+        // has leading and trailing '/' on pathnmes.
+        std::string::size_type rstart = ent.path.size() - 2;           // trailing '/' !
+        std::string::size_type lpos   = ent.path.rfind( '/', rstart ); // one but last '/'
+        if ( lpos == std::string::npos )
+          break;
+
+        ent.path.erase( lpos + 1 );
+      } while( true );
+    }
+  }
+  return dudata_r;
+}
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/RpmHeader.h b/zypp/target/rpm/RpmHeader.h
new file mode 100644 (file)
index 0000000..aa6d8b2
--- /dev/null
@@ -0,0 +1,200 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMHEADER_H
+#define ZYPP_TARGET_RPM_RPMHEADER_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/target/rpm/BinHeader.h"
+
+#include "zypp/Package.h"
+#include "zypp/Changelog.h"
+#include "zypp/Pathname.h"
+#include "zypp/DiskUsage.h"
+
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+struct FileInfo
+{
+  Pathname    filename;
+  ByteCount   size;
+  std::string md5sum;
+  uid_t       uid;
+  gid_t       gid;
+  mode_t      mode;
+  time_t      mtime;
+  bool        ghost;
+  Pathname link_target;
+};
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmHeader
+/**
+ * @short Wrapper class for rpm header struct.
+ *
+ * <code>RpmHeader</code> provides methods to query the content
+ * of a rpm header struct retrieved from the RPM database or by reading
+ * the rpm header of a package on disk.
+ *
+ * The rpm header contains all data associated with a package. So you
+ * probabely do not want to permanently store too many of them.
+ *
+ * <B>NEVER create <code>RpmHeader</code> from a NULL <code>Header</code>! </B>
+ **/
+class RpmHeader : public BinHeader
+{
+public:
+  typedef intrusive_ptr<RpmHeader> Ptr;
+  typedef intrusive_ptr<const RpmHeader> constPtr;
+
+private:
+
+  CapabilitySet PkgRelList_val( tag tag_r, bool pre, std::set<std::string> * freq_r = 0 ) const;
+
+public:
+
+  /**
+   *
+   **/
+  RpmHeader( Header h_r = 0 );
+
+  /**
+   * <B>Dangerous!<\B> This one takes the header out of rhs
+   * and leaves rhs empty.
+   **/
+  RpmHeader( BinHeader::Ptr & rhs );
+
+  virtual ~RpmHeader();
+
+  bool isSrc() const;
+
+public:
+
+  std::string      tag_name()    const;
+  Edition::epoch_t tag_epoch()   const;
+  std::string      tag_version() const;
+  std::string      tag_release() const;
+  Edition          tag_edition() const;
+  Arch             tag_arch()    const;
+
+  Date tag_installtime() const;
+  Date tag_buildtime()   const;
+
+  /**
+   * If <code>freq_r</code> is not NULL, file dependencies found are inserted.
+   **/
+  CapabilitySet tag_provides ( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_requires ( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_prerequires ( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_conflicts( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_obsoletes( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_enhances( std::set<std::string> * freq_r = 0 ) const;
+  /**
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_suggests( std::set<std::string> * freq_r = 0 ) const;
+  /** Unsupported by rpm.
+   * @see #tag_provides
+   **/
+  CapabilitySet tag_supplements( std::set<std::string> * freq_r = 0 ) const
+  { return CapabilitySet(); }
+
+  ByteCount tag_size()        const;
+  ByteCount tag_archivesize() const;
+
+  std::string tag_summary()      const;
+  std::string tag_description()  const;
+  std::string tag_group()        const;
+  std::string tag_vendor()       const;
+  std::string tag_distribution() const;
+  std::string tag_license()      const;
+  std::string tag_buildhost()    const;
+  std::string tag_packager()     const;
+  std::string tag_url()          const;
+  std::string tag_os()           const;
+  std::string tag_prein()        const;
+  std::string tag_postin()       const;
+  std::string tag_preun()        const;
+  std::string tag_postun()       const;
+  std::string tag_sourcerpm()    const;
+
+  /** just the list of names  */
+  std::list<std::string> tag_filenames() const;
+
+  /**
+   * complete information about the files
+   * (extended version of tag_filenames())
+          */
+  std::list<FileInfo> tag_fileinfos() const;
+
+  Changelog tag_changelog() const;
+
+  /**
+   * Returns reference to arg <code>dudata_r</code>.
+   **/
+  DiskUsage & tag_du( DiskUsage & dudata_r ) const;
+
+public:
+
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+public:
+
+  /**
+   * Digest and signature verification flags
+   **/
+  enum VERIFICATION
+  {
+    VERIFY       = 0x0000,
+    NODIGEST     = (1<<0),
+    NOSIGNATURE  = (1<<1),
+    NOVERIFY     = 0xffff
+  };
+
+  /**
+   * Get an accessible packages data from disk.
+   * Returns NULL on any error.
+   **/
+  static RpmHeader::constPtr readPackage( const Pathname & path,
+                                          VERIFICATION verification = VERIFY );
+};
+
+///////////////////////////////////////////////////////////////////
+} // namespace rpm
+} // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMHEADER_H
+
diff --git a/zypp/target/rpm/librpm.h b/zypp/target/rpm/librpm.h
new file mode 100644 (file)
index 0000000..2d7425d
--- /dev/null
@@ -0,0 +1,34 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/librpm.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_LIBRPM_H
+#define ZYPP_TARGET_RPM_LIBRPM_H
+
+#ifdef _RPM_5
+// needs to be outside 'extern "C"'
+#include <rpm/rpm4compat.h>
+#endif // _RPM_5
+
+extern "C"
+{
+#ifdef _RPM_5
+#include <rpm/rpmtag.h>
+#else
+#include <rpm/rpmlib.h>
+#endif // _RPM_5
+
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmts.h>
+#include <fcntl.h>
+}
+
+#endif // ZYPP_TARGET_RPM_LIBRPM_H
diff --git a/zypp/target/rpm/librpmDb.cc b/zypp/target/rpm/librpmDb.cc
new file mode 100644 (file)
index 0000000..3f472f4
--- /dev/null
@@ -0,0 +1,928 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/librpmDb.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/PathInfo.h"
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/target/rpm/RpmException.h"
+
+using namespace std;
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::D
+/**
+ * @short librpmDb internal database handle
+ **/
+class librpmDb::D
+{
+  D & operator=( const D & ); // NO ASSIGNMENT!
+  D ( const D & );            // NO COPY!
+public:
+
+  const Pathname _root;   // root directory for all operations
+  const Pathname _dbPath; // directory (below root) that contains the rpmdb
+  rpmts _ts;              // transaction handle, includes database
+  shared_ptr<RpmException> _error;  // database error
+
+  friend ostream & operator<<( ostream & str, const D & obj )
+  {
+    str << "{" << obj._error  << "(" << obj._root << ")" << obj._dbPath << "}";
+    return str;
+  }
+
+  D( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+      : _root  ( root_r )
+      , _dbPath( dbPath_r )
+      , _ts    ( 0 )
+  {
+    _error.reset();
+    // set %_dbpath macro
+    ::addMacro( NULL, "_dbpath", NULL, _dbPath.asString().c_str(), RMIL_CMDLINE );
+
+    _ts = ::rpmtsCreate();
+    ::rpmtsSetRootDir( _ts, _root.c_str() );
+
+    // check whether to create a new db
+    PathInfo master( _root + _dbPath + "Packages" );
+    if ( ! master.isFile() )
+    {
+      // init database
+      if ( filesystem::assert_dir(_root + _dbPath) != 0 )
+      {
+        ERR << "Could not create dbpath " << (_root + _dbPath).asString() << endl;
+        _error = shared_ptr<RpmInitException>(new RpmInitException(_root, _dbPath));
+        ZYPP_THROW(*_error);
+      }
+      int res = ::rpmtsInitDB( _ts, 0644 );
+      if ( res )
+      {
+        ERR << "rpmdbInit error(" << res << "): " << *this << endl;
+        _error = shared_ptr<RpmInitException>(new RpmInitException(_root, _dbPath));
+       rpmtsFree(_ts);
+        ZYPP_THROW(*_error);
+      }
+    }
+
+    // open database
+    int res = ::rpmtsOpenDB( _ts, (readonly_r ? O_RDONLY : O_RDWR ));
+    if ( res )
+    {
+      ERR << "rpmdbOpen error(" << res << "): " << *this << endl;
+      _error = shared_ptr<RpmDbOpenException>(new RpmDbOpenException(_root, _dbPath));
+      rpmtsFree(_ts);
+      ZYPP_THROW(*_error);
+      return;
+    }
+
+    DBG << "DBACCESS " << *this << endl;
+  }
+
+  ~D()
+  {
+    if ( _ts )
+    {
+      ::rpmtsFree(_ts);
+    }
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb (ststic interface)
+//
+///////////////////////////////////////////////////////////////////
+
+Pathname         librpmDb::_defaultRoot  ( "/" );
+Pathname         librpmDb::_defaultDbPath( "/var/lib/rpm" );
+librpmDb::constPtr librpmDb::_defaultDb;
+bool             librpmDb::_dbBlocked    ( true );
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::globalInit
+//     METHOD TYPE : bool
+//
+bool librpmDb::globalInit()
+{
+  static bool initialized = false;
+
+  if ( initialized )
+    return true;
+
+  int rc = ::rpmReadConfigFiles( NULL, NULL );
+  if ( rc )
+  {
+    ERR << "rpmReadConfigFiles returned " << rc << endl;
+    return false;
+  }
+
+  initialized = true; // Necessary to be able to use exand().
+
+#define OUTVAL(n) << " (" #n ":" << expand( "%{" #n "}" ) << ")"
+  MIL << "librpm init done:"
+  OUTVAL(_target)
+  OUTVAL(_dbpath)
+  << endl;
+#undef OUTVAL
+  return initialized;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::expand
+//     METHOD TYPE : std::string
+//
+std::string librpmDb::expand( const std::string & macro_r )
+{
+  if ( ! globalInit() )
+    return macro_r;  // unexpanded
+
+  char * val = ::rpmExpand( macro_r.c_str(), NULL );
+  if ( !val )
+    return "";
+
+  string ret( val );
+  free( val );
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::newLibrpmDb
+//     METHOD TYPE : librpmDb *
+//
+librpmDb * librpmDb::newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r )
+{
+  // check arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+  {
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+
+  // initialize librpm
+  if ( ! globalInit() )
+  {
+    ZYPP_THROW(GlobalRpmInitException());
+  }
+
+  // open rpmdb
+  librpmDb * ret = 0;
+  try
+  {
+    ret = new librpmDb( root_r, dbPath_r, readonly_r );
+  }
+  catch (const RpmException & excpt_r)
+  {
+    ZYPP_CAUGHT(excpt_r);
+    delete ret;
+    ret = 0;
+    ZYPP_RETHROW(excpt_r);
+  }
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( const Pathname & root_r, const Pathname & dbPath_r )
+{
+  // check arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+  {
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+
+  if ( _defaultDb )
+  {
+    // already accessing a database: switching is not allowed.
+    if ( _defaultRoot == root_r && _defaultDbPath == dbPath_r )
+      return;
+    else
+    {
+      ZYPP_THROW(RpmDbAlreadyOpenException(_defaultRoot, _defaultDbPath, root_r, dbPath_r));
+    }
+  }
+
+  // got no database: we could switch to a new one (even if blocked!)
+  _defaultRoot = root_r;
+  _defaultDbPath = dbPath_r;
+  MIL << "Set new database location: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+
+  return dbAccess();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess()
+{
+  if ( _dbBlocked )
+  {
+    ZYPP_THROW(RpmAccessBlockedException(_defaultRoot, _defaultDbPath));
+  }
+
+  if ( !_defaultDb )
+  {
+    // get access
+    _defaultDb = newLibrpmDb( _defaultRoot, _defaultDbPath, /*readonly*/true );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( librpmDb::constPtr & ptr_r )
+{
+  try
+  {
+    dbAccess();
+  }
+  catch (const RpmException & excpt_r)
+  {
+    ZYPP_CAUGHT(excpt_r);
+    ptr_r = 0;
+    ZYPP_RETHROW(excpt_r);
+  }
+  ptr_r = _defaultDb;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbRelease
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::dbRelease( bool force_r )
+{
+  if ( !_defaultDb )
+  {
+    return 0;
+  }
+
+  unsigned outstanding = _defaultDb->refCount() - 1; // refCount can't be 0
+
+  switch ( outstanding )
+  {
+  default:
+    if ( !force_r )
+    {
+      DBG << "dbRelease: keep access, outstanding " << outstanding << endl;
+      break;
+    }
+    // else fall through:
+  case 0:
+    DBG << "dbRelease: release" << (force_r && outstanding ? "(forced)" : "")
+    << ", outstanding " << outstanding << endl;
+
+    _defaultDb->_d._error = shared_ptr<RpmAccessBlockedException>(new RpmAccessBlockedException(_defaultDb->_d._root, _defaultDb->_d._dbPath));
+    // tag handle invalid
+    _defaultDb = 0;
+    break;
+  }
+
+  return outstanding;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::blockAccess
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::blockAccess()
+{
+  MIL << "Block access" << endl;
+  _dbBlocked = true;
+  return dbRelease( /*force*/true );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::unblockAccess
+//     METHOD TYPE : void
+//
+void librpmDb::unblockAccess()
+{
+  MIL << "Unblock access" << endl;
+  _dbBlocked = false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dumpState
+//     METHOD TYPE : ostream &
+//
+ostream & librpmDb::dumpState( ostream & str )
+{
+  if ( !_defaultDb )
+  {
+    return str << "[librpmDb " << (_dbBlocked?"BLOCKED":"CLOSED") << " " << stringPath( _defaultRoot, _defaultDbPath ) << "]";
+  }
+  return str << "[" << _defaultDb << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb (internal database handle interface (nonstatic))
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::librpmDb
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+librpmDb::librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+    : _d( * new D( root_r, dbPath_r, readonly_r ) )
+{}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::~librpmDb
+//     METHOD TYPE : Destructor
+//
+//     DESCRIPTION :
+//
+librpmDb::~librpmDb()
+{
+  delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::unref_to
+//     METHOD TYPE : void
+//
+void librpmDb::unref_to( unsigned refCount_r ) const
+{
+  if ( refCount_r == 1 )
+  {
+    dbRelease();
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::root
+//     METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::root() const
+{
+  return _d._root;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbPath
+//     METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::dbPath() const
+{
+  return _d._dbPath;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::error
+//     METHOD TYPE : PMError
+//
+shared_ptr<RpmException> librpmDb::error() const
+{
+  return _d._error;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::empty
+//     METHOD TYPE : bool
+//
+bool librpmDb::empty() const
+{
+  return( valid() && ! *db_const_iterator( this ) );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::size
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::size() const
+{
+  unsigned count = 0;
+  if ( valid() )
+  {
+    db_const_iterator it( this );
+    for ( db_const_iterator it( this ); *it; ++it )
+      ++count;
+  }
+  return count;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dont_call_it
+//     METHOD TYPE : void *
+//
+void * librpmDb::dont_call_it() const
+{
+  return rpmtsGetRdb(_d._ts);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dumpOn
+//     METHOD TYPE : ostream &
+//
+//     DESCRIPTION :
+//
+ostream & librpmDb::dumpOn( ostream & str ) const
+{
+  ReferenceCounted::dumpOn( str ) << _d;
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::DbDirInfo
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::DbDirInfo::DbDirInfo
+//     METHOD TYPE : Constructor
+//
+librpmDb::DbDirInfo::DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r )
+    : _root( root_r )
+    , _dbPath( dbPath_r )
+{
+  // check and adjust arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+  {
+    ERR << "Relative path for root(" << _root << ") or dbPath(" << _dbPath << ")" << endl;
+  }
+  else
+  {
+    _dbDir   ( _root + _dbPath );
+    _dbV4    ( _dbDir.path() + "Packages" );
+    _dbV3    ( _dbDir.path() + "packages.rpm" );
+    _dbV3ToV4( _dbDir.path() + "packages.rpm3" );
+    DBG << *this << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::DbDirInfo::update
+//     METHOD TYPE : void
+//
+void librpmDb::DbDirInfo::restat()
+{
+  _dbDir();
+  _dbV4();
+  _dbV3();
+  _dbV3ToV4();
+  DBG << *this << endl;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : std::ostream &
+*/
+std::ostream & operator<<( std::ostream & str, const librpmDb::DbDirInfo & obj )
+{
+  if ( obj.illegalArgs() )
+  {
+    str << "ILLEGAL: '(" << obj.root() << ")" << obj.dbPath() << "'";
+  }
+  else
+  {
+    str << "'(" << obj.root() << ")" << obj.dbPath() << "':" << endl;
+    str << "  Dir:    " << obj._dbDir << endl;
+    str << "  V4:     " << obj._dbV4 << endl;
+    str << "  V3:     " << obj._dbV3 << endl;
+    str << "  V3ToV4: " << obj._dbV3ToV4;
+  }
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::db_const_iterator::D
+/**
+ *
+ **/
+class librpmDb::db_const_iterator::D
+{
+  D & operator=( const D & ); // NO ASSIGNMENT!
+  D ( const D & );            // NO COPY!
+public:
+
+  librpmDb::constPtr     _dbptr;
+  shared_ptr<RpmException> _dberr;
+
+  RpmHeader::constPtr _hptr;
+  rpmdbMatchIterator   _mi;
+
+  D( librpmDb::constPtr dbptr_r )
+      : _dbptr( dbptr_r )
+      , _mi( 0 )
+  {
+    if ( !_dbptr )
+    {
+      try
+      {
+        librpmDb::dbAccess( _dbptr );
+      }
+      catch (const RpmException & excpt_r)
+      {
+        ZYPP_CAUGHT(excpt_r);
+      }
+      if ( !_dbptr )
+      {
+        WAR << "No database access: " << _dberr << endl;
+      }
+    }
+    else
+    {
+      destroy(); // Checks whether _dbptr still valid
+    }
+  }
+
+  ~D()
+  {
+    if ( _mi )
+    {
+      ::rpmdbFreeIterator( _mi );
+    }
+  }
+
+  /**
+   * Let iterator access a dbindex file. Call @ref advance to access the
+   * 1st element (if present).
+   **/
+  bool create( int rpmtag, const void * keyp = NULL, size_t keylen = 0 )
+  {
+    destroy();
+    if ( ! _dbptr )
+      return false;
+    _mi = ::rpmtsInitIterator( _dbptr->_d._ts, rpmTag(rpmtag), keyp, keylen );
+    return _mi;
+  }
+
+  /**
+   * Destroy iterator. Invalidates _dbptr, if database was blocked meanwile.
+   * Always returns false.
+   **/
+  bool destroy()
+  {
+    if ( _mi )
+    {
+      _mi = ::rpmdbFreeIterator( _mi );
+      _hptr = 0;
+    }
+    if ( _dbptr && _dbptr->error() )
+    {
+      _dberr = _dbptr->error();
+      WAR << "Lost database access: " << _dberr << endl;
+      _dbptr = 0;
+    }
+    return false;
+  }
+
+  /**
+   * Advance to the first/next header in iterator. Destroys iterator if
+   * no more headers available.
+   **/
+  bool advance()
+  {
+    if ( !_mi )
+      return false;
+    Header h = ::rpmdbNextIterator( _mi );
+    if ( ! h )
+    {
+      destroy();
+      return false;
+    }
+    _hptr = new RpmHeader( h );
+    return true;
+  }
+
+  /**
+   * Access a dbindex file and advance to the 1st header.
+   **/
+  bool init( int rpmtag, const void * keyp = NULL, size_t keylen = 0 )
+  {
+    if ( ! create( rpmtag, keyp, keylen ) )
+      return false;
+    return advance();
+  }
+
+  /**
+   * Create an itertator that contains the database entry located at
+   * off_r, and advance to the 1st header.
+   **/
+  bool set( int off_r )
+  {
+    if ( ! create( RPMDBI_PACKAGES ) )
+      return false;
+#warning TESTCASE: rpmdbAppendIterator and (non)sequential access?
+    ::rpmdbAppendIterator( _mi, &off_r, 1 );
+    return advance();
+  }
+
+  unsigned offset()
+  {
+    return( _mi ? ::rpmdbGetIteratorOffset( _mi ) : 0 );
+  }
+
+  int size()
+  {
+    if ( !_mi )
+      return 0;
+    int ret = ::rpmdbGetIteratorCount( _mi );
+#warning TESTCASE: rpmdbGetIteratorCount returns 0 on sequential access?
+    return( ret ? ret : -1 ); // -1: sequential access
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::Ptr::db_const_iterator
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::db_iterator
+//     METHOD TYPE : Constructor
+//
+librpmDb::db_const_iterator::db_const_iterator( librpmDb::constPtr dbptr_r )
+    : _d( * new D( dbptr_r ) )
+{
+  findAll();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::~db_const_iterator
+//     METHOD TYPE : Destructor
+//
+librpmDb::db_const_iterator::~db_const_iterator()
+{
+  delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::operator++
+//     METHOD TYPE : void
+//
+void librpmDb::db_const_iterator::operator++()
+{
+  _d.advance();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::dbHdrNum
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::db_const_iterator::dbHdrNum() const
+{
+  return _d.offset();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::operator*
+//     METHOD TYPE : const RpmHeader::constPtr &
+//
+const RpmHeader::constPtr & librpmDb::db_const_iterator::operator*() const
+{
+  return _d._hptr;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::dbError
+//     METHOD TYPE : PMError
+//
+shared_ptr<RpmException> librpmDb::db_const_iterator::dbError() const
+{
+  if ( _d._dbptr )
+    return _d._dbptr->error();
+
+  return _d._dberr;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const librpmDb::db_const_iterator & obj )
+{
+  str << "db_const_iterator(" << obj._d._dbptr
+  << " Size:" << obj._d.size()
+  << " HdrNum:" << obj._d.offset()
+  << ")";
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findAll
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findAll()
+{
+  return _d.init( RPMDBI_PACKAGES );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByFile
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByFile( const std::string & file_r )
+{
+  return _d.init( RPMTAG_BASENAMES, file_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByProvides
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByProvides( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_PROVIDENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByRequiredBy
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByRequiredBy( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_REQUIRENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByConflicts
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByConflicts( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_CONFLICTNAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::findByName
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByName( const string & name_r )
+{
+  return _d.init( RPMTAG_NAME, name_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const string & name_r )
+{
+  if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+    return false;
+
+  if ( _d.size() == 1 )
+    return true;
+
+  // check installtime on multiple entries
+  int match    = 0;
+  time_t itime = 0;
+  for ( ; operator*(); operator++() )
+  {
+    if ( operator*()->tag_installtime() > itime )
+    {
+      match = _d.offset();
+      itime = operator*()->tag_installtime();
+    }
+  }
+
+  return _d.set( match );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const std::string & name_r, const Edition & ed_r )
+{
+  if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+    return false;
+
+  for ( ; operator*(); operator++() )
+  {
+    if ( ed_r == operator*()->tag_edition() )
+    {
+      int match = _d.offset();
+      return _d.set( match );
+    }
+  }
+
+  return _d.destroy();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const Package::constPtr & which_r )
+{
+  if ( ! which_r )
+    return _d.destroy();
+
+  return findPackage( which_r->name(), which_r->edition() );
+}
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/librpmDb.cv3.cc b/zypp/target/rpm/librpmDb.cv3.cc
new file mode 100644 (file)
index 0000000..185f7af
--- /dev/null
@@ -0,0 +1,750 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/librpmDb.cv3.cc
+ *
+ */
+#if 0
+#include "librpm.h"
+extern "C"
+{
+#ifdef _RPM_5
+typedef rpmuint32_t rpm_count_t;
+#define HGEPtr_t void *
+#define headerGetEntryMinMemory headerGetEntry
+#define headerNVR(h,n,v,r) headerNEVRA(h,n,NULL,v,r,NULL)
+#else
+#ifdef _RPM_4_4
+typedef int32_t rpm_count_t;
+#define HGEPtr_t const void *
+#endif
+#endif
+}
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmCallbacks.h"
+#include "zypp/ZYppCallbacks.h"
+
+#define xmalloc malloc
+#define xstrdup strdup
+
+extern "C"
+{
+#include <string.h>
+
+#define FA_MAGIC      0x02050920
+
+  struct faFileHeader
+  {
+    unsigned int magic;
+    unsigned int firstFree;
+  };
+
+  struct faHeader
+  {
+    unsigned int size;
+    unsigned int freeNext; /* offset of the next free block, 0 if none */
+    unsigned int freePrev;
+    unsigned int isFree;
+
+    /* note that the u16's appear last for alignment/space reasons */
+  };
+}
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+static int fadFileSize;
+
+static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
+{
+  if (Fseek(fd, offset, SEEK_SET) < 0)
+    return -1;
+  return Fread(buf, sizeof(char), count, fd);
+}
+
+static FD_t fadOpen(const char * path)
+{
+  struct faFileHeader newHdr;
+  FD_t fd;
+  struct stat stb;
+
+  fd = Fopen(path, "r.fdio");
+  if (!fd || Ferror(fd))
+    return NULL;
+
+  if (fstat(Fileno(fd), &stb))
+  {
+    Fclose(fd);
+    return NULL;
+  }
+  fadFileSize = stb.st_size;
+
+  /* is this file brand new? */
+  if (fadFileSize == 0)
+  {
+    Fclose(fd);
+    return NULL;
+  }
+  if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
+  {
+    Fclose(fd);
+    return NULL;
+  }
+  if (newHdr.magic != FA_MAGIC)
+  {
+    Fclose(fd);
+    return NULL;
+  }
+  /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
+}
+
+static int fadNextOffset(FD_t fd, unsigned int lastOffset)
+{
+  struct faHeader header;
+  int offset;
+
+  offset = (lastOffset)
+           ? (lastOffset - sizeof(header))
+           : sizeof(struct faFileHeader);
+
+  if (offset >= fadFileSize)
+    return 0;
+
+  if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
+    return 0;
+
+  if (!lastOffset && !header.isFree)
+    return (offset + sizeof(header));
+
+  do
+  {
+    offset += header.size;
+
+    if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
+      return 0;
+
+    if (!header.isFree) break;
+  }
+  while (offset < fadFileSize && header.isFree);
+
+  if (offset < fadFileSize)
+  {
+    /* Sanity check this to make sure we're not going in loops */
+    offset += sizeof(header);
+
+    if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
+
+    return offset;
+  }
+  else
+    return 0;
+}
+
+static int fadFirstOffset(FD_t fd)
+{
+  return fadNextOffset(fd, 0);
+}
+
+/*@-boundsread@*/
+static int dncmp(const void * a, const void * b)
+/*@*/
+{
+  const char *const * first = (const char *const *)a;
+  const char *const * second = (const char *const *)b;
+  return strcmp(*first, *second);
+}
+/*@=boundsread@*/
+
+/*@-bounds@*/
+#ifndef _RPM_4_X
+static void compressFilelist(Header h)
+/*@*/
+{
+  char ** fileNames;
+  const char ** dirNames;
+  const char ** baseNames;
+  int_32 * dirIndexes;
+  rpmTagType fnt;
+  rpm_count_t count;
+  int xx;
+  int dirIndex = -1;
+
+  /*
+   * This assumes the file list is already sorted, and begins with a
+   * single '/'. That assumption isn't critical, but it makes things go
+   * a bit faster.
+   */
+
+  if (headerIsEntry(h, RPMTAG_DIRNAMES))
+  {
+    xx = headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
+    return;            /* Already converted. */
+  }
+
+  HGEPtr_t hgePtr = NULL;
+  if (!headerGetEntryMinMemory(h, RPMTAG_OLDFILENAMES, hTYP_t(&fnt), &hgePtr, &count))
+    return;            /* no file list */
+  fileNames = (char **)hgePtr;
+  if (fileNames == NULL || count <= 0)
+    return;
+
+  dirNames = (const char **)alloca(sizeof(*dirNames) * count); /* worst case */
+  baseNames = (const char **)alloca(sizeof(*dirNames) * count);
+  dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
+
+  if (fileNames[0][0] != '/')
+  {
+    /* HACK. Source RPM, so just do things differently */
+    dirIndex = 0;
+    dirNames[dirIndex] = "";
+    for (rpm_count_t i = 0; i < count; i++)
+    {
+      dirIndexes[i] = dirIndex;
+      baseNames[i] = fileNames[i];
+    }
+    goto exit;
+  }
+
+  /*@-branchstate@*/
+  for (rpm_count_t i = 0; i < count; i++)
+  {
+    const char ** needle;
+    char savechar;
+    char * baseName;
+    int len;
+
+    if (fileNames[i] == NULL)  /* XXX can't happen */
+      continue;
+    baseName = strrchr(fileNames[i], '/') + 1;
+    len = baseName - fileNames[i];
+    needle = dirNames;
+    savechar = *baseName;
+    *baseName = '\0';
+    /*@-compdef@*/
+    if (dirIndex < 0 ||
+        (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
+    {
+      char *s = (char *)alloca(len + 1);
+      memcpy(s, fileNames[i], len + 1);
+      s[len] = '\0';
+      dirIndexes[i] = ++dirIndex;
+      dirNames[dirIndex] = s;
+    }
+    else
+      dirIndexes[i] = needle - dirNames;
+    /*@=compdef@*/
+
+    *baseName = savechar;
+    baseNames[i] = baseName;
+  }
+  /*@=branchstate@*/
+
+exit:
+  if (count > 0)
+  {
+    xx = headerAddEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
+    xx = headerAddEntry(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
+             baseNames, count);
+    xx = headerAddEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
+             dirNames, dirIndex + 1);
+  }
+
+  fileNames = (char**)headerFreeData(fileNames, fnt);
+
+  xx = headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
+}
+/*@=bounds@*/
+
+/*
+ * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
+ * Retrofit an explicit "Provides: name = epoch:version-release".
+ */
+void providePackageNVR(Header h)
+{
+  const char *name, *version, *release;
+  HGEPtr_t hgePtr = NULL;
+  int_32 * epoch;
+  const char *pEVR;
+  char *p;
+  int_32 pFlags = RPMSENSE_EQUAL;
+  const char ** provides = NULL;
+  const char ** providesEVR = NULL;
+  rpmTagType pnt, pvt;
+  int_32 * provideFlags = NULL;
+  rpm_count_t providesCount;
+  int xx;
+  int bingo = 1;
+
+  /* Generate provides for this package name-version-release. */
+  xx = headerNVR(h, &name, &version, &release);
+  if (!(name && version && release))
+    return;
+  pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
+  *p = '\0';
+  if (headerGetEntryMinMemory(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
+  {
+    epoch = (int_32 *)hgePtr;
+    sprintf(p, "%d:", *epoch);
+    while (*p != '\0')
+      p++;
+  }
+  (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
+
+  /*
+   * Rpm prior to 3.0.3 does not have versioned provides.
+   * If no provides at all are available, we can just add.
+   */
+  if (!headerGetEntryMinMemory(h, RPMTAG_PROVIDENAME, hTYP_t(&pnt), &hgePtr, &providesCount))
+    goto exit;
+  provides = (const char **)hgePtr;
+
+  /*
+   * Otherwise, fill in entries on legacy packages.
+   */
+  if (!headerGetEntryMinMemory(h, RPMTAG_PROVIDEVERSION, hTYP_t(&pvt), &hgePtr, NULL))
+  {
+    providesEVR = (const char **)hgePtr;
+    for (rpm_count_t i = 0; i < providesCount; i++)
+    {
+      const char * vdummy = "";
+      int_32 fdummy = RPMSENSE_ANY;
+      xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
+                                  &vdummy, 1);
+      xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
+                                  &fdummy, 1);
+    }
+    goto exit;
+  }
+
+  xx = headerGetEntryMinMemory(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
+  provideFlags = (int_32 *)hgePtr;
+
+  /*@-nullderef@*/    /* LCL: providesEVR is not NULL */
+  if (provides && providesEVR && provideFlags)
+    for (rpm_count_t i = 0; i < providesCount; i++)
+    {
+      if (!(provides[i] && providesEVR[i]))
+        continue;
+      if (!(provideFlags[i] == RPMSENSE_EQUAL &&
+            !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
+        continue;
+      bingo = 0;
+      break;
+    }
+  /*@=nullderef@*/
+
+exit:
+  provides = (const char **)headerFreeData(provides, pnt);
+  providesEVR = (const char **)headerFreeData(providesEVR, pvt);
+
+  if (bingo)
+  {
+    xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
+                                &name, 1);
+    xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
+                                &pFlags, 1);
+    xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
+                                &pEVR, 1);
+  }
+}
+#else
+static void compressFilelist(Header h)
+{
+    struct rpmtd_s fileNames;
+    char ** dirNames;
+    const char ** baseNames;
+    uint32_t * dirIndexes;
+    rpm_count_t count;
+    int xx, i;
+    int dirIndex = -1;
+
+    /*
+     * This assumes the file list is already sorted, and begins with a
+     * single '/'. That assumption isn't critical, but it makes things go
+     * a bit faster.
+     */
+
+    if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
+        xx = headerDel(h, RPMTAG_OLDFILENAMES);
+        return;         /* Already converted. */
+    }
+
+    if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM))
+        return;
+    count = rpmtdCount(&fileNames);
+    if (count < 1)
+        return;
+
+    dirNames = (char**)malloc(sizeof(*dirNames) * count);      /* worst case */
+    baseNames = (const char**)malloc(sizeof(*dirNames) * count);
+    dirIndexes = (uint32_t*)malloc(sizeof(*dirIndexes) * count);
+
+    /* HACK. Source RPM, so just do things differently */
+    {   const char *fn = rpmtdGetString(&fileNames);
+        if (fn && *fn != '/') {
+            dirIndex = 0;
+            dirNames[dirIndex] = xstrdup("");
+            while ((i = rpmtdNext(&fileNames)) >= 0) {
+                dirIndexes[i] = dirIndex;
+                baseNames[i] = rpmtdGetString(&fileNames);
+            }
+            goto exit;
+        }
+    }
+
+    while ((i = rpmtdNext(&fileNames)) >= 0) {
+        char ** needle;
+        char savechar;
+        char * baseName;
+        size_t len;
+        const char *filename = rpmtdGetString(&fileNames);
+
+        if (filename == NULL)   /* XXX can't happen */
+            continue;
+        baseName = strrchr((char*)filename, '/') + 1;
+        len = baseName - filename;
+        needle = dirNames;
+        savechar = *baseName;
+        *baseName = '\0';
+        if (dirIndex < 0 ||
+            (needle = (char**)bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
+            char *s = (char*)malloc(len + 1);
+            rstrlcpy(s, filename, len + 1);
+            dirIndexes[i] = ++dirIndex;
+            dirNames[dirIndex] = s;
+        } else
+            dirIndexes[i] = needle - dirNames;
+
+        *baseName = savechar;
+        baseNames[i] = baseName;
+    }
+
+exit:
+    if (count > 0) {
+        headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, count);
+        headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, count);
+        headerPutStringArray(h, RPMTAG_DIRNAMES,
+                             (const char **) dirNames, dirIndex + 1);
+    }
+
+    rpmtdFreeData(&fileNames);
+    for (i = 0; i <= dirIndex; i++) {
+        free(dirNames[i]);
+    }
+    free(dirNames);
+    free(baseNames);
+    free(dirIndexes);
+
+    xx = headerDel(h, RPMTAG_OLDFILENAMES);
+}
+
+/*
+ * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
+ * Retrofit an explicit "Provides: name = epoch:version-release.
+ */
+static void providePackageNVR(Header h)
+{
+    const char *name;
+    char *pEVR;
+    rpmsenseFlags pFlags = RPMSENSE_EQUAL;
+    int bingo = 1;
+    struct rpmtd_s pnames;
+    rpmds hds, nvrds;
+
+    /* Generate provides for this package name-version-release. */
+    pEVR = headerGetEVR(h, &name);
+    if (!(name && pEVR))
+        return;
+
+    /*
+     * Rpm prior to 3.0.3 does not have versioned provides.
+     * If no provides at all are available, we can just add.
+     */
+    if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
+        goto exit;
+    }
+
+    /*
+     * Otherwise, fill in entries on legacy packages.
+     */
+    if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
+        while (rpmtdNext(&pnames) >= 0) {
+            uint32_t fdummy = RPMSENSE_ANY;
+
+            headerPutString(h, RPMTAG_PROVIDEVERSION, "");
+            headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
+        }
+        goto exit;
+    }
+
+    /* see if we already have this provide */
+    hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
+    nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
+    if (rpmdsFind(hds, nvrds) >= 0) {
+        bingo = 0;
+    }
+    rpmdsFree(hds);
+    rpmdsFree(nvrds);
+
+exit:
+    if (bingo) {
+        const char *evr = pEVR;
+       uint32_t fdummy = pFlags;
+        headerPutString(h, RPMTAG_PROVIDENAME, name);
+        headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
+        headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
+    }
+    rpmtdFreeData(&pnames);
+    free(pEVR);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+using namespace std;
+
+#undef Y2LOG
+#define Y2LOG "librpmDb"
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : internal_convertV3toV4
+**     FUNCTION TYPE : int
+*/
+void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
+                             callback::SendReport<ConvertDBReport> & report )
+{
+//  Timecount _t( "convert V3 to V4" );
+  MIL << "Convert rpm3 database to rpm4" << endl;
+
+  // Check arguments
+  FD_t fd = fadOpen( v3db_r.asString().c_str() );
+  if ( fd == 0 )
+  {
+    Fclose( fd );
+    ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
+  }
+
+  if ( ! v4db_r )
+  {
+    Fclose( fd );
+    INT << "NULL rpmV4 database passed as argument!" << endl;
+    ZYPP_THROW(RpmNullDatabaseException());
+  }
+
+  shared_ptr<RpmException> err = v4db_r->error();
+  if ( err )
+  {
+    Fclose( fd );
+    INT << "Can't access rpmV4 database " << v4db_r << endl;
+    ZYPP_THROW(*err);
+  }
+
+  // open rpmV4 database for writing. v4db_r is ok so librpm should
+  // be properly initialized.
+  rpmdb db = 0;
+  string rootstr( v4db_r->root().asString() );
+  const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
+
+  int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
+  if ( res || ! db )
+  {
+    if ( db )
+    {
+      ::rpmdbClose( db );
+    }
+    Fclose( fd );
+    ZYPP_THROW(RpmDbOpenException(root, v4db_r->dbPath()));
+  }
+
+  // Check ammount of packages to process.
+  int max = 0;
+  for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
+  {
+    ++max;
+  }
+  MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
+
+  unsigned failed      = 0;
+  unsigned ignored     = 0;
+  unsigned alreadyInV4 = 0;
+  report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
+
+  if ( !max )
+  {
+    Fclose( fd );
+    ::rpmdbClose( db );
+    return;
+  }
+
+  // Start conversion.
+#warning Add CBSuggest handling if needed, also on lines below
+//  CBSuggest proceed;
+  bool proceed = true;
+  for ( int offset = fadFirstOffset(fd); offset && proceed /*!= CBSuggest::CANCEL*/;
+        offset = fadNextOffset(fd, offset),
+        report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
+  {
+
+    // have to use lseek instead of Fseek because headerRead
+    // uses low level IO
+    if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
+    {
+      ostream * reportAs = &(ERR);
+      /*      proceed = report->dbReadError( offset );
+            if ( proceed == CBSuggest::SKIP ) {
+       // ignore this error
+       ++ignored;
+       reportAs = &(WAR << "IGNORED: ");
+            } else {*/
+      // PROCEED will fail after conversion; CANCEL immediately stop loop
+      ++failed;
+//      }
+      (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
+      continue;
+    }
+    Header h = headerRead(fd, HEADER_MAGIC_NO);
+    if ( ! h )
+    {
+      ostream * reportAs = &(ERR);
+      /*      proceed = report->dbReadError( offset );
+            if ( proceed == CBSuggest::SKIP ) {
+       // ignore this error
+       ++ignored;
+       reportAs = &(WAR << "IGNORED: ");
+            } else {*/
+      // PROCEED will fail after conversion; CANCEL immediately stop loop
+      ++failed;
+//      }
+      (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
+      continue;
+    }
+    compressFilelist(h);
+    providePackageNVR(h);
+    const char *name = 0;
+    const char *version = 0;
+    const char *release = 0;
+    headerNVR(h, &name, &version, &release);
+    string nrv( string(name) + "-" +  version + "-" + release );
+    rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
+    rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
+    rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
+    if (rpmdbNextIterator(mi))
+    {
+//      report.dbInV4( nrv );
+      WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
+      rpmdbFreeIterator(mi);
+      headerFree(h);
+      ++alreadyInV4;
+      continue;
+    }
+    rpmdbFreeIterator(mi);
+#ifdef _RPM_5
+    if (rpmdbAdd(db, -1, h, 0))
+#else
+    if (rpmdbAdd(db, -1, h, 0, 0))
+#endif
+    {
+//      report.dbWriteError( nrv );
+      proceed = false;//CBSuggest::CANCEL; // immediately stop loop
+      ++failed;
+      ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
+      headerFree(h);
+      continue;
+    }
+    headerFree(h);
+  }
+
+  Fclose(fd);
+  ::rpmdbClose(db);
+
+  if ( failed )
+  {
+    ERR << "Convert rpm3 database to rpm4: Aborted after "
+    << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
+    << endl;
+    ZYPP_THROW(RpmDbConvertException());
+  }
+  else
+  {
+    MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
+    if ( alreadyInV4 )
+    {
+      MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
+    }
+    if ( ignored )
+    {
+      MIL << "; IGNORED: " << ignored << " unconverted due to error";
+    }
+    MIL << endl;
+  }
+}
+#endif
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmCallbacks.h"
+#include "zypp/ZYppCallbacks.h"
+
+using namespace std;
+
+#undef Y2LOG
+#define Y2LOG "librpmDb"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+/******************************************************************
+*
+*
+*      FUNCTION NAME : convertV3toV4
+*
+* \throws RpmException
+*
+*/
+void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
+{
+  // report
+  callback::SendReport<ConvertDBReport> report;
+  report->start(v3db_r);
+  try
+  {
+    // Does no longer work with rpm 4.9.
+    // internal_convertV3toV4( v3db_r, v4db_r, report );
+    INT << "Unsupported: Convert rpm3 database to rpm4" << endl;
+    ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
+  }
+  catch (RpmException & excpt_r)
+  {
+    report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
+    ZYPP_RETHROW(excpt_r);
+  }
+  report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
+}
+
+} // namespace rpm
+} // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/librpmDb.h b/zypp/target/rpm/librpmDb.h
new file mode 100644 (file)
index 0000000..7bfbd34
--- /dev/null
@@ -0,0 +1,627 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/librpmDb.h
+ *
+*/
+#ifndef librpmDb_h
+#define librpmDb_h
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/PathInfo.h"
+#include "zypp/Package.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/target/rpm/RpmException.h"
+
+namespace zypp
+{
+namespace target
+{
+namespace rpm
+{
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb
+/**
+ * @short Manage access to librpm database.
+ **/
+class librpmDb : public base::ReferenceCounted, private base::NonCopyable
+{
+public:
+  typedef intrusive_ptr<librpmDb> Ptr;
+  typedef intrusive_ptr<const librpmDb> constPtr;
+private:
+  /**
+   * <B>INTENTIONALLY UNDEFINED<\B> because of bug in Ptr classes
+   * which allows implicit conversion from librpmDb::Ptr to
+   * librpmDb::constPtr. Currently we don't want to provide non const
+   * handles, as the database is opened READONLY.
+  *
+   * \throws RpmException
+   *
+   **/
+  static void dbAccess( librpmDb::Ptr & ptr_r );
+
+public:
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   static interface
+  //
+  ///////////////////////////////////////////////////////////////////
+private:
+
+  /**
+   * Current root directory for all operations.
+   * (initialy /)
+   **/
+  static Pathname _defaultRoot;
+
+  /**
+   * Current directory (below root) that contains the rpmdb.
+   * (initialy /var/lib/rpm)
+   **/
+  static Pathname _defaultDbPath;
+
+  /**
+   * Current rpmdb handle.
+   **/
+  static librpmDb::constPtr _defaultDb;
+
+  /**
+   * Wheter access is blocked (no _defaultDb will be available).
+   **/
+  static bool _dbBlocked;
+
+  /**
+   * For internal use. Pointer returned should immediately be
+   * wrapped into librpmDb::Ptr.
+   *
+   * \throws RpmException
+   *
+   **/
+  static librpmDb * newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r );
+
+  /**
+   * Access the database at the current default location. If necessary
+   * (eg. after @ref dbRelease), the database is opened. This just creates
+   * the internal handle. Once the handle is passed to e.g. some
+   * @ref db_const_iterator, the database will be closed if the last
+   * outstanding reference goes out of scope. If no external reference is
+   * created, you'll have to explicitly call @ref dbRelease to close the
+   * database.
+   *
+   * \throws RpmException
+   *
+   **/
+  static void dbAccess();
+
+public:
+
+  /**
+   * Initialize lib librpm (read configfiles etc.). It's called
+   * on demand but you may call it anytime.
+   *
+   * @return Whether librpm is initialized.
+   **/
+  static bool globalInit();
+
+  /**
+   * @return librpm macro expansion.
+   **/
+  static std::string expand( const std::string & macro_r );
+
+  /**
+   * @return String '(root_r)sub_r' used in debug output.
+   **/
+  static std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
+  {
+    return std::string( "'(" ) + root_r.asString() + ")" + sub_r.asString() + "'";
+  }
+
+public:
+
+  /**
+   * @return Current root directory for all operations.
+   **/
+  static const Pathname & defaultRoot()
+  {
+    return _defaultRoot;
+  }
+
+  /**
+   * @return Current directory (below root) that contains the rpmdb.
+   **/
+  static const Pathname & defaultDbPath()
+  {
+    return _defaultDbPath;
+  }
+
+  /**
+   * Adjust access to the given database location, making it the new
+   * default location on success. No relative Pathnames are allowed.
+   *
+   * It's not possible to access a database while access is blocked
+   * (see @ref blockAccess), but valid Pathnames provided will be stored
+   * as new default location.
+   *
+   * It's not allowed to switch to another location while a database
+   * is accessed. Use @ref dbRelease to force releasing the database first.
+  *
+   * \throws RpmException
+   *
+   **/
+  static void dbAccess( const Pathname & root_r, const Pathname & dbPath_r );
+
+  /**
+   * Same as &ref dbAccess(), but returns the database handle if
+   * avaialble, otherwise NULL. This creates an external reference, thus
+   * it should not be used longer than necessary. Be prepared that the
+   * handle might become invalid (see @ref dbRelease) later.
+   *
+   * \throws RpmException
+   *
+   **/
+  static void dbAccess( librpmDb::constPtr & ptr_r );
+
+  /**
+   * If there are no outstanding references to the database (e.g. by @ref db_const_iterator),
+   * the database is closed. Subsequent calls to @ref dbAccess may however
+   * open the database again.
+   *
+   * If forced, the internal reference is dropped and it will look like
+   * the database was closed. But physically the database will be closed
+   * after all outstanding references are gone.
+   *
+   * @return The number of outstandig references to the database, 0 if
+   * if database was physically closed.
+   **/
+  static unsigned dbRelease( bool force_r = false );
+
+  /**
+   * Blocks further access to rpmdb. Basically the same as @ref dbRelease( true ),
+   * but subsequent calls to @ref dbAccess will fail returning E_RpmDB_access_blocked.
+   *
+   * @return The number of outstandig references to the database, 0 if
+   * if database was physically closed.
+   **/
+  static unsigned blockAccess();
+
+  /**
+   * Allow access to rpmdb e.g. after @ref blockAccess. Subsequent calls to
+   * @ref dbAccess will perform.
+   *
+   * <B>NOTE:</B> Initially we're in blocked mode. So you must call @ref unblockAccess
+   * unblockAccess at least once. Othwise nothing will happen.
+   *
+   * @return The number of outstandig references to the database, 0 if
+   * if database was physically closed.
+   **/
+  static void unblockAccess();
+
+  /**
+   * @return Whether database access is blocked.
+   **/
+  static bool isBlocked()
+  {
+    return _dbBlocked;
+  }
+
+  /**
+   * Dump debug info.
+   **/
+  static std::ostream & dumpState( std::ostream & str );
+
+public:
+
+  /**
+   * Collect info about what kind of rpmdb seems to be present by
+   * looking at paths and filenames.
+   **/
+  class DbDirInfo;
+
+  /**
+   * Subclass to retrieve database content.
+   **/
+  class db_const_iterator;
+
+private:
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   internal database handle interface (nonstatic)
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * Hides librpm specific data
+   **/
+  class D;
+  D & _d;
+
+protected:
+
+  /**
+   * Private constructor! librpmDb objects are to be created via
+   * static interface only.
+   **/
+  librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r );
+
+  /**
+   * Trigger from @ref Rep, after refCount was decreased.
+   **/
+  virtual void unref_to( unsigned refCount_r ) const;
+
+public:
+
+  /**
+   * Destructor. Closes rpmdb.
+   **/
+  virtual ~librpmDb();
+
+  /**
+   * @return This handles root directory for all operations.
+   **/
+  const Pathname & root() const;
+
+  /**
+   * @return This handles directory that contains the rpmdb.
+   **/
+  const Pathname & dbPath() const;
+
+  /**
+   * Return any database error. Usg. if the database was
+   * blocked by calling @ref dbRelease(true) or @ref blockAccess.
+   **/
+  shared_ptr<RpmException> error() const;
+
+  /**
+   * @return Whether handle is valid.
+   **/
+  bool valid() const
+  {
+    return( ! error() );
+  }
+
+  /**
+   * @return True if handle is valid and database is empty.
+   **/
+  bool empty() const;
+
+  /**
+   * @return Number of entries in the database (0 if not valid).
+   **/
+  unsigned size() const;
+
+public:
+
+  /**
+   * Dont call it ;) It's for development and testing only.
+   **/
+  void * dont_call_it() const;
+
+  /**
+   * Dump debug info.
+   **/
+  virtual std::ostream & dumpOn( std::ostream & str ) const;
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::DbDirInfo
+/**
+ * Collect info about what kind of rpmdb seems to be present by
+ * looking at paths and filenames.
+ **/
+class librpmDb::DbDirInfo
+{
+  friend std::ostream & operator<<( std::ostream & str, const DbDirInfo & obj );
+
+private:
+
+  /**
+   * Root directory for all operations.
+   **/
+  Pathname _root;
+
+  /**
+   * Directory that contains the rpmdb.
+   **/
+  Pathname _dbPath;
+
+  /**
+   * database directory (unset on illegal constructor arguments)
+   **/
+  PathInfo _dbDir;
+
+  /**
+   * rpmV4 database (_dbDir/Packages)
+   **/
+  PathInfo _dbV4;
+
+  /**
+   * rpmV3 database (_dbDir/packages.rpm)
+   **/
+  PathInfo _dbV3;
+
+  /**
+   * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+   **/
+  PathInfo _dbV3ToV4;
+
+public:
+
+  /**
+   * For Constructor arguments see @ref accessPath. On illegal
+   * arguments @ref _dbDir is unset.
+   **/
+  DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r );
+
+public:
+
+  /**
+   * Root directory for all operations.
+   **/
+  const Pathname & root() const
+  {
+    return _root;
+  }
+
+  /**
+   * Directory that contains the rpmdb.
+   **/
+  const Pathname & dbPath() const
+  {
+    return _dbPath;
+  }
+
+  /**
+   * database directory (unset on illegal constructor arguments)
+   **/
+  const PathInfo & dbDir() const
+  {
+    return _dbDir;
+  }
+
+  /**
+   * rpmV4 database (_dbDir/Packages)
+   **/
+  const PathInfo & dbV4() const
+  {
+    return _dbV4;
+  }
+
+  /**
+   * rpmV3 database (_dbDir/packages.rpm)
+   **/
+  const PathInfo & dbV3() const
+  {
+    return _dbV3;
+  }
+
+  /**
+   * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+   **/
+  const PathInfo & dbV3ToV4() const
+  {
+    return _dbV3ToV4;
+  }
+
+public:
+
+  /**
+   * Restat all paths
+   **/
+  void restat();
+
+public:
+
+  /**
+   * Whether constructor arguments were illegal.
+   **/
+  bool illegalArgs() const
+  {
+    return _dbDir.path().empty();
+  }
+
+  /**
+   * Whether constructor arguments were llegal and dbDir either
+   * is a directory or may be created (path does not exist).
+   **/
+  bool usableArgs() const
+  {
+    return _dbDir.isDir() || ! ( _dbDir.path().empty() || _dbDir.isExist() );
+  }
+
+  /**
+   * Whether dbDir directory exists.
+   **/
+  bool hasDbDir() const
+  {
+    return _dbDir.isDir();
+  }
+
+  /**
+   * Whether dbV4 file exists.
+   **/
+  bool hasDbV4() const
+  {
+    return _dbV4.isFile();
+  }
+
+  /**
+   * Whether dbV3 file exists.
+   **/
+  bool hasDbV3() const
+  {
+    return _dbV3.isFile();
+  }
+
+  /**
+   * Whether dbV3ToV4 file exists.
+   **/
+  bool hasDbV3ToV4() const
+  {
+    return _dbV3ToV4.isFile();
+  }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::db_const_iterator
+/**
+ * @short Subclass to retrieve database content.
+ *
+ *
+ **/
+class librpmDb::db_const_iterator
+{
+  db_const_iterator & operator=( const db_const_iterator & ); // NO ASSIGNMENT!
+  db_const_iterator ( const db_const_iterator & );            // NO COPY!
+  friend std::ostream & operator<<( std::ostream & str, const db_const_iterator & obj );
+  friend class librpmDb;
+
+private:
+
+  /**
+   * Hides librpm specific data
+   **/
+  class D;
+  D & _d;
+
+public:
+
+  /**
+   * Constructor. Iterator is initialized to @ref findAll.
+   * The default form accesses librpmDb's default database.
+   * Explicitly providing a database handle should not be
+   * neccesary, except for testing.
+   **/
+  db_const_iterator( librpmDb::constPtr dbptr_r = 0 );
+
+  /**
+   * Destructor.
+   **/
+  ~db_const_iterator();
+
+  /**
+   * Return any database error.
+   *
+   * <B>NOTE:</B> If the database gets blocked (see @ref dbRelease)
+   * dbError will immediately report this, but an already running
+   * iteration will proceed to its end. Then the database is dropped.
+   **/
+  shared_ptr<RpmException> dbError() const;
+
+  /**
+   * Advance to next RpmHeader::constPtr.
+   **/
+  void operator++();
+
+  /**
+   * Returns the current headers index in database,
+   * 0 if no header.
+   **/
+  unsigned dbHdrNum() const;
+
+  /**
+   * Returns the current RpmHeader::constPtr or
+   * NULL, if no more entries available.
+   **/
+  const RpmHeader::constPtr & operator*() const;
+
+  /**
+   * Forwards to the current RpmHeader::constPtr.
+   **/
+  const RpmHeader::constPtr & operator->() const
+  {
+    return operator*();
+  }
+
+public:
+
+  /**
+   * Reset to iterate all packages. Returns true if iterator
+   * contains at least one entry.
+   *
+   * <B>NOTE:</B> No entry (false) migt be returned due to a
+   * meanwhile blocked database (see @ref dbRelease). Use
+   * @ref dbError to check this.
+   **/
+  bool findAll();
+
+  /**
+   * Reset to iterate all packages that own a certain file.
+   **/
+  bool findByFile( const std::string & file_r );
+
+  /**
+   * Reset to iterate all packages that provide a certain tag.
+   **/
+  bool findByProvides( const std::string & tag_r );
+
+  /**
+   * Reset to iterate all packages that require a certain tag.
+   **/
+  bool findByRequiredBy( const std::string & tag_r );
+
+  /**
+   * Reset to iterate all packages that conflict with a certain tag.
+   **/
+  bool findByConflicts( const std::string & tag_r );
+
+  /**
+   * Reset to iterate all packages with a certain name.
+   *
+   * <B>NOTE:</B> Multiple entries for one package installed
+   * in different versions are possible but not desired. Usually
+   * you'll want to use @ref findPackage instead.
+   *
+   * findByName is needed to retrieve pseudo packages like
+   * 'gpg-pubkey', which in fact exist in multiple instances.
+   **/
+  bool findByName( const std::string & name_r );
+
+public:
+
+  /**
+   * Find package by name.
+   *
+   * Multiple entries for one package installed in different versions
+   * are possible but not desired. If so, the last package installed
+   * is returned.
+   **/
+  bool findPackage( const std::string & name_r );
+
+  /**
+   * Find package by name and edition.
+   * Commonly used by PMRpmPackageDataProvider.
+   **/
+  bool findPackage( const std::string & name_r, const Edition & ed_r );
+
+  /**
+   * Abbr. for <code>findPackage( which_r->name(), which_r->edition() );</code>
+   **/
+  bool findPackage( const Package::constPtr & which_r );
+};
+
+///////////////////////////////////////////////////////////////////
+} //namespace rpm
+} //namespace target
+} // namespace zypp
+
+#endif // librpmDb_h
+
diff --git a/zypp/thread/Mutex.cc b/zypp/thread/Mutex.cc
new file mode 100644 (file)
index 0000000..df2b481
--- /dev/null
@@ -0,0 +1,108 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/thread/Mutex.cc
+ */
+#include <zypp/thread/Mutex.h>
+#include <zypp/thread/MutexException.h>
+#include <zypp/base/Gettext.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace thread
+  { //////////////////////////////////////////////////////////////////
+
+    // -------------------------------------------------------------
+    Mutex::Mutex()
+    {
+      pthread_mutexattr_t attr;
+
+      int ret = pthread_mutexattr_init(&attr);
+      if( ret != 0)
+      {
+        ZYPP_THROW_ERRNO_MSG(zypp::thread::MutexException,
+        _("Can't initialize mutex attributes"));
+      }
+
+      ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+      if( ret != 0)
+      {
+        ZYPP_THROW_ERRNO_MSG(MutexException,
+        _("Can't set recursive mutex attribute"));
+      }
+
+      ret = pthread_mutex_init(&m_mutex, &attr);
+      if( ret != 0)
+      {
+        ZYPP_THROW_ERRNO_MSG(MutexException,
+        _("Can't initialize recursive mutex"));
+      }
+    }
+
+    // -------------------------------------------------------------
+    Mutex::~Mutex()
+    {
+      if( pthread_mutex_destroy(&m_mutex) != 0 && errno == EBUSY)
+      {
+        // try to unlock and to destroy again...
+        if( pthread_mutex_unlock(&m_mutex) == 0)
+        {
+            pthread_mutex_destroy(&m_mutex);
+        }
+        /*
+        else
+        {
+          ZYPP_THROW_ERRNO_MSG(MutexException,
+          _("Can't destroy mutex owned by another thread"));
+        }
+        */
+      }
+    }
+    // -------------------------------------------------------------
+    void Mutex::lock()
+    {
+      if( pthread_mutex_lock(&m_mutex) != 0)
+      {
+        ZYPP_THROW_ERRNO_MSG(MutexException,
+        _("Can't acquire the mutex lock"));
+      }
+    }
+
+    // -------------------------------------------------------------
+    void Mutex::unlock()
+    {
+      if( pthread_mutex_unlock(&m_mutex) != 0)
+      {
+        ZYPP_THROW_ERRNO_MSG(MutexException,
+        _("Can't release the mutex lock"));
+      }
+    }
+
+    // -------------------------------------------------------------
+    bool Mutex::trylock()
+    {
+      return (pthread_mutex_trylock(&m_mutex) == 0);
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace thread
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/thread/Mutex.h b/zypp/thread/Mutex.h
new file mode 100644 (file)
index 0000000..794b516
--- /dev/null
@@ -0,0 +1,96 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/thread/Mutex.h
+ */
+#ifndef   ZYPP_THREAD_MUTEX_H
+#define   ZYPP_THREAD_MUTEX_H
+
+#include <zypp/base/NonCopyable.h>
+#include <zypp/thread/MutexException.h>
+#include <pthread.h>
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace thread
+  { //////////////////////////////////////////////////////////////////
+
+
+    typedef pthread_mutex_t RecursiveMutex_t;
+
+
+    ////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Mutex
+    //
+    /** A recursive Mutex.
+     */
+    class Mutex: public zypp::base::NonCopyable
+    {
+    public:
+      /** Create a new recursive Mutex object.
+       * \throws MutexException on initialization failure.
+       */
+      Mutex();
+
+      /** Destroys this Mutex object.
+       */
+      ~Mutex();
+
+      /** Acquire ownership of this Mutex object.
+       * This call will block if another thread has ownership of
+       * this Mutex. When it returns, the current thread is the
+       * owner of this Mutex object.
+       *
+       * In the same thread, this recursive mutex can be acquired
+       * multiple times.
+       *
+       * \throws MutexException if the maximum number of recursive
+       *         locks for mutex has been exceeded.
+       */
+      void lock();
+
+      /** Release ownership of this Mutex object.
+       * If another thread is waiting to acquire the ownership of
+       * this mutex it will stop blocking and acquire ownership
+       * when this call returns.
+       *
+       * \throws MutexException if the current thread does not
+       *         own the mutex.
+       */
+      void unlock();
+
+      /** Try to acquire ownership of this Mutex object.
+       * This call will return false if another thread has ownership
+       * of this Mutex or the maximum number of recursive locks for
+       * mutex has been exceeded.
+       * When it returns true, the current thread is the owner of
+       * this Mutex object.
+       *
+       * \return true, if ownership was acquired.
+       */
+      bool trylock();
+
+    private:
+      RecursiveMutex_t m_mutex;
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace thread
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_THREAD_MUTEX_H
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/thread/MutexException.h b/zypp/thread/MutexException.h
new file mode 100644 (file)
index 0000000..2fd2515
--- /dev/null
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/thread/MutexException.h
+ *
+*/
+#ifndef   ZYPP_THREAD_MUTEXEXCEPTION_H
+#define   ZYPP_THREAD_MUTEXEXCEPTION_H
+
+#include <zypp/base/Exception.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace thread
+  { //////////////////////////////////////////////////////////////////
+
+
+    ////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : MutexException
+    //
+    /** Exception type thrown on mutex errors.
+     */
+    class MutexException: public zypp::Exception
+    {
+    public:
+      MutexException()
+        : zypp::Exception( ::zypp::Exception::strErrno(errno))
+      {}
+
+      MutexException(const std::string &msg)
+        : zypp::Exception( msg)
+      {}
+
+      virtual ~MutexException() throw()
+      {}
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace thread
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_THREAD_MUTEXEXCEPTION_H
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/thread/MutexLock.h b/zypp/thread/MutexLock.h
new file mode 100644 (file)
index 0000000..7c1998c
--- /dev/null
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/thread/MutexLock.h
+ *
+*/
+#ifndef   ZYPP_THREAD_MUTEXLOCK_H
+#define   ZYPP_THREAD_MUTEXLOCK_H
+
+#include <zypp/thread/Mutex.h>
+#include <cassert>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace thread
+  { //////////////////////////////////////////////////////////////////
+
+
+    // -------------------------------------------------------------
+    class MutexLock
+    {
+    public:
+      explicit MutexLock(Mutex &mutex, bool init_locked=true)
+        : m_mutex(&mutex)
+        , m_locked(false)
+      {
+        if(init_locked)
+          lock();
+      }
+
+      MutexLock(const MutexLock &ref)
+        : m_mutex( ref.m_mutex)
+        , m_locked(ref.m_locked)
+      {
+        ref.m_locked = false;
+      }
+
+      ~MutexLock()
+      {
+        try
+        {
+          if( m_locked)
+            unlock();
+        }
+        catch( ... )
+        {
+          // don't let exceptions escape
+        }
+      }
+
+      void lock()
+      {
+        assert(m_locked == false);
+        m_mutex->lock();
+        m_locked = true;
+      }
+
+      void unlock()
+      {
+        assert(m_locked == true);
+        m_mutex->unlock();
+        m_locked = false;
+      }
+
+      bool trylock()
+      {
+        assert(m_locked == false);
+        m_locked = m_mutex->trylock();
+        return m_locked;
+      }
+
+      bool locked()
+      {
+        return m_locked;
+      }
+
+    private:
+      Mutex        *m_mutex;
+      mutable bool  m_locked;
+      //friend class Condition;
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace thread
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_THREAD_MUTEXLOCK_H
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/thread/Once.h b/zypp/thread/Once.h
new file mode 100644 (file)
index 0000000..036cf58
--- /dev/null
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/thread/Once.h
+ *
+*/
+#ifndef   ZYPP_THREAD_ONCE_H
+#define   ZYPP_THREAD_ONCE_H
+
+#include <pthread.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+  namespace thread
+  { //////////////////////////////////////////////////////////////////
+
+
+    /** The initialization value for OnceFlag variables. */
+    #define ZYPP_ONCE_INIT  PTHREAD_ONCE_INIT
+
+
+    /** The OnceFlag variable type.
+     */
+    typedef pthread_once_t OnceFlag;
+
+
+    /**
+     * Call once function.
+     *
+     * The purpose of callOnce is to ensure that a piece of initialization
+     * code is executed at most once.
+     * The OnceFlag \p flag has to point to a static or extern variable,
+     * that was statically initialized to ZYPP_ONCE_INIT.
+     *
+     * The first time callOnce is called with a given \p onceFlag argument,
+     * it calls \p fuct with no argument and changes the value of \p flag
+     * to indicate that the function has been run.
+     * Subsequent calls with the same once flag does nothing.
+     */
+    void callOnce(OnceFlag& flag, void (*func)());
+
+    inline void callOnce(OnceFlag& flag, void (*func)())
+    {
+      pthread_once(&flag, func);
+    }
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace thread
+  ////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_THREAD_ONCE_H
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/ui/SelFilters.h b/zypp/ui/SelFilters.h
new file mode 100644 (file)
index 0000000..2fcdfc7
--- /dev/null
@@ -0,0 +1,96 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/SelFilters.h
+ *
+*/
+#ifndef ZYPP_UI_SELFILTERS_H
+#define ZYPP_UI_SELFILTERS_H
+
+#include <string>
+
+#include "zypp/base/Functional.h"
+#include "zypp/ui/Selectable.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    namespace selfilter
+    { /////////////////////////////////////////////////////////////////
+
+      typedef std::unary_function<Selectable::constPtr,bool> SelectableFilterFunctor;
+
+      /** */
+      struct ByKind : public SelectableFilterFunctor
+      {
+        ByKind( const ResObject::Kind & kind_r )
+        : _kind( kind_r )
+        {}
+
+        bool operator()( const Selectable::constPtr & obj ) const
+        {
+          return obj && obj->kind() == _kind;
+        }
+
+        ResObject::Kind _kind;
+      };
+
+      /** */
+      struct ByName : public SelectableFilterFunctor
+      {
+        ByName( const std::string & name_r )
+        : _name( name_r )
+        {}
+
+        bool operator()( const ui::Selectable::constPtr & obj ) const
+        { return obj && obj->name() == _name; }
+
+        std::string _name;
+      };
+
+      /** */
+      struct ByHasInstalledObj : public SelectableFilterFunctor
+      {
+        bool operator()( const ui::Selectable::constPtr & obj ) const
+        { return obj && !obj->installedEmpty(); }
+      };
+
+      /** */
+      struct ByHasCandidateObj : public SelectableFilterFunctor
+      {
+        bool operator()( const ui::Selectable::constPtr & obj ) const
+        { return obj && obj->hasCandidateObj(); }
+      };
+
+      struct ByStatus : public SelectableFilterFunctor
+      /** */
+      {
+        ByStatus( Status status_r )
+        : _status( status_r )
+        {}
+
+        bool operator()( const ui::Selectable::constPtr & obj ) const
+        { return obj && obj->status() == _status; }
+
+        Status _status;
+      };
+
+      /////////////////////////////////////////////////////////////////
+    } // namespace selfilter
+    ///////////////////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_UI_SELFILTERS_H
diff --git a/zypp/ui/Selectable.cc b/zypp/ui/Selectable.cc
new file mode 100644 (file)
index 0000000..8965f87
--- /dev/null
@@ -0,0 +1,293 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/Selectable.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/ui/Selectable.h"
+#include "zypp/ui/SelectableImpl.h"
+#include "zypp/ResPool.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    IMPL_PTR_TYPE(Selectable);
+
+    Selectable::Ptr Selectable::get( const pool::ByIdent & ident_r )
+    { return ResPool::instance().proxy().lookup( ident_r ); }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Selectable::Selectable
+    // METHOD TYPE : Ctor
+    //
+    Selectable::Selectable( Impl_Ptr pimpl_r )
+    : _pimpl( pimpl_r )
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : Selectable::~Selectable
+    // METHOD TYPE : Dtor
+    //
+    Selectable::~Selectable()
+    {}
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // Forward to implementation.
+    // Restrict PoolItems to ResObject::constPtr!
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    IdString Selectable::ident() const
+    { return _pimpl->ident(); }
+
+    ResObject::Kind Selectable::kind() const
+    { return _pimpl->kind(); }
+
+    const std::string & Selectable::name() const
+    { return _pimpl->name(); }
+
+    Status Selectable::status() const
+    { return _pimpl->status(); }
+
+    bool Selectable::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
+    { return _pimpl->setStatus( state_r, causer_r ); }
+
+    PoolItem Selectable::installedObj() const
+    { return _pimpl->installedObj(); }
+
+    PoolItem Selectable::candidateObj() const
+    { return _pimpl->candidateObj(); }
+
+    PoolItem Selectable::candidateObjFrom( Repository repo_r ) const
+    { return _pimpl->candidateObjFrom( repo_r ); }
+
+    PoolItem Selectable::updateCandidateObj() const
+    { return _pimpl->updateCandidateObj(); }
+
+    PoolItem Selectable::highestAvailableVersionObj() const
+    { return _pimpl->highestAvailableVersionObj(); }
+
+    bool Selectable::identicalAvailable( const PoolItem & rhs ) const
+    { return _pimpl->identicalAvailable( rhs ); }
+
+    bool Selectable::identicalInstalled( const PoolItem & rhs ) const
+    { return _pimpl->identicalInstalled( rhs ); }
+
+    PoolItem Selectable::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
+    { return _pimpl->setCandidate( newCandidate_r, causer_r ); }
+
+    PoolItem Selectable::setCandidate( ResObject::constPtr newCandidate_r, ResStatus::TransactByValue causer_r )
+    { return _pimpl->setCandidate( PoolItem( newCandidate_r ), causer_r ); }
+
+    bool Selectable::setOnSystem( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
+    {
+      if ( identicalInstalled( newCandidate_r ) )
+        return setFate( UNMODIFIED, causer_r );
+      return setCandidate( newCandidate_r, causer_r ) && setFate( TO_INSTALL, causer_r );
+    }
+
+    PoolItem Selectable::theObj() const
+    { return _pimpl->theObj(); }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    bool Selectable::availableEmpty() const
+    { return _pimpl->availableEmpty(); }
+
+    Selectable::available_size_type Selectable::availableSize() const
+    { return _pimpl->availableSize(); }
+
+    Selectable::available_iterator Selectable::availableBegin() const
+    { return _pimpl->availableBegin(); }
+
+    Selectable::available_iterator Selectable::availableEnd() const
+    { return _pimpl->availableEnd(); }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    bool Selectable::installedEmpty() const
+    { return _pimpl->installedEmpty(); }
+
+    Selectable::installed_size_type Selectable::installedSize() const
+    { return _pimpl->installedSize(); }
+
+    Selectable::installed_iterator Selectable::installedBegin() const
+    { return _pimpl->installedBegin(); }
+
+    Selectable::installed_iterator Selectable::installedEnd() const
+    { return _pimpl->installedEnd(); }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    bool Selectable::picklistEmpty() const
+    { return _pimpl->picklistEmpty();  }
+
+    Selectable::picklist_size_type Selectable::picklistSize() const
+    { return _pimpl->picklistSize(); }
+
+    Selectable::picklist_iterator Selectable::picklistBegin() const
+    { return _pimpl->picklistBegin(); }
+
+    Selectable::picklist_iterator Selectable::picklistEnd() const
+    { return _pimpl->picklistEnd(); }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    bool Selectable::isUnmaintained() const
+    { return _pimpl->isUnmaintained(); }
+
+    bool Selectable::multiversionInstall() const
+    { return _pimpl->multiversionInstall(); }
+
+    bool Selectable::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+    { return _pimpl->pickInstall( pi_r, causer_r, yesno_r ); }
+
+    bool Selectable::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+    { return _pimpl->pickDelete( pi_r, causer_r, yesno_r ); }
+
+    Status Selectable::pickStatus( const PoolItem & pi_r ) const
+    { return _pimpl->pickStatus( pi_r ); }
+
+    bool Selectable::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
+    { return _pimpl->setPickStatus( pi_r, state_r, causer_r ); }
+
+    ////////////////////////////////////////////////////////////////////////
+
+    bool Selectable::isUndetermined() const
+    { return _pimpl->isUndetermined(); }
+
+    bool Selectable::isRelevant() const
+    { return _pimpl->isRelevant(); }
+
+    bool Selectable::isSatisfied() const
+    { return _pimpl->isSatisfied(); }
+
+    bool Selectable::isBroken() const
+    { return _pimpl->isBroken(); }
+
+    bool Selectable::isNeeded() const
+    {
+      return fate() == TO_INSTALL || ( ! locked() && isBroken() );
+    }
+
+    bool Selectable::isUnwanted() const
+    {
+      return locked() && isBroken() ;
+    }
+
+    ResStatus::TransactByValue Selectable::modifiedBy() const
+    { return _pimpl->modifiedBy(); }
+
+    bool Selectable::hasLicenceConfirmed() const
+    { return _pimpl->hasLicenceConfirmed(); }
+
+    void Selectable::setLicenceConfirmed( bool val_r )
+    { _pimpl->setLicenceConfirmed( val_r ); }
+
+
+    Selectable::Fate Selectable::fate() const
+    {
+      switch ( status() ) {
+      case S_Update:
+      case S_Install:
+      case S_AutoUpdate:
+      case S_AutoInstall:
+        return TO_INSTALL;
+        break;
+
+      case S_Del:
+      case S_AutoDel:
+        return TO_DELETE;
+        break;
+
+      case S_Protected:
+      case S_Taboo:
+      case S_KeepInstalled:
+      case S_NoInst:
+        break;
+      }
+      return UNMODIFIED;
+    };
+
+    bool Selectable::setFate( Fate fate_r, ResStatus::TransactByValue causer_r )
+    {
+      switch ( fate_r )
+      {
+        case TO_INSTALL:
+          return setStatus( hasInstalledObj() ? S_Update : S_Install, causer_r );
+          break;
+
+        case TO_DELETE:
+          return setStatus( S_Del, causer_r );
+          break;
+
+        case UNMODIFIED:
+          switch ( status() ) {
+            case S_Protected:
+            case S_Taboo:
+              return true;
+              break;
+            default:
+              return setStatus( hasInstalledObj() ? S_KeepInstalled : S_NoInst, causer_r );
+              break;
+          }
+          break;
+      }
+      return false;
+    }
+
+    bool Selectable::setInstalled( ResStatus::TransactByValue causer_r )
+    {
+      return( hasInstalledObj() || setStatus( S_Install, causer_r ) );
+    }
+
+    bool Selectable::setUpToDate( ResStatus::TransactByValue causer_r )
+    {
+      if ( ! hasInstalledObj() )
+        return setStatus( S_Install, causer_r );
+
+      PoolItem cand( candidateObj() );
+      if ( ! cand )
+        return true;
+
+      return( installedObj()->edition() >= cand->edition()
+              || setStatus( S_Update, causer_r ) );
+    }
+
+    bool Selectable::setDeleted( ResStatus::TransactByValue causer_r )
+    {
+      return( ! hasInstalledObj() || setStatus( S_Del, causer_r ) );
+    }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const Selectable & obj )
+    { return str << *(obj._pimpl); }
+
+    std::ostream & dumpOn( std::ostream & str, const Selectable & obj )
+    { return dumpOn( str, *(obj._pimpl) ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ui/Selectable.h b/zypp/ui/Selectable.h
new file mode 100644 (file)
index 0000000..6a9d4ea
--- /dev/null
@@ -0,0 +1,525 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/Selectable.h
+ *
+*/
+#ifndef ZYPP_UI_SELECTABLE_H
+#define ZYPP_UI_SELECTABLE_H
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Iterator.h"
+
+#include "zypp/ui/SelectableTraits.h"
+#include "zypp/ui/Status.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    DEFINE_PTR_TYPE(Selectable);
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Selectable
+    //
+    /** Collects PoolItems of same kind and name.
+     *
+     * Selectable is a status wrapper. The ui::Status is calculated
+     * from (and transated to) \ref PoolItems individual \ref ResStatus
+     * values.
+     *
+     * Available objects are sorted according the solver policies, 'best'
+     * packages first (e.g. by repository priority, then Arch, then Edition).
+     *
+     * Installed objects are sorted according the installation date, newer install
+     * time first.
+    */
+    class Selectable : public base::ReferenceCounted, private base::NonCopyable
+    {
+      friend std::ostream & operator<<( std::ostream & str, const Selectable & obj );
+      friend std::ostream & dumpOn( std::ostream & str, const Selectable & obj );
+
+    public:
+      typedef intrusive_ptr<Selectable>        Ptr;
+      typedef intrusive_ptr<const Selectable>  constPtr;
+
+      /** Iterates over ResObject::constPtr */
+      typedef SelectableTraits::available_iterator      available_iterator;
+      typedef SelectableTraits::available_size_type     available_size_type;
+
+      typedef SelectableTraits::installed_iterator      installed_iterator;
+      typedef SelectableTraits::installed_size_type     installed_size_type;
+
+      typedef SelectableTraits::picklist_iterator      picklist_iterator;
+      typedef SelectableTraits::picklist_size_type     picklist_size_type;
+
+    public:
+      /** \name Static ctor substitues picking the item from the pool.
+       * \code
+       * Selectable::Ptr item;
+       * item = Selectable::get( "amarok );                  // package amamrok
+       * item = Selectable::get( ResKind::patch, "amarok );  // patch amamrok
+       * item = Selectable::get( IdString( "patch:amarok" ); // patch amamrok
+       * \endcode
+      */
+      //@{
+      /** Get the \ref Selctable */
+      static Ptr get( const pool::ByIdent & ident_r );
+
+      /** Get the \ref Selctable by it's \c sat-identifyer. */
+      static Ptr get( IdString ident_r )
+      { return get( pool::ByIdent( ident_r ) ); }
+
+      /** Get the \ref Selctable by \c kind and \c name. */
+      static Ptr get( ResKind kind_r, const std::string & name_r )
+      { return get( pool::ByIdent( kind_r, name_r ) ); }
+
+      /** Get the \c Package \ref Selctable by \c name. */
+      static Ptr get( const std::string & name_r )
+      { return get( pool::ByIdent( ResKind::package, name_r ) ); }
+
+      /** Get the \ref Selctable containing a specific \ref sat::Solvable. */
+      static Ptr get( const sat::Solvable & solv_r )
+      { return get( pool::ByIdent( solv_r ) ); }
+
+      /** Get the \ref Selctable containing a specific \ref ResObject. */
+      static Ptr get( const ResObject::constPtr & resolvable_r )
+      { return resolvable_r ? get( resolvable_r->satSolvable() ) : Ptr(); }
+
+      /** Get the \ref Selctable containing a specific \ref PoolItem. */
+      static Ptr get( const PoolItem & pi_r )
+      { return get( pi_r.satSolvable() ); }
+      //@}
+
+    public:
+      /** The identifier.
+       * This is the solvables \ref name, \b except for packages and
+       * source packes, prefixed by it's \ref kind.
+       * \see \ref sat::Solvable.
+       */
+      IdString ident() const;
+
+      /** The ResObjects kind. */
+      ResObject::Kind kind() const;
+
+      /** The ResObjects name.  */
+      const std::string & name() const;
+
+      /** The last Installed object. */
+      PoolItem installedObj() const;
+
+      /** The 'best' or 'most interesting' among all available objects.
+       * One that is, or is likely to be, chosen for installation, unless
+       * it violated any solver policy (see \ref updateCandidateObj).
+       */
+      PoolItem candidateObj() const;
+
+      /** The best candidate provided by a specific \ref Repository, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the \ref Repository
+       * does not provide this object.
+       */
+      PoolItem candidateObjFrom( Repository repo_r ) const;
+
+      /** The best candidate for update, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the best object is
+       * already installed, and all available objects violate at least one
+       * update policy.
+       */
+      PoolItem updateCandidateObj() const;
+
+      /** Simply the highest available version, ignoring priorities and policies.
+       * It's doubtful whether solely looking at the version makes a good
+       * candidate, but apps ask for it. Beware that different vendors may
+       * use different (uncomparable) version schemata.
+       */
+      PoolItem highestAvailableVersionObj() const;
+
+     /** \c True if \a rhs is installed and one with the same content is available.
+       * Basically the same name, edition, arch, vendor and buildtime.
+       * \see \ref sat::Solvable::identical
+       */
+      bool identicalAvailable( const PoolItem & rhs ) const;
+
+     /** \c True if \a rhs has the same content as an installed one.
+       * Basically the same name, edition, arch, vendor and buildtime.
+       * \see \ref sat::Solvable::identical
+       */
+      bool identicalInstalled( const PoolItem & rhs ) const;
+
+      /** \c True if the \ref candidateObj is installed (same content).
+       * \see \ref identicalInstalled.
+       */
+      bool identicalInstalledCandidate() const
+      { return identicalInstalled( candidateObj() ); }
+
+      /** \c True if the \ref updateCandidateObj is installed (same content).
+       * \see \ref identicalInstalled.
+       */
+      bool identicalInstalledUpdateCandidate() const
+      { return identicalInstalled( updateCandidateObj() ); }
+
+
+      /** Return the \ref installedObj resolvable casted to a specific kind.
+       * \code
+       *   Selectable mySelectable;
+       *   Package::constPtr p( mySelectable.installedAsKind<Package>() );
+       * \endcode
+      */
+      template<class _Res>
+      typename ResTraits<_Res>::constPtrType installedAsKind() const
+      { return asKind<_Res>( candidateObj() ); }
+
+      /** Return the \ref candidateObj resolvable casted to a specific kind.
+       * \code
+       *   Selectable mySelectable;
+       *   Package::constPtr p( mySelectable.candidateAsKind<Package>() );
+       * \endcode
+      */
+      template<class _Res>
+      typename ResTraits<_Res>::constPtrType candidateAsKind() const
+      { return asKind<_Res>( candidateObj() ); }
+
+      /** Set a candidate (out of available objects).
+       * \return The new candidate, or NULL if choice was invalid
+       * (NULL or not among availableObjs). An invalid choice
+       * selects the default candidate.
+       * In case the causer is not \c ResStatus::USER the operation
+       * may also fail if there are insufficient permissions to change
+       * a transacting candidate.
+       */
+      PoolItem setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+      /** \overload */
+      PoolItem setCandidate( ResObject::constPtr newCandidate_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Arrange the specified candidate (out of available objects) to be on system after commit.
+       * If the specified candidate is not already installed (\ref identicalInstalled),
+       * and the \a causer_r has sufficient permisssion, then \a newCandidate_r is set as the new
+       * candidate (\ref setCandidate) and selected for installation.
+       * \returns \c True if \a newCandidate_r is already installed or sucessfully selected for installation.
+       */
+      bool setOnSystem( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** An object you could use as pars pro toto.
+       *
+       * \return the \ref candidateObj, or ,if no available objects
+       * exist, the \ref installedObj.
+       */
+      PoolItem theObj() const;
+
+      ////////////////////////////////////////////////////////////////////////
+
+      /** \name Available objects iterators.
+       * Oredered according to solver policy. 'Best' first.
+      */
+      //@{
+      bool availableEmpty() const;
+      available_size_type availableSize() const;
+      available_iterator availableBegin() const;
+      available_iterator availableEnd() const;
+      //@}
+
+      ////////////////////////////////////////////////////////////////////////
+
+      /** \name Installed objects iterators.
+       * Ordered by install time. Latest first.
+      */
+      //@{
+      bool installedEmpty() const;
+      installed_size_type installedSize() const;
+      installed_iterator installedBegin() const;
+      installed_iterator installedEnd() const;
+      //}
+
+      ////////////////////////////////////////////////////////////////////////
+
+      /** \name picklist iterators.
+       * This is basically the available items list prepended by those
+       * installed items, that are nolonger \ref identicalAvailable.
+       */
+      //@{
+      bool picklistEmpty() const;
+      picklist_size_type picklistSize() const;
+      picklist_iterator picklistBegin() const;
+      picklist_iterator picklistEnd() const;
+      //}
+
+      ////////////////////////////////////////////////////////////////////////
+
+    public:
+      /** \name Query for objects within this Selectable.
+      */
+      //@{
+      /** True if either installed or candidate object is present */
+      bool hasObject() const
+      { return (! installedEmpty()) || candidateObj(); }
+
+      /** True if installed object is present. */
+      bool hasInstalledObj() const
+      { return ! installedEmpty(); }
+
+      /** True if candidate object is present. */
+      bool hasCandidateObj() const
+      { return candidateObj(); }
+
+      /** True if installed and candidate object is present */
+      bool hasBothObjects() const
+      { return (! installedEmpty()) && candidateObj(); }
+
+      /** True if installed object is present but no candidate. */
+      bool hasInstalledObjOnly() const
+      { return (! installedEmpty()) && ! candidateObj(); }
+
+      /** True if candidate object is present but no installed. */
+      bool hasCandidateObjOnly() const
+      { return ( installedEmpty() ) && candidateObj(); }
+      //@}
+
+      /**
+       * True if this package has no replacement from
+       * the available repositories
+       */
+      bool isUnmaintained() const;
+
+      /** \name Multiversion install.
+       *
+       * Using \ref pickInstall or \ref pickDelete with non-multiversionInstall items
+       * is possible, but additional constraints will apply. E.g. selecting one item for
+       * install will deselect any other.
+       */
+      //@{
+      /** Whether different versions of this package can be installed at the same time.
+       * Per default \c false. \see also \ref ZConfig::multiversion.
+       */
+      bool multiversionInstall() const;
+
+      /** Select a specific available item for installation.
+       */
+      bool pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r = ResStatus::USER, bool yesno_r = true );
+
+      /** Deselect a specific available item from installation.
+      */
+      bool pickNoInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r = ResStatus::USER )
+      { return pickInstall( pi_r, causer_r, false ); }
+
+      /** Select a specific installed item for deletion.
+       */
+      bool pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r = ResStatus::USER, bool yesno_r = true );
+
+      /** Deselect a specific installed item from deletion.
+       */
+      bool pickNoDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r = ResStatus::USER )
+      { return pickDelete( pi_r, causer_r, false ); }
+
+      /** Compute the \ref ui::Status for an individual PoolItem.
+       * This just takes into account the item and any identical
+       * installed (or available) one.
+       * \code
+       *   Assume there are 3 identical 'foo-1.1 (vendor A)' items,
+       *   one 'foo-2.1 (vendor A)' and one ''foo-1.1 (vendor B)':
+       *
+       *   installed: . foo-1.1 (vendor A)          -> S_KeepInstalled
+       *   available:   foo-2.1 (vendor A) (repo 1) -> S_NoInst
+       *              . foo-1.1 (vendor A) (repo 1) -> S_KeepInstalled
+       *              . foo-1.1 (vendor A) (repo 2) -> S_KeepInstalled
+       *                foo-1.1 (vendor B) (repo 3) -> S_NoInst
+       *
+       *   After 'foo-1.1 (vendor A) (repo 1)' was selected to be installed:
+       *
+       *   installed: . foo-1.1 (vendor A)          -> S_Update
+       *   available:   foo-2.1 (vendor A) (repo 1) -> S_NoInst
+       *              I foo-1.1 (vendor A) (repo 1) -> S_Update
+       *              . foo-1.1 (vendor A) (repo 2) -> S_KeepInstalled
+       *                foo-1.1 (vendor B) (repo 3) -> S_NoInst
+       * \endcode
+       * \see \ref sat::Solvable::identical
+       */
+      Status pickStatus( const PoolItem & pi_r ) const;
+
+      /** Assign a new status to a specific item. */
+      bool setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+      //@}
+
+      /** \name Classification of available patches (pseudo installed items).
+       * A patch is either \c not \c relevant, \c satisfied or \c broken.
+       * The same applies to other pseudo installed kinds.
+       * \see \ref traits::isPseudoInstalled
+       */
+      //@{
+      /** Returns true for packages, because packages are not
+       * classified by the solver.
+       */
+      bool isUndetermined() const;
+
+      /** Returns true if the patch is relevant which means that at least
+       *  one package of the patch is installed.
+       */
+      bool isRelevant() const;
+
+      /** Whether a relevant patchs requirements are met. */
+      bool isSatisfied() const;
+
+      /** Whether a relevant patchs requirements are broken. */
+      bool isBroken() const;
+
+      /** This includes \c unlocked broken patches, as well as those already
+       * selected to be installed. This is because already selected
+       * patches will be classified as \c satisfied. \c Locked but broken
+       * patches will be classified as \ref isUnwanted.
+       */
+      bool isNeeded() const;
+
+      /** Broken (needed) but locked patches. */
+       bool isUnwanted() const;
+      //@}
+
+     public:
+      /** \name Query and maip objects fate in case of commit.
+       */
+      //@{
+      enum Fate {
+        TO_DELETE  = -1,
+        UNMODIFIED = 0,
+        TO_INSTALL = 1
+      };
+      /**  */
+      Fate fate() const;
+
+      /** True if neither to delete or to install */
+      bool unmodified() const
+      { return fate() == UNMODIFIED; }
+
+      /** True if locked (subclass of unmodified). */
+      bool locked() const
+      { Status st( status() ); return( st == S_Protected || st == S_Taboo ); }
+
+      /** True if either to delete or to install */
+      bool toModify() const
+      { return fate() != UNMODIFIED; }
+
+      /** True if to delete */
+      bool toDelete() const
+      { return fate() == TO_DELETE; }
+
+      /** True if to install */
+      bool toInstall() const
+      { return fate() == TO_INSTALL; }
+
+      /** True if would be on system after commit. */
+      bool onSystem() const
+      { return( ( hasInstalledObj() && !toDelete() )
+              ||( hasCandidateObj() && toInstall() ) ); }
+
+      /** True if would be off system after commit. */
+      bool offSystem() const
+      { return ! onSystem(); }
+
+      /** */
+      bool setFate( Fate fate_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Set the item to be installed (new- or re-install). */
+      bool setToInstall( ResStatus::TransactByValue causer_r = ResStatus::USER )
+      { return setFate( TO_INSTALL, causer_r ); }
+
+      /** Take care the item gets installed if it is not. */
+      bool setInstalled( ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Take care the item gets installed if it is not, or is older. */
+      bool setUpToDate( ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Set the item to be deleted (must be installed). */
+      bool setToDelete( ResStatus::TransactByValue causer_r = ResStatus::USER )
+      { return setFate( TO_DELETE, causer_r ); }
+
+      /** Take care the item gets deleted if it is installed. */
+      bool setDeleted( ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Set the item to stay unmodified. */
+      bool unset( ResStatus::TransactByValue causer_r = ResStatus::USER )
+      { return setFate( UNMODIFIED, causer_r ); }
+      //@}
+
+    public:
+      /**
+       * \name Special inteface for Y2UI.
+       * \note This interface acts on \ref ResStatus::USER level.
+       * The \ref Status enum, and allowed state transitions are
+       * tightly related to the Y2UI.
+      */
+      //@{
+      /** Return the current Status */
+      Status status() const;
+
+      /**
+       * Try to set a new Status.
+       * Returns \c false if the transitions is not allowed.
+       */
+      bool setStatus( Status state_r, ResStatus::TransactByValue causer_r = ResStatus::USER );
+
+      /** Return who caused the modification. */
+      ResStatus::TransactByValue modifiedBy() const;
+
+      /** Return value of LicenceConfirmed bit. */
+      bool hasLicenceConfirmed() const;
+
+      /** Set LicenceConfirmed bit. */
+      void setLicenceConfirmed( bool val_r = true );
+      //@}
+
+    public:
+      /** Implementation  */
+      class Impl;
+      typedef shared_ptr<Impl> Impl_Ptr;
+      /** Default ctor */
+      Selectable( Impl_Ptr pimpl_r );
+    private:
+      /** Dtor */
+      ~Selectable();
+    private:
+      /** Pointer to implementation */
+      RW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Selectable Stream output */
+    std::ostream & operator<<( std::ostream & str, const Selectable & obj );
+
+    /** \relates Selectable More verbose stream output */
+    std::ostream & dumpOn( std::ostream & str, const Selectable & obj );
+
+    /** Solvable to Selectable transform functor.
+     * \relates Selectable
+     * \relates sat::SolvIterMixin
+     */
+    struct asSelectable
+    {
+      typedef Selectable_Ptr result_type;
+
+      Selectable_Ptr operator()( const sat::Solvable & solv_r ) const;
+
+      Selectable_Ptr operator()( const PoolItem & pi_r ) const
+      { return operator()( pi_r.satSolvable() ); }
+    };
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_UI_SELECTABLE_H
diff --git a/zypp/ui/SelectableImpl.cc b/zypp/ui/SelectableImpl.cc
new file mode 100644 (file)
index 0000000..9c02863
--- /dev/null
@@ -0,0 +1,621 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/SelectableImpl.cc
+ *
+*/
+#include <iostream>
+//#include "zypp/base/Logger.h"
+
+#include "zypp/ui/SelectableImpl.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    /** Simple ResStatus backup stack.
+     * \ref restore simply rewinds all remembered status.
+    */
+    class StatusBackup
+    {
+      public:
+        typedef ResStatus::TransactByValue Causer;
+
+      public:
+        /** Backup status. */
+        ResStatus & backup( ResStatus & status_r )
+        {
+          _backup.push_back( status_r );
+          return status_r;
+        }
+        /** \overload */
+        ResStatus & backup( const PoolItem & pi_r )
+        { return backup( pi_r.status() ); }
+
+        /** Backup status. */
+        ResStatus & operator()( ResStatus & status_r )
+        { return backup( status_r ); }
+        /** \overload */
+        ResStatus & operator()( const PoolItem & pi_r )
+        { return backup( pi_r.status() ); }
+
+        /** Restore all status. */
+        bool restore()
+        {
+          for_( rit, _backup.rbegin(), _backup.rend() )
+            rit->replay();
+          return false; // restore is done on error - return restore();
+        }
+
+      public:
+        /** lowlevel \c ResStatus::setTransact */
+        bool setTransact( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+        { return backup( pi_r ).setTransact( yesno_r, causer_r ); }
+
+        /** lowlevel \c ResStatus::setLock */
+        bool setLock( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+        { return backup( pi_r ).setLock( yesno_r, causer_r ); }
+
+        /** lowlevel \c ResStatus::setTransact(true). */
+        bool setTransactTrue( const PoolItem & pi_r, Causer causer_r )
+        { return setTransact( pi_r, true, causer_r ); }
+
+        /** lowlevel \c ResStatus::setTransact(false). */
+        bool setTransactFalse( const PoolItem & pi_r, Causer causer_r )
+        { return setTransact( pi_r, false, causer_r ); }
+
+      public:
+        /** highevel set transact (force unlock). */
+        bool transact( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setLock( false, causer_r ) ) return false;
+          if ( ! status.setTransact( true, causer_r ) ) return false;
+          return true;
+        }
+
+        /** highlevel set locked. */
+        bool lock( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setTransact( false, causer_r ) ) return false;
+          if ( ! status.setLock( true, causer_r ) ) return false;
+          return true;
+        }
+
+        /** highlevel unlock (also unsets transact). */
+        bool unlock( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setTransact( false, causer_r ) ) return false;
+          if ( ! status.setLock( false, causer_r ) ) return false;
+          return true;
+        }
+
+        /** Highlevel action. */
+        typedef bool (StatusBackup::*Action)( const PoolItem &, Causer );
+
+        /** Highlevel action on range of items. */
+        template <class _Iter>
+        bool forEach( _Iter begin_r, _Iter end_r, Action action_r, Causer causer_r )
+        {
+          for_( it, begin_r, end_r )
+            if ( ! (this->*action_r)( *it, causer_r ) )
+              return false;
+          return true;
+        }
+
+      private:
+        std::vector<resstatus::StatusBackup> _backup;
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : StatusHelper
+    //
+    /** \todo Unify status and pickStatus.
+    */
+    struct StatusHelper
+    {
+      StatusHelper( const Selectable::Impl & impl, ResStatus::TransactByValue causer_r )
+      : _impl( impl )
+      , inst( impl.installedObj() )
+      , cand( impl.candidateObj() )
+      , causer( causer_r )
+      {}
+
+      typedef Selectable::Impl::available_const_iterator available_const_iterator;
+
+      //
+      // Queries
+      //
+      bool hasInstalled() const
+      { return inst; }
+
+      bool hasCandidate() const
+      { return cand; }
+
+      bool hasInstalledOnly() const
+      { return inst && !cand; }
+
+      bool hasCandidateOnly() const
+      { return cand && !inst; }
+
+      bool hasBoth() const
+      { return inst && cand; }
+
+      /** \name Topevel methods must restore status on failure. */
+      //@{
+      bool setInstall()
+      {
+        if ( cand )
+        {
+          if ( inst ) {
+            for_( it, _impl.installedBegin(), _impl.installedEnd() )
+            {
+              ResStatus & inststatus( backup( it->status() ) );
+              if ( ! inststatus.setTransact( false, causer ) ) return restore();
+              if ( ! inststatus.setLock    ( false, causer ) ) return restore();
+              if ( ! cand->multiversionInstall() )
+              {
+              // This is what the solver most probabely will do.
+              // If we are wrong the solver will correct it. But
+              // this way we will get a better disk usage result,
+              // even if no autosolving is on.
+                inststatus.setTransact( true, ResStatus::SOLVER );
+              }
+            }
+          }
+          if ( ! unlockCandidates() ) return restore();
+          ResStatus & candstatus( backup( cand.status() ) );
+          if ( ! candstatus.setTransact( true, causer ) ) return restore();
+          return true;
+        }
+        return false;
+      }
+
+      bool setDelete()
+      {
+        if ( inst )
+        {
+          if ( ! resetTransactingCandidates() ) return restore();
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            ResStatus & inststatus( backup( it->status() ) );
+            if ( ! inststatus.setLock( false, causer ) ) return restore();
+            if ( ! inststatus.setTransact( true, causer ) ) return restore();
+          }
+          return true;
+        }
+        return false;
+      }
+
+      bool unset()
+      {
+        if ( inst )
+        {
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            ResStatus & inststatus( backup( it->status() ) );
+            if ( ! inststatus.setTransact( false, causer ) ) return restore();
+            if ( ! inststatus.setLock( false, causer ) ) return restore();
+          }
+        }
+        if ( ! unlockCandidates() ) return restore();
+        return true;
+      }
+
+      bool setProtected()
+      {
+        if ( causer != ResStatus::USER ) // by user only
+          return false;
+
+        if ( inst ) {
+          resetTransactingCandidates();
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            it->status().setTransact( false, causer );
+            it->status().setLock( true, causer );
+          }
+          return true;
+        } else
+          return false;
+      }
+
+      bool setTaboo()
+      {
+        if ( causer != ResStatus::USER ) // by user only
+          return false;
+
+        if ( cand ) {
+          lockCandidates();
+          return true;
+        } else
+          return false;
+      }
+      //@}
+
+    private:
+      /** \name Helper methods backup status but do not replay. */
+      //@{
+      bool resetTransactingCandidates()
+      {
+        for_( it, _impl.availableBegin(), _impl.availableEnd() )
+        {
+          ResStatus & status( backup( (*it).status() ) );
+          if ( ! status.setTransact( false, causer ) ) return false;
+        }
+        return true;
+      }
+      bool unlockCandidates()
+      {
+        for_( it, _impl.availableBegin(), _impl.availableEnd() )
+        {
+          ResStatus & status( backup( (*it).status() ) );
+          if ( ! status.setTransact( false, causer ) ) return false;
+          if ( ! status.setLock( false, causer ) ) return false;
+        }
+        return true;
+      }
+      bool lockCandidates()
+      {
+        for_( it, _impl.availableBegin(), _impl.availableEnd() )
+        {
+          ResStatus & status( backup( (*it).status() ) );
+          if ( ! status.setTransact( false, causer ) ) return false;
+          if ( ! status.setLock( true, causer ) ) return false;
+        }
+        return true;
+      }
+      //@}
+
+    private:
+      const Selectable::Impl & _impl;
+      PoolItem                   inst;
+      PoolItem                   cand;
+      ResStatus::TransactByValue causer;
+
+    private:
+      bool restore() { return backup.restore(); }
+      StatusBackup backup;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Selectable::Impl
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    Status Selectable::Impl::status() const
+    {
+      PoolItem cand( candidateObj() );
+      if ( cand && cand.status().transacts() )
+        {
+          if ( cand.status().isByUser() )
+            return( installedObj() ? S_Update : S_Install );
+          else
+            return( installedObj() ? S_AutoUpdate : S_AutoInstall );
+        }
+
+      if ( installedObj() && installedObj().status().transacts() )
+        {
+          return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
+        }
+
+      if ( installedObj() && allInstalledLocked() )
+         return S_Protected;
+
+      if ( !installedObj() && allCandidatesLocked() )
+         return S_Taboo;
+
+      // KEEP state:
+      if ( installedObj() )
+        return S_KeepInstalled;
+      // Report pseudo installed items as installed, if they are satisfied.
+      if ( traits::isPseudoInstalled( kind() )
+           && cand.status().isSatisfied() ) // no installed, so we must have candidate
+        return S_KeepInstalled;
+
+      return S_NoInst;
+    }
+
+    bool Selectable::Impl::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
+    {
+      StatusHelper self( *this, causer_r );
+
+      switch ( state_r )
+        {
+        case S_Protected:
+           return self.setProtected();
+        case S_Taboo:
+           return self.setTaboo();
+        case S_AutoDel:
+        case S_AutoInstall:
+        case S_AutoUpdate:
+          // Auto level is SOLVER level. UI may query, but not
+          // set at this level.
+          break;
+
+        case S_Del:
+          return self.setDelete();
+          break;
+
+        case S_Install:
+          return self.hasCandidateOnly() && self.setInstall();
+          break;
+
+        case S_Update:
+          return self.hasBoth() && self.setInstall();
+          break;
+
+        case S_KeepInstalled:
+          return self.hasInstalled() && self.unset();
+          break;
+
+        case S_NoInst:
+          return !self.hasInstalled() && self.unset();
+          break;
+        }
+
+      return false;
+    }
+
+    PoolItem Selectable::Impl::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
+    {
+      PoolItem newCandidate;
+
+      if ( newCandidate_r ) // must be in available list
+      {
+        for_( it, availableBegin(), availableEnd() )
+        {
+          if ( *it == newCandidate_r )
+          {
+            newCandidate = *it;
+            break;
+          }
+        }
+      }
+
+      if ( newCandidate )
+      {
+        PoolItem trans( transactingCandidate() );
+        if ( trans && trans != newCandidate )
+        {
+          // adjust transact to the new cancidate
+          if (    trans.status().maySetTransact( false, causer_r )
+               && newCandidate.status().maySetTransact( true, causer_r ) )
+          {
+            trans.status().setTransact( false, causer_r );
+            newCandidate.status().setTransact( true, causer_r );
+          }
+          else
+          {
+            // No permission to change a transacting candidate.
+            // Leave _candidate untouched and return NULL.
+            return PoolItem();
+          }
+        }
+      }
+
+      return _candidate = newCandidate;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+    {
+      if ( identicalInstalled( pi_r ) )
+        return setPickStatus( pi_r, ( yesno_r ? S_Update : S_KeepInstalled ), causer_r );
+      return setPickStatus( pi_r, ( yesno_r ? S_Install : S_NoInst ), causer_r );
+    }
+
+    bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+    {
+      return setPickStatus( pi_r, ( yesno_r ? S_Del : S_KeepInstalled ), causer_r );
+    }
+
+    bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
+    {
+      if ( pi_r.satSolvable().ident() != ident() )
+        return false;  // not my PoolItem
+
+      if ( ! multiversionInstall() )
+        return false;  // We're not yet ready for this.
+      // TODO: Without multiversionInstall take care at most ONE available is set
+      // to install. Upon install ALL installed get deleted. Only upon deletetion
+      // one might pick individual versions (but more than one would be an error here).
+
+      StatusBackup backup;
+      std::vector<PoolItem> i;
+      std::vector<PoolItem> a;
+
+      for_( it, installedBegin(), installedEnd() )
+        if ( identical( *it, pi_r ) )
+          i.push_back( *it );
+      for_( it, availableBegin(), availableEnd() )
+        if ( identical( *it, pi_r ) )
+          a.push_back( *it );
+
+      switch ( state_r )
+      {
+        case S_Protected:
+          if ( causer_r == ResStatus::USER && ! i.empty() )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_Taboo:
+          if ( causer_r == ResStatus::USER && ! a.empty() )
+          {
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_AutoDel:
+        case S_AutoInstall:
+        case S_AutoUpdate:
+          // Auto level is SOLVER level. UI may query, but not
+          // set at this level.
+          break;
+
+        case S_Del:
+          if ( ! i.empty() )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::transact, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_Install:
+          if ( i.empty() && ! a.empty() )
+          {
+            // maybe unlock candidate only?
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r ); // status already backed up above
+            if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_Update:
+          if ( ! i.empty() && ! a.empty() )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::setTransactTrue, ResStatus::SOLVER ) ) return backup.restore();
+            // maybe unlock candidate only?
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r ); // status already backed up above
+            if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_KeepInstalled:
+          if ( ! i.empty()  )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+
+        case S_NoInst:
+          if ( i.empty()  )
+          {
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
+      }
+      return false;
+    }
+
+    Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
+    {
+      if ( pi_r.satSolvable().ident() != ident() )
+        return Status(-1); // not my PoolItem
+
+      std::vector<PoolItem> i;
+      std::vector<PoolItem> a;
+      PoolItem ti;
+      PoolItem ta;
+
+      for_( it, installedBegin(), installedEnd() )
+        if ( identical( *it, pi_r ) )
+        {
+          i.push_back( *it );
+          if ( ! ti && it->status().transacts() )
+            ti = *it;
+        }
+
+      for_( it, availableBegin(), availableEnd() )
+        if ( identical( *it, pi_r ) )
+        {
+          a.push_back( *it );
+          if ( ! ta && it->status().transacts() )
+            ta = *it;
+        }
+
+      if ( ta )
+      {
+        if ( ta.status().isByUser() )
+          return( i.empty() ? S_Install : S_Update );
+        else
+          return( i.empty() ? S_AutoInstall : S_AutoUpdate );
+      }
+
+      if ( ti )
+      {
+        return( ti.status().isByUser() ? S_Del : S_AutoDel );
+      }
+
+      for_( it, i.begin(), i.end() )
+        if ( it->status().isLocked() )
+          return S_Protected;
+
+      if ( i.empty() )
+      {
+        bool allALocked = true;
+        for_( it, a.begin(), a.end() )
+          if ( ! it->status().isLocked() )
+          {
+            allALocked = false;
+            break;
+          }
+        if ( allALocked )
+          return S_Taboo;
+      }
+
+      // KEEP state:
+      if ( ! i.empty() )
+        return S_KeepInstalled;
+      // Report pseudo installed items as installed, if they are satisfied.
+      if ( traits::isPseudoInstalled( kind() )
+           && ( ta ? ta : *a.begin() ).status().isSatisfied() ) // no installed, so we must have candidate
+        return S_KeepInstalled;
+
+      return S_NoInst;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
+    {
+      PoolItem cand( candidateObj() );
+      if ( cand && cand.status().transacts() )
+        return cand.status().getTransactByValue();
+
+      if ( installedObj() && installedObj().status().transacts() )
+        return installedObj().status().getTransactByValue();
+
+      if ( cand )
+        return cand.status().getTransactByValue();
+
+      if ( installedObj() )
+        return installedObj().status().getTransactByValue();
+
+      return ResStatus::SOLVER;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ui/SelectableImpl.h b/zypp/ui/SelectableImpl.h
new file mode 100644 (file)
index 0000000..9cdd33f
--- /dev/null
@@ -0,0 +1,517 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/SelectableImpl.h
+ *
+*/
+#ifndef ZYPP_UI_SELECTABLEIMPL_H
+#define ZYPP_UI_SELECTABLEIMPL_H
+
+#include <iostream>
+#include "zypp/base/LogTools.h"
+
+#include "zypp/base/PtrTypes.h"
+
+#include "zypp/ZConfig.h"
+#include "zypp/ui/Selectable.h"
+#include "zypp/ui/SelectableTraits.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : Selectable::Impl
+    //
+    /** Selectable implementation.
+     * \note Implementation is based in PoolItem, just the Selectable
+     * inteface restricts them to ResObject::constPtr.
+    */
+    struct Selectable::Impl
+    {
+    public:
+
+      typedef SelectableTraits::AvailableItemSet         AvailableItemSet;
+      typedef SelectableTraits::available_iterator       available_iterator;
+      typedef SelectableTraits::available_const_iterator available_const_iterator;
+      typedef SelectableTraits::available_size_type      available_size_type;
+
+      typedef SelectableTraits::InstalledItemSet         InstalledItemSet;
+      typedef SelectableTraits::installed_iterator       installed_iterator;
+      typedef SelectableTraits::installed_const_iterator installed_const_iterator;
+      typedef SelectableTraits::installed_size_type      installed_size_type;
+
+      typedef SelectableTraits::PickList               PickList;
+
+    public:
+      template <class _Iterator>
+      Impl( const ResObject::Kind & kind_r,
+            const std::string & name_r,
+            _Iterator begin_r,
+            _Iterator end_r )
+      : _ident( sat::Solvable::SplitIdent( kind_r, name_r ).ident() )
+      , _kind( kind_r )
+      , _name( name_r )
+      {
+        for_( it, begin_r, end_r )
+        {
+          if ( it->status().isInstalled() )
+            _installedItems.insert( *it );
+          else
+            _availableItems.insert( *it );
+        }
+        _defaultCandidate = defaultCandidate();
+      }
+
+    public:
+      /**  */
+      IdString ident() const
+      { return _ident; }
+
+      /**  */
+      ResObject::Kind kind() const
+      { return _kind; }
+
+      /**  */
+      const std::string & name() const
+      { return _name; }
+
+      /**  */
+      Status status() const;
+
+      /**  */
+      bool setStatus( Status state_r, ResStatus::TransactByValue causer_r );
+
+      /** Installed object (transacting ot highest version). */
+      PoolItem installedObj() const
+      {
+        if ( installedEmpty() )
+          return PoolItem();
+        PoolItem ret( transactingInstalled() );
+        return ret ? ret : *_installedItems.begin();
+      }
+
+      /** Best among available objects.
+       * The transacting candidate or the one scheduled to receive
+       * the transact bit.
+      */
+      PoolItem candidateObj() const
+      {
+        PoolItem ret( transactingCandidate() );
+        if ( ret )
+          return ret;
+        return _candidate ? _candidate : _defaultCandidate;
+      }
+
+      /** Set a userCandidate (out of available objects).
+       * \return The new userCandidate or NULL if choice was invalid
+       * (not among availableObjs).
+      */
+      PoolItem setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r );
+
+      /** The best candidate provided by a specific \ref Repository, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the \ref Repository
+       * does not provide this object.
+       */
+      PoolItem candidateObjFrom( Repository repo_r ) const
+      {
+        for_( it, availableBegin(), availableEnd() )
+        {
+          if ( (*it)->repository() == repo_r )
+            return *it;
+        }
+        return PoolItem();
+      }
+
+      /** The best candidate for update, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the best object is
+       * already installed, and all available objects violate at least one
+       * update policy.
+       */
+      PoolItem updateCandidateObj() const
+      {
+        if ( multiversionInstall() || installedEmpty() || ! _defaultCandidate )
+          return _defaultCandidate;
+        // Here: installed and _defaultCandidate are non NULL and it's not a
+        //       multiversion install.
+
+        // update candidate must come from the highest priority repo
+        if ( _defaultCandidate->repoInfo().priority() != (*availableBegin())->repoInfo().priority() )
+          return PoolItem();
+
+        PoolItem installed( installedObj() );
+        // check vendor change
+        if ( ! ( ZConfig::instance().solver_allowVendorChange()
+                 || VendorAttr::instance().equivalent( _defaultCandidate->vendor(), installed->vendor() ) ) )
+          return PoolItem();
+
+        // check arch change (arch noarch changes are allowed)
+        if ( _defaultCandidate->arch() != installed->arch()
+           && ! ( _defaultCandidate->arch() == Arch_noarch || installed->arch() == Arch_noarch ) )
+          return PoolItem();
+
+        // check greater edition
+        if ( _defaultCandidate->edition() <= installed->edition() )
+          return PoolItem();
+
+        return _defaultCandidate;
+      }
+
+      /** \copydoc Selectable::highestAvailableVersionObj()const */
+      PoolItem highestAvailableVersionObj() const
+      {
+        PoolItem ret;
+        for_( it, availableBegin(), availableEnd() )
+        {
+          if ( !ret || (*it).satSolvable().edition() > ret.satSolvable().edition() )
+            ret = *it;
+        }
+        return ret;
+      }
+
+      /** \copydoc Selectable::identicalAvailable( const PoolItem & )const */
+      bool identicalAvailable( const PoolItem & rhs ) const
+      {
+        if ( !availableEmpty() && rhs )
+        {
+          for_( it, _availableItems.begin(), _availableItems.end() )
+          {
+            if ( identical( *it, rhs ) )
+              return true;
+          }
+        }
+        return false;
+      }
+
+      /** \copydoc Selectable::identicalInstalled( const PoolItem & )const */
+      bool identicalInstalled( const PoolItem & rhs ) const
+      {
+        if ( !installedEmpty() && rhs )
+        {
+          for_( it, _installedItems.begin(), _installedItems.end() )
+          {
+            if ( identical( *it, rhs ) )
+              return true;
+          }
+        }
+        return false;
+      }
+
+      /** Best among all objects. */
+      PoolItem theObj() const
+      {
+        PoolItem ret( candidateObj() );
+        if ( ret )
+          return ret;
+        return installedObj();
+      }
+
+      ////////////////////////////////////////////////////////////////////////
+
+      bool availableEmpty() const
+      { return _availableItems.empty(); }
+
+      available_size_type availableSize() const
+      { return _availableItems.size(); }
+
+      available_const_iterator availableBegin() const
+      { return _availableItems.begin(); }
+
+      available_const_iterator availableEnd() const
+      { return _availableItems.end(); }
+
+      ////////////////////////////////////////////////////////////////////////
+
+      bool installedEmpty() const
+      { return _installedItems.empty(); }
+
+      installed_size_type installedSize() const
+      { return _installedItems.size(); }
+
+      installed_iterator installedBegin() const
+      { return _installedItems.begin(); }
+
+      installed_iterator installedEnd() const
+      { return _installedItems.end(); }
+
+      ////////////////////////////////////////////////////////////////////////
+
+      const PickList & picklist() const
+      {
+        if ( ! _picklistPtr )
+        {
+          _picklistPtr.reset( new PickList );
+          // installed without identical avaialble first:
+          for_( it, _installedItems.begin(), _installedItems.end() )
+          {
+            if ( ! identicalAvailable( *it ) )
+              _picklistPtr->push_back( *it );
+          }
+          _picklistPtr->insert( _picklistPtr->end(), availableBegin(), availableEnd() );
+        }
+        return *_picklistPtr;
+      }
+
+      bool picklistEmpty() const
+      { return picklist().empty(); }
+
+      picklist_size_type picklistSize() const
+      { return picklist().size(); }
+
+      picklist_iterator picklistBegin() const
+      { return picklist().begin(); }
+
+      picklist_iterator picklistEnd() const
+      { return picklist().end(); }
+
+      ////////////////////////////////////////////////////////////////////////
+
+      bool isUnmaintained() const
+      { return availableEmpty(); }
+
+      bool multiversionInstall() const
+      { return theObj().satSolvable().multiversionInstall(); }
+
+      bool pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
+
+      bool pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
+
+      Status pickStatus( const PoolItem & pi_r ) const;
+
+      bool setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r );
+
+      ////////////////////////////////////////////////////////////////////////
+
+      bool isUndetermined() const
+      {
+        PoolItem cand( candidateObj() );
+        return ! cand || cand.isUndetermined();
+      }
+      bool isRelevant() const
+      {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isRelevant();
+      }
+      bool isSatisfied() const
+       {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isSatisfied();
+      }
+      bool isBroken() const
+      {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isBroken();
+      }
+
+      /** Return who caused the modification. */
+      ResStatus::TransactByValue modifiedBy() const;
+
+      /** Return value of LicenceConfirmed bit. */
+      bool hasLicenceConfirmed() const
+      { return candidateObj() && candidateObj().status().isLicenceConfirmed(); }
+
+      /** Set LicenceConfirmed bit. */
+      void setLicenceConfirmed( bool val_r )
+      { if ( candidateObj() ) candidateObj().status().setLicenceConfirmed( val_r ); }
+
+    private:
+      PoolItem transactingInstalled() const
+      {
+        for_( it, installedBegin(), installedEnd() )
+          {
+            if ( (*it).status().transacts() )
+              return (*it);
+          }
+        return PoolItem();
+      }
+
+      PoolItem transactingCandidate() const
+      {
+        for_( it, availableBegin(), availableEnd() )
+          {
+            if ( (*it).status().transacts() )
+              return (*it);
+          }
+        return PoolItem();
+      }
+
+      PoolItem defaultCandidate() const
+      {
+        if ( ! ( multiversionInstall() || installedEmpty() ) )
+        {
+          // prefer the installed objects arch and vendor
+          bool solver_allowVendorChange( ZConfig::instance().solver_allowVendorChange() );
+          for ( installed_const_iterator iit = installedBegin();
+                iit != installedEnd(); ++iit )
+          {
+            PoolItem sameArch; // in case there's no same vendor at least stay with same arch.
+            for ( available_const_iterator it = availableBegin();
+                  it != availableEnd(); ++it )
+            {
+              // 'same arch' includes allowed changes to/from noarch.
+              if ( (*iit)->arch() == (*it)->arch() || (*iit)->arch() == Arch_noarch || (*it)->arch() == Arch_noarch )
+              {
+                if ( ! solver_allowVendorChange )
+                {
+                  if ( VendorAttr::instance().equivalent( (*iit), (*it) ) )
+                    return *it;
+                  else if ( ! sameArch ) // remember best same arch in case no same vendor found
+                     sameArch = *it;
+                }
+                else // same arch is sufficient
+                  return *it;
+              }
+            }
+            if ( sameArch )
+              return sameArch;
+          }
+        }
+        if ( _availableItems.empty() )
+          return PoolItem();
+
+        return *_availableItems.begin();
+      }
+
+      bool allCandidatesLocked() const
+      {
+        for ( available_const_iterator it = availableBegin();
+              it != availableEnd(); ++it )
+          {
+            if ( ! (*it).status().isLocked() )
+              return false;
+          }
+        return( ! _availableItems.empty() );
+      }
+
+      bool allInstalledLocked() const
+      {
+        for ( installed_const_iterator it = installedBegin();
+              it != installedEnd(); ++it )
+          {
+            if ( ! (*it).status().isLocked() )
+              return false;
+          }
+        return( ! _installedItems.empty() );
+      }
+
+
+    private:
+      const IdString         _ident;
+      const ResObject::Kind  _kind;
+      const std::string      _name;
+      InstalledItemSet       _installedItems;
+      AvailableItemSet       _availableItems;
+      //! Best among availabe with restpect to installed.
+      PoolItem               _defaultCandidate;
+      //! The object selected by setCandidateObj() method.
+      PoolItem               _candidate;
+      //! lazy initialized picklist
+      mutable scoped_ptr<PickList> _picklistPtr;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Selectable::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Selectable::Impl & obj )
+    {
+      return str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
+                 << " (I " << obj.installedSize() << ")"
+                 << " (A " << obj.availableSize() << ")"
+                 << obj.candidateObj();
+    }
+
+    /** \relates Selectable::Impl Stream output */
+    inline std::ostream & dumpOn( std::ostream & str, const Selectable::Impl & obj )
+    {
+      str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
+          << ( obj.multiversionInstall() ? " (multiversion)" : "") << endl;
+
+      if ( obj.installedEmpty() )
+        str << "   (I 0) {}" << endl << "   ";
+      else
+      {
+        PoolItem icand( obj.installedObj() );
+        str << "   (I " << obj.installedSize() << ") {" << endl;
+        for_( it, obj.installedBegin(), obj.installedEnd() )
+        {
+          char t = ' ';
+          if ( *it == icand )
+          {
+            t = 'i';
+          }
+          str << " " << t << " " << *it << endl;
+        }
+        str << "}  ";
+      }
+
+      if ( obj.availableEmpty() )
+      {
+        str << "(A 0) {}" << endl << "   ";
+      }
+      else
+      {
+        PoolItem cand( obj.candidateObj() );
+        PoolItem up( obj.updateCandidateObj() );
+        str << "(A " << obj.availableSize() << ") {" << endl;
+        for_( it, obj.availableBegin(), obj.availableEnd() )
+        {
+          char t = ' ';
+          if ( *it == cand )
+          {
+            t = *it == up ? 'C' : 'c';
+          }
+          else if ( *it == up )
+          {
+            t = 'u';
+          }
+          str << " " << t << " " << *it << endl;
+        }
+        str << "}  ";
+      }
+
+      if ( obj.picklistEmpty() )
+      {
+        str << "(P 0) {}";
+      }
+      else
+      {
+        PoolItem cand( obj.candidateObj() );
+        PoolItem up( obj.updateCandidateObj() );
+        str << "(P " << obj.picklistSize() << ") {" << endl;
+        for_( it, obj.picklistBegin(), obj.picklistEnd() )
+        {
+          char t = ' ';
+          if ( *it == cand )
+          {
+            t = *it == up ? 'C' : 'c';
+          }
+          else if ( *it == up )
+          {
+            t = 'u';
+          }
+          str << " " << t << " " << *it << "\t" << obj.pickStatus( *it ) << endl;
+        }
+        str << "}  ";
+      }
+
+      return str;
+    }
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_UI_SELECTABLEIMPL_H
diff --git a/zypp/ui/SelectableTraits.h b/zypp/ui/SelectableTraits.h
new file mode 100644 (file)
index 0000000..9ad8102
--- /dev/null
@@ -0,0 +1,130 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/SelectableTraits.h
+ *
+*/
+#ifndef ZYPP_UI_SELECTABLETRAITS_H
+#define ZYPP_UI_SELECTABLETRAITS_H
+
+#include <set>
+#include <vector>
+
+#include "zypp/base/Iterator.h"
+#include "zypp/PoolItem.h"
+#include "zypp/pool/ByIdent.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : SelectableTraits
+    //
+    /** */
+    struct SelectableTraits
+    {
+      /** Oder on AvailableItemSet.
+       * \li repository priority
+       * \li best Arch (arch/noarch changes are ok)
+       * \li best Edition
+       * \li ResObject::constPtr as fallback.
+      */
+      struct AVOrder : public std::binary_function<PoolItem,PoolItem,bool>
+      {
+        // NOTE: operator() provides LESS semantics to order the set.
+        // So LESS means 'prior in set'. We want 'better' archs and
+        // 'better' editions at the beginning of the set. So we return
+        // TRUE if (lhs > rhs)!
+        //
+        bool operator()( const PoolItem & lhs, const PoolItem & rhs ) const
+        {
+          int lprio = lhs->satSolvable().repository().satInternalPriority();
+          int rprio = rhs->satSolvable().repository().satInternalPriority();
+          if ( lprio != rprio )
+            return( lprio > rprio );
+
+          // arch/noarch changes are ok.
+          if ( lhs->arch() != Arch_noarch && rhs->arch() != Arch_noarch )
+          {
+            int res = lhs->arch().compare( rhs->arch() );
+            if ( res )
+              return res > 0;
+          }
+
+          int res = lhs->edition().compare( rhs->edition() );
+          if ( res )
+            return res > 0;
+
+          lprio = lhs->satSolvable().repository().satInternalSubPriority();
+          rprio = rhs->satSolvable().repository().satInternalSubPriority();
+          if ( lprio != rprio )
+            return( lprio > rprio );
+
+          // no more criteria, still equal: sort by id
+          return lhs.satSolvable().id() < rhs.satSolvable().id();
+        }
+      };
+
+      /** Oder on InstalledItemSet.
+       * \li best Arch
+       * \li best Edition
+       * \li newer install time
+       * \li ResObject::constPtr as fallback.
+      */
+      struct IOrder : public std::binary_function<PoolItem,PoolItem,bool>
+      {
+        // NOTE: operator() provides LESS semantics to order the set.
+        // So LESS means 'prior in set'. We want 'newer' install time
+        // at the beginning of the set.
+        //
+        bool operator()( const PoolItem & lhs, const PoolItem & rhs ) const
+        {
+          int res = lhs->arch().compare( rhs->arch() );
+          if ( res )
+            return res > 0;
+          res = lhs->edition().compare( rhs->edition() );
+          if ( res )
+            return res > 0;
+          Date ldate = lhs->installtime();
+          Date rdate = rhs->installtime();
+          if ( ldate != rdate )
+            return( ldate > rdate );
+
+          // no more criteria, still equal: sort by id
+          return lhs.satSolvable().id() < rhs.satSolvable().id();
+        }
+      };
+
+      typedef std::set<PoolItem,AVOrder>       AvailableItemSet;
+      typedef AvailableItemSet::iterator       available_iterator;
+      typedef AvailableItemSet::const_iterator available_const_iterator;
+      typedef AvailableItemSet::size_type      available_size_type;
+
+      typedef std::set<PoolItem,IOrder>        InstalledItemSet;
+      typedef AvailableItemSet::iterator       installed_iterator;
+      typedef AvailableItemSet::const_iterator installed_const_iterator;
+      typedef AvailableItemSet::size_type      installed_size_type;
+
+      typedef std::vector<PoolItem>             PickList;
+      typedef PickList::const_iterator          picklist_iterator;
+      typedef PickList::size_type               picklist_size_type;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_UI_SELECTABLETRAITS_H
diff --git a/zypp/ui/Status.cc b/zypp/ui/Status.cc
new file mode 100644 (file)
index 0000000..cff70a2
--- /dev/null
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/Status.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/ui/Status.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : asString
+    ** FUNCTION TYPE : std::string
+    */
+    std::string asString( const Status & obj )
+    {
+      switch ( obj ) {
+#define ENUM_OUT(V) case V: return #V; break
+
+        ENUM_OUT( S_Protected );
+        ENUM_OUT( S_Taboo );
+        ENUM_OUT( S_Del );
+        ENUM_OUT( S_Install );
+        ENUM_OUT( S_Update );
+        ENUM_OUT( S_AutoDel );
+        ENUM_OUT( S_AutoInstall );
+        ENUM_OUT( S_AutoUpdate );
+        ENUM_OUT( S_NoInst );
+        ENUM_OUT( S_KeepInstalled );
+
+#undef ENUM_OUT
+      }
+
+      INT << "Unknown ui::Status " << (unsigned)obj << std::endl;
+      return "Status(UNKNOWN)";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ui/Status.h b/zypp/ui/Status.h
new file mode 100644 (file)
index 0000000..9d5d061
--- /dev/null
@@ -0,0 +1,68 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/ui/Status.h
+ *
+*/
+#ifndef ZYPP_UI_STATUS_H
+#define ZYPP_UI_STATUS_H
+
+#include <iosfwd>
+#include <string>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace ui
+  { /////////////////////////////////////////////////////////////////
+
+    /** UI status
+     * Status values calculated by \ref Selectable.
+
+     * \note The \ref Status enum, and it's use within \ref Selectabe
+     * is tightly related to the Y2UI. It might be not verry usefull
+     * outside the Y2UI.
+     *
+     * \todo make it an EnumerationClass
+    */
+    enum Status
+    {
+      S_Protected,           // Keep this unmodified ( have installedObj && S_Protected )
+      S_Taboo,               // Keep this unmodified ( have no installedObj && S_Taboo)
+      // requested by user:
+      S_Del,                 // delete  installedObj ( clears S_Protected if set )
+      S_Update,              // install candidateObj ( have installedObj, clears S_Protected if set )
+      S_Install,             // install candidateObj ( have no installedObj, clears S_Taboo if set )
+      // not requested by user:
+      S_AutoDel,             // delete  installedObj
+      S_AutoUpdate,          // install candidateObj ( have installedObj )
+      S_AutoInstall,         // install candidateObj ( have no installedObj )
+      // no modification:
+      S_KeepInstalled,       // no modification      ( have installedObj && !S_Protected, clears S_Protected if set )
+      S_NoInst,              // no modification      ( have no installedObj && !S_Taboo, clears S_Taboo if set )
+    };
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Status Enum value as string. */
+    std::string asString( const Status & obj );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates Status Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Status & obj )
+    { return str << asString( obj ); }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace ui
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_UI_STATUS_H
diff --git a/zypp/ui/UserWantedPackages.cc b/zypp/ui/UserWantedPackages.cc
new file mode 100644 (file)
index 0000000..7e92856
--- /dev/null
@@ -0,0 +1,161 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file       zypp/ui/UserWantedPackages.cc
+ *
+ *  \author    Stefan Hundhammer <sh@suse.de>
+ *
+ */
+#include <iostream>
+#include "zypp/base/Logger.h"
+
+#include "zypp/ui/UserWantedPackages.h"
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/ui/Selectable.h"
+
+#include "zypp/ResObjects.h"
+#include "zypp/ZYppFactory.h"
+#include "zypp/ResPoolProxy.h"
+
+
+using std::string;
+using std::set;
+using std::endl;
+
+
+namespace zypp
+{
+    namespace ui
+    {
+       typedef ResPoolProxy::const_iterator    PoolProxyIterator;
+
+        static inline ResPoolProxy             poolProxy()     { return getZYpp()->poolProxy();        }
+
+       template<class T> PoolProxyIterator poolProxyBegin()    { return poolProxy().byKindBegin<T>();  }
+       template<class T> PoolProxyIterator poolProxyEnd()      { return poolProxy().byKindEnd<T>();    }
+
+       static inline PoolProxyIterator pkgBegin()              { return poolProxyBegin<Package>();     }
+       static inline PoolProxyIterator pkgEnd()                { return poolProxyEnd<Package>();       }
+
+//     static inline PoolProxyIterator langBegin()             { return poolProxyBegin<Language>();    }
+//     static inline PoolProxyIterator langEnd()               { return poolProxyEnd<Language>();      }
+
+       static inline PoolProxyIterator patchesBegin()          { return poolProxyBegin<Patch>();       }
+       static inline PoolProxyIterator patchesEnd()            { return poolProxyEnd<Patch>();         }
+
+       template<typename T> bool contains( const std::set<T> & container, T search )
+       {
+           return container.find( search ) != container.end();
+       }
+
+
+
+       static void addDirectlySelectedPackages ( set<string> & pkgNames );
+        template<class PkgSet_T> void addPkgSetPackages( set<string> & pkgNames );
+
+       static void addPatternPackages          ( set<string> & pkgNames );
+       static void addPatchPackages            ( set<string> & pkgNames );
+
+
+
+       set<string> userWantedPackageNames()
+       {
+           set<string> pkgNames;
+
+           DBG << "Collecting packages the user explicitly asked for" << endl;
+
+           addDirectlySelectedPackages ( pkgNames );
+           addPatternPackages          ( pkgNames );
+           addPatchPackages            ( pkgNames );
+
+           return pkgNames;
+       }
+
+
+
+       static void addDirectlySelectedPackages( set<string> & pkgNames )
+       {
+           for ( PoolProxyIterator it = pkgBegin();
+                 it != pkgEnd();
+                 ++it )
+           {
+               // Add all packages the user wanted to transact directly,
+               // no matter what the transaction is (install, update, delete)
+
+               if ( (*it)->toModify() && (*it)->modifiedBy() == ResStatus::USER )
+               {
+                   DBG << "Explicit user transaction on pkg \"" << (*it)->name() << "\"" << endl;
+
+                   pkgNames.insert( (*it)->name() );
+               }
+           }
+       }
+
+
+
+       static void addPatternPackages( set<string> & pkgNames )
+       {
+           addPkgSetPackages<Pattern>( pkgNames );
+       }
+
+
+       /**
+        * Template to handle Patterns
+        **/
+        template<class PkgSet_T> void addPkgSetPackages( set<string> & pkgNames )
+       {
+           for ( PoolProxyIterator it = poolProxyBegin<PkgSet_T>();
+                 it != poolProxyEnd<PkgSet_T>();
+                 ++it )
+           {
+               // Take all pkg sets (patterns) into account that
+               // will be transacted, no matter if the user explicitly asked
+               // for that pkg set or if the patterns is required by another
+               // pkg set of the same class
+
+          typename PkgSet_T::constPtr pkgSet = dynamic_pointer_cast<const PkgSet_T>( (*it)->theObj() ? (*it)->theObj().resolvable() : 0L );
+
+               if ( pkgSet && (*it)->toModify() )
+               {
+                   DBG << (*it)->theObj()->kind().asString()
+                       << " will be transacted: \"" << pkgSet->name() << "\"" << endl;
+
+#warning NEEDS FIX
+                   set<string> setPkgs;// = pkgSet->install_packages();
+                   pkgNames.insert( setPkgs.begin(), setPkgs.end() );
+               }
+           }
+       }
+
+
+        static void addPatchPackages( set<string> & pkgNames )
+        {
+            for ( PoolProxyIterator patch_it = patchesBegin();
+                  patch_it != patchesEnd();
+                  ++patch_it )
+            {
+                Patch::constPtr patch = dynamic_pointer_cast<const Patch>( (*patch_it)->theObj() ? (*patch_it)->theObj().resolvable() : 0 );
+
+                if ( patch && (*patch_it)->toModify() )
+               {
+                   DBG << "Patch will be transacted: \"" << patch->name()
+                       << "\" - \"" << patch->summary() << "\"" << endl;
+
+                    Patch::Contents contents( patch->contents() );
+                    for_( it, contents.begin(), contents.end() )
+                    {
+                      pkgNames.insert( it->name() );
+                    }
+               }
+           }
+       }
+
+    } // namespace ui
+} // namespace zypp
diff --git a/zypp/ui/UserWantedPackages.h b/zypp/ui/UserWantedPackages.h
new file mode 100644 (file)
index 0000000..6f8709a
--- /dev/null
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file       zypp/ui/UserWantedPackages.h
+ *
+ *  \author    Stefan Hundhammer <sh@suse.de>
+ *
+ */
+#ifndef USER_WANTED_PACKAGES_H
+#define USER_WANTED_PACKAGES_H
+
+#include <set>
+#include <string>
+
+namespace zypp
+{
+    namespace ui
+    {
+       /**
+        * This returns a set of package names the user explicitly wanted to
+        * transact ( to install, to update, or to delete) for any of the
+        * following reasons:
+        *
+        * - The user wanted to transact the pkg directly
+        *
+        * - Pkg is part of a pattern   the user wanted to transact
+        * - Pkg is part of a language  the user wanted to transact
+         *   (? No more transacting Languages)
+        * - Pkg is part of a patch     the user wanted to transact
+        *
+        * - Pkg is part of a pattern that is required by a pattern the
+        *   user wanted to transact
+        **/
+       std::set<std::string> userWantedPackageNames();
+
+    } // namespace ui
+} // namespace zypp
+
+#endif // USER_WANTED_PACKAGES_H
diff --git a/zypp/url/UrlBase.cc b/zypp/url/UrlBase.cc
new file mode 100644 (file)
index 0000000..296f0bc
--- /dev/null
@@ -0,0 +1,1376 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/url/UrlBase.cc
+ */
+#include <zypp/url/UrlBase.h>
+#include <zypp/base/String.h>
+#include <zypp/base/Gettext.h>
+#include <zypp/base/Regex.h>
+
+#include <stdexcept>
+#include <climits>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <iostream>
+
+// in the Estonian locale, a-z excludes t, for example. #302525
+// http://en.wikipedia.org/wiki/Estonian_alphabet
+#define a_zA_Z "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+// ---------------------------------------------------------------
+/*
+** authority = //[user [:password] @ ] host [:port]
+**
+** host      = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
+*/
+#define RX_VALID_SCHEME    "^[" a_zA_Z "][" a_zA_Z "0-9\\.+-]*$"
+
+#define RX_VALID_PORT      "^[0-9]{1,5}$"
+
+#define RX_VALID_HOSTNAME  "^[[:alnum:]]+([\\.-][[:alnum:]]+)*$"
+
+#define RX_VALID_HOSTIPV4  \
+        "^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"
+
+#define RX_VALID_HOSTIPV6  \
+        "^\\[[:a-fA-F0-9]+(:[0-9]{1,3}(\\.[0-9]{1,3}){3})?\\]$"
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace url
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    /*
+    ** URL asString() view option constants:
+    */
+    const ViewOption  ViewOption::WITH_SCHEME       = 0x0001;
+    const ViewOption  ViewOption::WITH_USERNAME     = 0x0002;
+    const ViewOption  ViewOption::WITH_PASSWORD     = 0x0004;
+    const ViewOption  ViewOption::WITH_HOST         = 0x0008;
+    const ViewOption  ViewOption::WITH_PORT         = 0x0010;
+    const ViewOption  ViewOption::WITH_PATH_NAME    = 0x0020;
+    const ViewOption  ViewOption::WITH_PATH_PARAMS  = 0x0040;
+    const ViewOption  ViewOption::WITH_QUERY_STR    = 0x0080;
+    const ViewOption  ViewOption::WITH_FRAGMENT     = 0x0100;
+    const ViewOption  ViewOption::EMPTY_AUTHORITY   = 0x0200;
+    const ViewOption  ViewOption::EMPTY_PATH_NAME   = 0x0400;
+    const ViewOption  ViewOption::EMPTY_PATH_PARAMS = 0x0800;
+    const ViewOption  ViewOption::EMPTY_QUERY_STR   = 0x1000;
+    const ViewOption  ViewOption::EMPTY_FRAGMENT    = 0x2000;
+    const ViewOption  ViewOption::DEFAULTS          = 0x07bb;
+    /*
+    const ViewOption  ViewOption::DEFAULTS          =
+                      ViewOption::WITH_SCHEME       +
+                      ViewOption::WITH_USERNAME     +
+                      ViewOption::WITH_HOST         +
+                      ViewOption::WITH_PORT         +
+                      ViewOption::WITH_PATH_NAME    +
+                      ViewOption::WITH_QUERY_STR    +
+                      ViewOption::WITH_FRAGMENT     +
+                      ViewOption::EMPTY_AUTHORITY   +
+                      ViewOption::EMPTY_PATH_NAME;
+    */
+
+    // ---------------------------------------------------------------
+    ViewOption::ViewOption()
+      : opt(0x07bb)
+    {}
+
+    // ---------------------------------------------------------------
+    ViewOption::ViewOption(int option)
+      : opt(option)
+    {}
+
+
+    // ---------------------------------------------------------------
+    /*
+    ** Behaviour configuration variables.
+    */
+    typedef std::map< std::string, std::string > UrlConfig;
+
+
+    // ---------------------------------------------------------------
+    /**
+     * \brief Internal data used by UrlBase.
+     */
+    class UrlBaseData
+    {
+    public:
+      UrlBaseData()
+      {}
+
+      UrlBaseData(const UrlConfig &conf)
+        : config(conf)
+      {}
+
+      UrlConfig       config;
+      ViewOptions     vopts;
+
+      std::string     scheme;
+      std::string     user;
+      std::string     pass;
+      std::string     host;
+      std::string     port;
+      std::string     pathname;
+      std::string     pathparams;
+      std::string     querystr;
+      std::string     fragment;
+    };
+
+
+    // ---------------------------------------------------------------
+    /*
+    ** Anonymous/internal utility namespace:
+    */
+    namespace // anonymous
+    {
+
+                       // -------------------------------------------------------------
+      inline void
+      checkUrlData(const std::string &data,
+                   const std::string &name,
+                   const std::string &regx,
+                   bool               show=true)
+      {
+        if( regx.empty() || regx == "^$")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            str::form(_("Url scheme does not allow a %s"), name.c_str())
+          ));
+        }
+        else
+        {
+          bool valid = false;
+          try
+          {
+            str::regex rex(regx);
+            valid = str::regex_match(data, rex);
+          }
+          catch( ... )
+          {}
+
+          if( !valid)
+          {
+            if( show)
+            {
+              ZYPP_THROW(UrlBadComponentException(
+                str::form(_("Invalid %s component '%s'"),
+                          name.c_str(), data.c_str())
+              ));
+            }
+            else
+            {
+              ZYPP_THROW(UrlBadComponentException(
+                str::form(_("Invalid %s component"), name.c_str())
+              ));
+            }
+          }
+        }
+      }
+
+    } // namespace
+
+
+    // ---------------------------------------------------------------
+    UrlBase::~UrlBase()
+    {
+      delete m_data;
+      m_data = NULL;
+    }
+
+
+    // ---------------------------------------------------------------
+    UrlBase::UrlBase()
+      : m_data( new UrlBaseData())
+    {
+      configure();
+    }
+
+
+    // ---------------------------------------------------------------
+    UrlBase::UrlBase(const UrlBase &url)
+      : m_data( new UrlBaseData( *(url.m_data)))
+    {
+    }
+
+
+    // ---------------------------------------------------------------
+    UrlBase::UrlBase(const std::string &scheme,
+                     const std::string &authority,
+                     const std::string &pathdata,
+                     const std::string &querystr,
+                     const std::string &fragment)
+      : m_data( new UrlBaseData())
+    {
+      configure();
+      init(scheme, authority, pathdata, querystr, fragment);
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::init(const std::string &scheme,
+                  const std::string &authority,
+                  const std::string &pathdata,
+                  const std::string &querystr,
+                  const std::string &fragment)
+    {
+      setScheme(scheme);
+      setAuthority(authority);
+      setPathData(pathdata);
+      setQueryString(querystr);
+      setFragment(fragment, zypp::url::E_ENCODED);
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::configure()
+    {
+      config("sep_pathparams",  ";");
+      config("psep_pathparam",  ",");
+      config("vsep_pathparam",  "=");
+
+      config("psep_querystr",   "&");
+      config("vsep_querystr",   "=");
+
+      config("safe_username",   "~!$&'()*+=,;");
+      config("safe_password",   "~!$&'()*+=,:;");
+      config("safe_hostname",   "[:]");
+      config("safe_pathname",   "~!$&'()*+=,:@/");
+      config("safe_pathparams", "~!$&'()*+=,:;@/");
+      config("safe_querystr",   "~!$&'()*+=,:;@/?");
+      config("safe_fragment",   "~!$&'()*+=,:;@/?");
+
+      // y=yes (allowed)
+      // n=no  (disallowed, exception if !empty)
+      config("with_authority",  "y");
+      config("with_port",       "y");
+
+      // y=yes (required but don't throw if empty)
+      // n=no  (not required, ignore if empty)
+      // m=mandatory (exception if empty)
+      config("require_host",    "n");
+      config("require_pathname","n");
+
+      // y=yes (encode 2. slash even if authority present)
+      // n=no  (don't encode 2. slash if authority present)
+      config("path_encode_slash2", "n");
+
+      config("rx_username",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,;~\\._-]|%[a-fA-F0-9]{2})+$");
+      config("rx_password",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;~\\._-]|%[a-fA-F0-9]{2})+$");
+
+      config("rx_pathname",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:@/~\\._-]|%[a-fA-F0-9]{2})+$");
+      config("rx_pathparams",   "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/~\\._-]|%[a-fA-F0-9]{2})+$");
+
+      config("rx_querystr",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
+      config("rx_fragment",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::config(const std::string &opt, const std::string &val)
+    {
+      m_data->config[opt] = val;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::config(const std::string &opt) const
+    {
+      UrlConfig::const_iterator v( m_data->config.find(opt));
+      if( v != m_data->config.end())
+        return v->second;
+      else
+        return std::string();
+    }
+
+
+    // ---------------------------------------------------------------
+    ViewOptions
+    UrlBase::getViewOptions() const
+    {
+      return m_data->vopts;
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setViewOptions(const ViewOptions &vopts)
+    {
+        m_data->vopts = vopts;
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::clear()
+    {
+      zypp::url::UrlConfig   config(m_data->config);
+      zypp::url::ViewOptions vopts(m_data->vopts);
+      *m_data = UrlBaseData();
+      m_data->config = config;
+      m_data->vopts  = vopts;
+    }
+
+
+    // ---------------------------------------------------------------
+    UrlBase *
+    UrlBase::clone() const
+    {
+      return new UrlBase(*this);
+    }
+
+
+    // ---------------------------------------------------------------
+    zypp::url::UrlSchemes
+    UrlBase::getKnownSchemes() const
+    {
+      return UrlSchemes();
+    }
+
+
+    // ---------------------------------------------------------------
+    bool
+    UrlBase::isKnownScheme(const std::string &scheme) const
+    {
+      std::string                lscheme( str::toLower(scheme));
+      UrlSchemes                 schemes( getKnownSchemes());
+      UrlSchemes::const_iterator s;
+
+      for(s=schemes.begin(); s!=schemes.end(); ++s)
+      {
+        if( lscheme == str::toLower(*s))
+          return true;
+      }
+      return false;
+    }
+
+
+    // ---------------------------------------------------------------
+    bool
+    UrlBase::isValidScheme(const std::string &scheme) const
+    {
+      bool valid = false;
+      try
+      {
+        str::regex rex(RX_VALID_SCHEME);
+        valid = str::regex_match(scheme, rex);
+      }
+      catch( ... )
+      {}
+
+      if(valid)
+      {
+        std::string    lscheme( str::toLower(scheme));
+        UrlSchemes     schemes( getKnownSchemes());
+
+        if( schemes.empty())
+          return true;
+
+        UrlSchemes::const_iterator s;
+        for(s=schemes.begin(); s!=schemes.end(); ++s)
+        {
+          if( lscheme == str::toLower(*s))
+            return true;
+        }
+      }
+      return false;
+    }
+
+
+    // ---------------------------------------------------------------
+    bool
+    UrlBase::isValid() const
+    {
+      /*
+      ** scheme is the only mandatory component
+      ** for all url's and is already verified,
+      ** (except for empty Url instances), so
+      ** Url with empty scheme is never valid.
+      */
+      if( getScheme().empty())
+        return false;
+
+      std::string host( getHost(zypp::url::E_ENCODED));
+      if( host.empty() && config("require_host")     != "n")
+        return false;
+
+      std::string path( getPathName(zypp::url::E_ENCODED));
+      if( path.empty() && config("require_pathname") != "n")
+        return false;
+
+      /*
+      ** path has to begin with "/" if authority avaliable
+      ** if host is set after the pathname, we can't throw
+      */
+      if( !host.empty() && !path.empty() && path.at(0) != '/')
+        return false;
+
+      return true;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::asString() const
+    {
+      return asString(getViewOptions());
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::asString(const zypp::url::ViewOptions &opts) const
+    {
+      std::string   url;
+      UrlBaseData   tmp;
+
+      if( opts.has(ViewOptions::WITH_SCHEME))
+      {
+        tmp.scheme = getScheme();
+        if( !tmp.scheme.empty())
+        {
+          url += tmp.scheme + ":";
+
+          if( opts.has(ViewOptions::WITH_HOST))
+          {
+            tmp.host = getHost(zypp::url::E_ENCODED);
+            if( !tmp.host.empty())
+            {
+              url += "//";
+
+              if( opts.has(ViewOptions::WITH_USERNAME))
+              {
+                tmp.user = getUsername(zypp::url::E_ENCODED);
+                if( !tmp.user.empty())
+                {
+                  url += tmp.user;
+
+                  if( opts.has(ViewOptions::WITH_PASSWORD))
+                  {
+                    tmp.pass = getPassword(zypp::url::E_ENCODED);
+                    if( !tmp.pass.empty())
+                    {
+                      url += ":" + tmp.pass;
+                    }
+                  }
+                  url += "@";
+                }
+              }
+
+              url += tmp.host;
+
+              if( opts.has(ViewOptions::WITH_PORT))
+              {
+                tmp.port = getPort();
+                if( !tmp.port.empty())
+                {
+                  url += ":" + tmp.port;
+                }
+              }
+            }
+            else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
+            {
+              url += "//";
+            }
+          }
+          else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
+          {
+            url += "//";
+          }
+        }
+      }
+
+      if( opts.has(ViewOptions::WITH_PATH_NAME))
+      {
+        tmp.pathname = getPathName(zypp::url::E_ENCODED);
+        if( !tmp.pathname.empty())
+        {
+          if(url.find("/") != std::string::npos)
+          {
+            // Url contains authority (that may be empty),
+            // we may need a rewrite of the encoded path.
+            tmp.pathname = cleanupPathName(tmp.pathname, true);
+            if(tmp.pathname.at(0) != '/')
+            {
+              url += "/";
+            }
+          }
+          url += tmp.pathname;
+
+          if( opts.has(ViewOptions::WITH_PATH_PARAMS))
+          {
+            tmp.pathparams = getPathParams();
+            if( !tmp.pathparams.empty())
+            {
+              url += ";" + tmp.pathparams;
+            }
+            else if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
+            {
+              url += ";";
+            }
+          }
+        }
+        else if( opts.has(ViewOptions::EMPTY_PATH_NAME)
+                 && url.find("/") != std::string::npos)
+        {
+          url += "/";
+          if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
+          {
+            url += ";";
+          }
+        }
+      }
+
+      if( opts.has(ViewOptions::WITH_QUERY_STR))
+      {
+        tmp.querystr = getQueryString();
+        if( !tmp.querystr.empty())
+        {
+          url += "?" + tmp.querystr;
+        }
+        else if( opts.has(ViewOptions::EMPTY_QUERY_STR))
+        {
+          url += "?";
+        }
+      }
+
+      if( opts.has(ViewOptions::WITH_FRAGMENT))
+      {
+        tmp.fragment = getFragment(zypp::url::E_ENCODED);
+        if( !tmp.fragment.empty())
+        {
+          url += "#" + tmp.fragment;
+        }
+        else if( opts.has(ViewOptions::EMPTY_FRAGMENT))
+        {
+          url += "#";
+        }
+      }
+
+      return url;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getScheme() const
+    {
+      return m_data->scheme;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getAuthority() const
+    {
+      std::string str;
+      if( !getHost(zypp::url::E_ENCODED).empty())
+      {
+        if( !getUsername(zypp::url::E_ENCODED).empty())
+        {
+          str = getUsername(zypp::url::E_ENCODED);
+          if( !getPassword(zypp::url::E_ENCODED).empty())
+          {
+            str += ":" + getPassword(zypp::url::E_ENCODED);
+          }
+          str += "@";
+        }
+
+        str += getHost(zypp::url::E_ENCODED);
+        if( !getPort().empty())
+        {
+          str += ":" + getPort();
+        }
+      }
+      return str;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPathData() const
+    {
+      return getPathName(zypp::url::E_ENCODED) +
+             config("sep_pathparams") +
+             getPathParams();
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getQueryString() const
+    {
+      return m_data->querystr;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getFragment(EEncoding eflag) const
+    {
+      if(eflag == zypp::url::E_DECODED)
+        return zypp::url::decode(m_data->fragment);
+      else
+        return m_data->fragment;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getUsername(EEncoding eflag) const
+    {
+      if(eflag == zypp::url::E_DECODED)
+        return zypp::url::decode(m_data->user);
+      else
+        return m_data->user;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPassword(EEncoding eflag) const
+    {
+      if(eflag == zypp::url::E_DECODED)
+        return zypp::url::decode(m_data->pass);
+      else
+        return m_data->pass;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getHost(EEncoding eflag) const
+    {
+      if(eflag == zypp::url::E_DECODED)
+        return zypp::url::decode(m_data->host);
+      else
+        return m_data->host;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPort() const
+    {
+      return m_data->port;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPathName(EEncoding eflag) const
+    {
+      if(eflag == zypp::url::E_DECODED)
+        return zypp::url::decode(m_data->pathname);
+      else
+        return cleanupPathName(m_data->pathname);
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPathParams() const
+    {
+      return m_data->pathparams;
+    }
+
+
+    // ---------------------------------------------------------------
+    zypp::url::ParamVec
+    UrlBase::getPathParamsVec() const
+    {
+      zypp::url::ParamVec pvec;
+      if( config("psep_pathparam").empty())
+      {
+        pvec.push_back(getPathParams());
+      }
+      else
+      {
+        zypp::url::split(
+          pvec,
+          getPathParams(),
+          config("psep_pathparam")
+        );
+      }
+      return pvec;
+    }
+
+
+    // ---------------------------------------------------------------
+    zypp::url::ParamMap
+    UrlBase::getPathParamsMap(EEncoding eflag) const
+    {
+      if( config("psep_pathparam").empty() ||
+          config("vsep_pathparam").empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          "Path parameter parsing not supported for this URL"
+        ));
+      }
+      zypp::url::ParamMap pmap;
+      zypp::url::split(
+        pmap,
+        getPathParams(),
+        config("psep_pathparam"),
+        config("vsep_pathparam"),
+        eflag
+      );
+      return pmap;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getPathParam(const std::string &param, EEncoding eflag) const
+    {
+      zypp::url::ParamMap pmap( getPathParamsMap( eflag));
+      zypp::url::ParamMap::const_iterator i( pmap.find(param));
+
+      return i != pmap.end() ? i->second : std::string();
+    }
+
+
+    // ---------------------------------------------------------------
+    zypp::url::ParamVec
+    UrlBase::getQueryStringVec() const
+    {
+      zypp::url::ParamVec pvec;
+      if( config("psep_querystr").empty())
+      {
+        pvec.push_back(getQueryString());
+      }
+      else
+      {
+        zypp::url::split(
+          pvec,
+          getQueryString(),
+          config("psep_querystr")
+        );
+      }
+      return pvec;
+    }
+
+
+    // ---------------------------------------------------------------
+    zypp::url::ParamMap
+    UrlBase::getQueryStringMap(EEncoding eflag) const
+    {
+      if( config("psep_querystr").empty() ||
+          config("vsep_querystr").empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          _("Query string parsing not supported for this URL")
+        ));
+      }
+      zypp::url::ParamMap pmap;
+      zypp::url::split(
+        pmap,
+        getQueryString(),
+        config("psep_querystr"),
+        config("vsep_querystr"),
+        eflag
+      );
+      return pmap;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::getQueryParam(const std::string &param, EEncoding eflag) const
+    {
+      zypp::url::ParamMap pmap( getQueryStringMap( eflag));
+      zypp::url::ParamMap::const_iterator i( pmap.find(param));
+
+      return i != pmap.end() ? i->second : std::string();
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setScheme(const std::string &scheme)
+    {
+      if( isValidScheme(scheme))
+      {
+        m_data->scheme = str::toLower(scheme);
+      }
+      else
+      if( scheme.empty())
+      {
+        ZYPP_THROW(UrlBadComponentException(
+          _("Url scheme is a required component")
+        ));
+      }
+      else
+      {
+        ZYPP_THROW(UrlBadComponentException(
+          str::form(_("Invalid Url scheme '%s'"), scheme.c_str())
+        ));
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setAuthority(const std::string &authority)
+    {
+      std::string s = authority;
+      std::string::size_type p,q;
+
+      std::string username, password, host, port;
+
+      if ((p=s.find('@')) != std::string::npos)
+      {
+        q = s.find(':');
+        if (q != std::string::npos && q < p)
+        {
+          setUsername(s.substr(0, q), zypp::url::E_ENCODED);
+          setPassword(s.substr(q+1, p-q-1), zypp::url::E_ENCODED);
+        }
+        else
+          setUsername(s.substr(0, p), zypp::url::E_ENCODED);
+        s = s.substr(p+1);
+      }
+      if ((p = s.rfind(':')) != std::string::npos && ( (q = s.rfind(']')) == std::string::npos || q < p) )
+      {
+        setHost(s.substr(0, p));
+        setPort(s.substr(p+1));
+      }
+      else
+        setHost(s);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathData(const std::string &pathdata)
+    {
+      size_t      pos = std::string::npos;
+      std::string sep(config("sep_pathparams"));
+
+      if( !sep.empty())
+        pos = pathdata.find(sep);
+
+      if( pos != std::string::npos)
+      {
+        setPathName(pathdata.substr(0, pos),
+                    zypp::url::E_ENCODED);
+        setPathParams(pathdata.substr(pos + 1));
+      }
+      else
+      {
+        setPathName(pathdata,
+                    zypp::url::E_ENCODED);
+        setPathParams("");
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setQueryString(const std::string &querystr)
+    {
+      if( querystr.empty())
+      {
+        m_data->querystr = querystr;
+      }
+      else
+      {
+        checkUrlData(querystr, "query string", config("rx_querystr"));
+
+        m_data->querystr = querystr;
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setFragment(const std::string &fragment,
+                         EEncoding         eflag)
+    {
+      if( fragment.empty())
+      {
+        m_data->fragment = fragment;
+      }
+      else
+      {
+        if(eflag == zypp::url::E_ENCODED)
+        {
+          checkUrlData(fragment, "fragment", config("rx_fragment"));
+
+          m_data->fragment = fragment;
+        }
+        else
+        {
+          m_data->fragment = zypp::url::encode(
+            fragment, config("safe_fragment")
+          );
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setUsername(const std::string &user,
+                         EEncoding         eflag)
+    {
+      if( user.empty())
+      {
+        m_data->user = user;
+      }
+      else
+      {
+        if( config("with_authority") != "y")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme does not allow a username")
+          ));
+        }
+
+        if(eflag == zypp::url::E_ENCODED)
+        {
+          checkUrlData(user, "username", config("rx_username"));
+
+          m_data->user = user;
+        }
+        else
+        {
+          m_data->user = zypp::url::encode(
+            user, config("safe_username")
+          );
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPassword(const std::string &pass,
+                         EEncoding         eflag)
+    {
+      if( pass.empty())
+      {
+        m_data->pass = pass;
+      }
+      else
+      {
+        if( config("with_authority") != "y")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme does not allow a password")
+          ));
+        }
+
+        if(eflag == zypp::url::E_ENCODED)
+        {
+          checkUrlData(pass, "password", config("rx_password"), false);
+
+          m_data->pass = pass;
+        }
+        else
+        {
+          m_data->pass = zypp::url::encode(
+            pass, config("safe_password")
+          );
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setHost(const std::string &host)
+    {
+      if( host.empty())
+      {
+        if(config("require_host") == "m")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme requires a host component")
+          ));
+        }
+        m_data->host = host;
+      }
+      else
+      {
+        if( config("with_authority") != "y")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme does not allow a host component")
+          ));
+        }
+
+        if( isValidHost(host))
+        {
+          std::string temp;
+
+          // always decode in case isValidHost()
+          // is reimplemented and supports also
+          // the [v ... ] notation.
+          if( host.at(0) == '[')
+          {
+            temp = str::toUpper(zypp::url::decode(host));
+          }
+          else
+          {
+            temp = str::toLower(zypp::url::decode(host));
+          }
+
+          m_data->host = zypp::url::encode(
+            temp, config("safe_hostname")
+          );
+        }
+        else
+        {
+          ZYPP_THROW(UrlBadComponentException(
+            str::form(_("Invalid host component '%s'"), host.c_str())
+          ));
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPort(const std::string &port)
+    {
+      if( port.empty())
+      {
+        m_data->port = port;
+      }
+      else
+      {
+        if( config("with_authority") != "y" ||
+            config("with_port")      != "y")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme does not allow a port")
+          ));
+        }
+
+        if( isValidPort(port))
+        {
+          m_data->port = port;
+        }
+        else
+        {
+          ZYPP_THROW(UrlBadComponentException(
+            str::form(_("Invalid port component '%s'"), port.c_str())
+          ));
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathName(const std::string &path,
+                         EEncoding         eflag)
+    {
+      if( path.empty())
+      {
+        if(config("require_pathname") == "m")
+        {
+          ZYPP_THROW(UrlNotAllowedException(
+            _("Url scheme requires path name")
+          ));
+        }
+        m_data->pathname = path;
+      }
+      else
+      {
+        if(eflag == zypp::url::E_ENCODED)
+        {
+          checkUrlData(path, "path name", config("rx_pathname"));
+
+          if( !getHost(zypp::url::E_ENCODED).empty())
+          {
+            // has to begin with a "/". For consistency with
+            // setPathName while the host is empty, we allow
+            // it in encoded ("%2f") form - cleanupPathName()
+            // will fix / decode the first slash if needed.
+            if(!(path.at(0) == '/' || (path.size() >= 3 &&
+                 str::toLower(path.substr(0, 3)) == "%2f")))
+            {
+              ZYPP_THROW(UrlNotAllowedException(
+                _("Relative path not allowed if authority exists")
+              ));
+            }
+          }
+
+          m_data->pathname = cleanupPathName(path);
+        }
+        else //     zypp::url::E_DECODED
+        {
+          if( !getHost(zypp::url::E_ENCODED).empty())
+          {
+            if(path.at(0) != '/')
+            {
+              ZYPP_THROW(UrlNotAllowedException(
+                _("Relative path not allowed if authority exists")
+              ));
+            }
+          }
+
+          m_data->pathname = cleanupPathName(
+            zypp::url::encode(
+              path, config("safe_pathname")
+            )
+          );
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathParams(const std::string &params)
+    {
+      if( params.empty())
+      {
+        m_data->pathparams = params;
+      }
+      else
+      {
+        checkUrlData(params, "path parameters", config("rx_pathparams"));
+
+        m_data->pathparams = params;
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathParamsVec(const zypp::url::ParamVec &pvec)
+    {
+      setPathParams(
+        zypp::url::join(
+          pvec,
+          config("psep_pathparam")
+        )
+      );
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
+    {
+      if( config("psep_pathparam").empty() ||
+          config("vsep_pathparam").empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          "Path Parameter parsing not supported for this URL"
+        ));
+      }
+      setPathParams(
+        zypp::url::join(
+          pmap,
+          config("psep_pathparam"),
+          config("vsep_pathparam"),
+          config("safe_pathparams")
+        )
+      );
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setPathParam(const std::string &param, const std::string &value)
+    {
+          zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
+          pmap[param] = value;
+          setPathParamsMap(pmap);
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
+    {
+      setQueryString(
+        zypp::url::join(
+          pvec,
+          config("psep_querystr")
+        )
+      );
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
+    {
+      if( config("psep_querystr").empty() ||
+          config("vsep_querystr").empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          _("Query string parsing not supported for this URL")
+        ));
+      }
+      setQueryString(
+        zypp::url::join(
+          pmap,
+          config("psep_querystr"),
+          config("vsep_querystr"),
+          config("safe_querystr")
+        )
+      );
+    }
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::setQueryParam(const std::string &param, const std::string &value)
+    {
+          zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
+          pmap[param] = value;
+          setQueryStringMap(pmap);
+    }
+
+    // ---------------------------------------------------------------
+    void
+    UrlBase::delQueryParam(const std::string &param)
+    {
+          zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
+          pmap.erase(param);
+          setQueryStringMap(pmap);
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::cleanupPathName(const std::string &path) const
+    {
+      bool authority = !getHost(zypp::url::E_ENCODED).empty();
+      return cleanupPathName(path, authority);
+    }
+
+    // ---------------------------------------------------------------
+    std::string
+    UrlBase::cleanupPathName(const std::string &path, bool authority) const
+    {
+      std::string copy( path);
+
+      // decode the first slash if it is encoded ...
+      if(copy.size() >= 3 && copy.at(0) != '/' &&
+         str::toLower(copy.substr(0, 3)) == "%2f")
+      {
+        copy.replace(0, 3, "/");
+      }
+
+      // if path begins with a double slash ("//"); encode the second
+      // slash [minimal and IMO sufficient] before the first path
+      // segment, to fulfill the path-absolute rule of RFC 3986
+      // disallowing a "//" if no authority is present.
+      if( authority)
+      {
+        //
+        // rewrite of "//" to "/%2f" not required, use config
+        //
+        if(config("path_encode_slash2") == "y")
+        {
+          // rewrite "//" ==> "/%2f"
+          if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
+          {
+            copy.replace(1, 1, "%2F");
+          }
+        }
+        else
+        {
+          // rewrite "/%2f" ==> "//"
+          if(copy.size() >= 4 && copy.at(0) == '/' &&
+             str::toLower(copy.substr(1, 4)) == "%2f")
+          {
+            copy.replace(1, 4, "/");
+          }
+        }
+      }
+      else
+      {
+        // rewrite of "//" to "/%2f" is required (no authority)
+        if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
+        {
+          copy.replace(1, 1, "%2F");
+        }
+      }
+      return copy;
+    }
+
+
+    // ---------------------------------------------------------------
+    bool
+    UrlBase::isValidHost(const std::string &host) const
+    {
+      try
+      {
+        str::regex regx(RX_VALID_HOSTIPV6);
+        if( str::regex_match(host, regx))
+        {
+          struct in6_addr ip;
+          std::string temp( host.substr(1, host.size()-2));
+
+          return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
+        }
+        else
+        {
+          // matches also IPv4 dotted-decimal adresses...
+          std::string temp( zypp::url::decode(host));
+          str::regex  regx(RX_VALID_HOSTNAME);
+          return str::regex_match(temp, regx);
+        }
+      }
+      catch( ... )
+      {}
+
+      return false;
+    }
+
+
+    // ---------------------------------------------------------------
+    bool
+    UrlBase::isValidPort(const std::string &port) const
+    {
+      try
+      {
+        str::regex regx(RX_VALID_PORT);
+        if( str::regex_match(port, regx))
+        {
+          long pnum = str::strtonum<long>(port);
+          return ( pnum >= 1 && pnum <= USHRT_MAX);
+        }
+      }
+      catch( ... )
+      {}
+      return false;
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace url
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/url/UrlBase.h b/zypp/url/UrlBase.h
new file mode 100644 (file)
index 0000000..09b9c6e
--- /dev/null
@@ -0,0 +1,1098 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/url/UrlBase.h
+ */
+#ifndef   ZYPP_URL_URLBASE_H
+#define   ZYPP_URL_URLBASE_H
+
+#include <zypp/url/UrlUtils.h>
+#include <zypp/base/PtrTypes.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace url
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Url::asString() view options.
+     *
+     * A instance of this class represents a bit-wise combination
+     * of view option constants.
+     *
+     * It provides ViewOption::operator+() and ViewOption::operator-()
+     * to modify a view option combination and a ViewOption::has()
+     * method, to check if a specified option is enabled or not.
+     */
+    struct ViewOption
+    {
+      /** @{ */
+      /**
+       * Option to include scheme name in the URL string.
+       *
+       * Disabling this option causes, that the URL string
+       * contains the path, query and fragment components
+       * only, for example just "/foo/bar.txt".
+       *
+       * This option is \b enabled by default.
+       */
+      static const ViewOption WITH_SCHEME;
+      /**
+       * Option to include username in the URL string.
+       *
+       * This option depends on a enabled WITH_SCHEME and
+       * WITH_HOST options and is \b enabled by default.
+       */
+      static const ViewOption WITH_USERNAME;
+      /**
+       * Option to include password in the URL string.
+       *
+       * This option depends on a enabled WITH_SCHEME,
+       * WITH_HOST and WITH_USERNAME options and is
+       * \b disabled by default, causing to hide the
+       * password in the URL authority.
+       */
+      static const ViewOption WITH_PASSWORD;
+      /**
+       * Option to include hostname in the URL string.
+       *
+       * This option depends on a enabled WITH_SCHEME
+       * option and is \b enabled by default.
+       */
+      static const ViewOption WITH_HOST;
+      /**
+       * Option to include port number in the URL string.
+       *
+       * This option depends on a enabled WITH_SCHEME and
+       * WITH_HOST options and is \b enabled by default.
+       */
+      static const ViewOption WITH_PORT;
+      /**
+       * Option to include path name in the URL string.
+       *
+       * This option is \b enabled by default.
+       */
+      static const ViewOption WITH_PATH_NAME;
+      /**
+       * Option to include path parameters in the URL string.
+       *
+       * This option depends on a enabled WITH_PATH_NAME
+       * option and is \b disabled by default, causing to
+       * hide the path parameters.
+       */
+      static const ViewOption WITH_PATH_PARAMS;
+      /**
+       * Option to include query string in the URL string.
+       *
+       * This option is \b enabled by default.
+       */
+      static const ViewOption WITH_QUERY_STR;
+      /**
+       * Option to include fragment string in the URL string.
+       *
+       * This option is \b enabled by default.
+       */
+      static const ViewOption WITH_FRAGMENT;
+      /** @} */
+
+      /** @{ */
+      /**
+       * Explicitely include the URL authority separator "//".
+       *
+       * It causes, that the URL string includes an empty URL
+       * authority, for example:
+       * "file:///foo.txt" instead of just "file:/foo.txt".
+       *
+       * This option depends on a enabled WITH_SCHEME view
+       * option and is enabled by default.
+       */
+      static const ViewOption EMPTY_AUTHORITY;
+      /**
+       * Explicitely include the "/" path character.
+       *
+       * It causes, that a "/" is added to the Url if the path
+       * name is empty, for example:
+       *
+       * "http://localhost/" instead of just "http://localhost".
+       *
+       * This option depends on a enabled WITH_PATH_NAME view
+       * option and is enabled by default.
+       */
+      static const ViewOption EMPTY_PATH_NAME;
+      /**
+       * Explicitely include the path parameters separator ";".
+       *
+       * It causes, that the URL allways contains the ";" path
+       * parameters separator.
+       *
+       * This option depends on a enabled EMPTY_PATH_NAME view
+       * option and is disabled by default.
+       */
+      static const ViewOption EMPTY_PATH_PARAMS;
+      /**
+       * Explicitely include the query string separator "?".
+       *
+       * It causes, that if the query string is requested using
+       * the WITH_QUERY_STR option, the URL allways contains the
+       * "?" query string separator, even if the query string is
+       * empty.
+       * This option depends on a enabled WITH_QUERY_STR view
+       * option and is disabled by default.
+       */
+      static const ViewOption EMPTY_QUERY_STR;
+      /**
+       * Explicitely include the fragment string separator "#".
+       *
+       * It causes, that if the fragment string is requested using
+       * the WITH_FRAGMENT option, the URL allways contains the "#"
+       * fragment string separator, even if the fragment string is
+       * empty. 
+       * This option depends on a enabled WITH_FRAGMENT view
+       * option and is disabled by default.
+       */
+      static const ViewOption EMPTY_FRAGMENT;
+      /** @} */
+
+      /** @{ */
+      /**
+       * Default combination of view options.
+       *
+       * By default, following view options are enabled:
+       *   WITH_SCHEME,    WITH_USERNAME,    WITH_HOST,
+       *   WITH_PORT,      WITH_PATH_NAME,   WITH_QUERY_STR,
+       *   WITH_FRAGMENT,  EMPTY_AUTHORITY,  EMPTY_PATH_NAME.
+       */
+      static const ViewOption DEFAULTS;
+      /** @} */
+
+
+      /**
+       * Create instance with default combination of view options.
+       */
+      ViewOption();
+
+
+      /**
+       * Adds \p l and \p r to a new option combination.
+       *
+       * @return The new option combination.
+       */
+      friend inline ViewOption
+      operator + (const ViewOption &l, const ViewOption &r)
+      {
+        return ViewOption(l.opt |  r.opt);
+      }
+
+      /**
+       * Substract \p r from \p l to a new option combination.
+       *
+       * @return The new option combination.
+       */
+      friend inline ViewOption
+      operator - (const ViewOption &l, const ViewOption &r)
+      {
+        return ViewOption(l.opt & ~r.opt);
+      }
+
+      /**
+       * Assign specified option combination \p o to the current object.
+       *
+       * \param o   The option or option combination to make a copy of.
+       * \return A reference to this option combination.
+       */
+      inline ViewOption &
+      operator = (const ViewOption &o)
+      {
+        opt = o.opt; return *this;
+      }
+
+      /**
+       * Check if specified option \p o is set in the current object.
+       * \param o    A view option constant.
+       * \return True, if specified option \p o is
+       *               set/enabled in the instance.
+       */
+      inline bool
+      has(const ViewOption &o) const
+      {
+        return o.opt & opt;
+      }
+
+    private:
+      ViewOption(int option);
+      int opt;
+    };
+
+
+    // ---------------------------------------------------------------
+    /**
+     * ViewOptions is just an alias for ViewOption.
+     */
+    typedef ViewOption                           ViewOptions;
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Vector of URL scheme names.
+     */
+    typedef std::vector<std::string>             UrlSchemes;
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Forward declaration of internal UrlBase data.
+     */
+    class UrlBaseData;
+
+
+    // ---------------------------------------------------------------
+    /**
+     * \brief Generic Url base class.
+     *
+     * The UrlBase class implements default behaviour for URL
+     * manipulations and a base for implementation of scheme-
+     * specialized URL's for the Url class.
+     *
+     */
+    class UrlBase
+    {
+    public:
+
+      virtual
+      ~UrlBase();
+
+      UrlBase();
+
+      /**
+       * Create a new Url object as copy of the given one.
+       * \param url The Url object to make a copy of.
+       */
+      UrlBase(const UrlBase &url);
+
+      /**
+       * \brief Construct new object and initializes it with
+       *        specified URL components.
+       *
+       * \param scheme    The scheme name.
+       * \param authority The encoded authority component data.
+       * \param pathdata  The encoded path component data.
+       * \param querystr  The encoded query string component.
+       * \param fragment  The encoded fragment string component.
+       * \throws UrlNotAllowedException if one of the components
+       *         is not allowed for the scheme.
+       * \throws UrlBadComponentException if one of the components
+       *         contains an invalid character.
+       */
+      UrlBase(const std::string &scheme,
+              const std::string &authority,
+              const std::string &pathdata,
+              const std::string &querystr,
+              const std::string &fragment);
+
+
+      // -----------------
+      /**
+       * \brief Clears all data in the object.
+       */
+      virtual void
+      clear();
+
+      /**
+       * Returns pointer to a copy of the current object.
+       *
+       * Should be reimplemented by all derived object using
+       * the copy constructor of the derived class, e.g.:
+       * \code
+       *   return new MyUrlDerivedFromUrlBase(*this);
+       * \endcode
+       *
+       * \return A pointer to a copy of the current object.
+       */
+      virtual UrlBase *
+      clone() const;
+
+      /**
+       * \brief Initializes current object with new URL components.
+       *
+       * \param scheme    The scheme name.
+       * \param authority The encoded authority component data.
+       * \param pathdata  The encoded path component data.
+       * \param querystr  The encoded query string component.
+       * \param fragment  The encoded fragment string component.
+       * \throws UrlNotAllowedException if one of the components
+       *         is not allowed in the scheme.
+       * \throws UrlBadComponentException if one of the components
+       *         contains an invalid character.
+       */
+      virtual void
+      init(const std::string &scheme,
+           const std::string &authority,
+           const std::string &pathdata,
+           const std::string &querystr,
+           const std::string &fragment);
+
+
+      // -----------------
+      /**
+       * \brief Returns scheme names known by this object.
+       *
+       * This method is used in the isValidScheme() method and
+       * is intended to be reimplemented by derived classes to
+       * return the scheme names it implements (is restricted
+       * or compatible to).
+       *
+       * For example, if your derived class implements special
+       * features of LDAP URL's, this method may return "ldap"
+       * and "ldaps" scheme names.
+       *
+       * The UrlBase class returns an empty vector, that signals
+       * that it is useable with all URL's.
+       *
+       * \return A vector with scheme names known by this object.
+       */
+      virtual UrlSchemes
+      getKnownSchemes() const;
+
+      /**
+       * \brief Returns if scheme name is known to this object.
+       * \return True, if scheme name is known to this object.
+       */
+      virtual bool
+      isKnownScheme(const std::string &scheme) const;
+
+
+      /**
+       * \brief Verifies specified scheme name.
+       *
+       * Verifies the generic syntax of the specified \p scheme name
+       * and if it is contained in the current object's list of known
+       * schemes (see getKnownSchemes()) if the list is not empty (as
+       * in the UrlBase class).
+       *
+       * \param  scheme The scheme name to verify.
+       * \return True, if generic scheme name syntax is valid and
+       *         the scheme name is known to the current object.
+       */
+      virtual bool
+      isValidScheme(const std::string &scheme) const;
+
+      /**
+       * \brief Verifies the Url.
+       *
+       * Verifies if the current object contains a non-empty scheme
+       * name. Additional semantical URL checks may be performed by
+       * derived UrlBase-objects.
+       *
+       * \return True, if the Url seems to be valid.
+       */
+      virtual bool
+      isValid() const;
+
+
+      // -----------------
+      /**
+       * Returns a default string representation of the Url object.
+       *
+       * By default, a password in the URL will be hidden.
+       *
+       * \return A default string representation of the Url object.
+       */
+      virtual std::string
+      asString() const;
+
+      /**
+       * Returns a string representation of the Url object.
+       *
+       * To include a password in the resulting Url string, use:
+       * \code
+       *    url.asString(url.getViewOptions() +
+       *                 url::ViewOptions::WITH_PASSWORD);
+       * \endcode
+       *
+       * \param opts  A combination of view options.
+       * \return A string representation of the Url object. 
+       */
+      virtual std::string
+      asString(const zypp::url::ViewOptions &opts) const;
+
+
+      // -----------------
+      /**
+       * Returns the scheme name of the URL.
+       * \return Scheme name of the current Url object.
+       */
+      virtual std::string
+      getScheme() const;
+
+
+      // -----------------
+      /**
+       * Returns the encoded authority component of the URL.
+       *
+       * The returned authority string does not contain the leading
+       * "//" separator characters, but just its "user:pass@host:port"
+       * content only.
+       *
+       * \return The encoded authority component string.
+       */
+      virtual std::string
+      getAuthority() const;
+
+      /**
+       * Returns the username from the URL authority.
+       * \param eflag Flag if the usename should be percent-decoded or not.
+       * \return The username sub-component from the URL authority.
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getUsername(EEncoding eflag) const;
+
+      /**
+       * Returns the password from the URL authority.
+       * \param eflag Flag if the password should be percent-decoded or not.
+       * \return The password sub-component from the URL authority.
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getPassword(EEncoding eflag) const;
+
+      /**
+       * Returns the hostname or IP from the URL authority.
+       *
+       * In case the Url contains an IPv6 number, it is be surrounded
+       * by "[" and "]" characters, for example "[::1]" for an IPv6
+       * localhost address.
+       *
+       * \param eflag Flag if the host should be percent-decoded or not.
+       * \return The host sub-component from the URL authority.
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getHost(EEncoding eflag) const;
+
+      /**
+       * Returns the port number from the URL authority.
+       * \return The port sub-component from the URL authority.
+       */
+      virtual std::string
+      getPort() const;
+
+
+      // -----------------
+      /**
+       * Returns the encoded path component of the URL.
+       *
+       * The path data contains the path name, optionally
+       * followed by path parameters separated with a ";"
+       * character, for example "/foo/bar;version=1.1".
+       *
+       * \return The encoded path component of the URL.
+       */
+      virtual std::string
+      getPathData() const;
+
+      /**
+       * Returns the path name from the URL.
+       * \param eflag Flag if the path should be decoded or not.
+       * \return The path name sub-component without path parameters
+       *         from path data component of the URL.
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getPathName(EEncoding eflag) const;
+
+      /**
+       * Returns the encoded path parameters from the URL.
+       * \return The encoded path parameters from the URL.
+       */
+      virtual std::string
+      getPathParams() const;
+
+      /**
+       * Returns a vector with encoded path parameter substrings.
+       *
+       * The default path parameter separator is the \c ',' character.
+       * A schema specific object may overide the default separators.
+       *
+       * For example, the path parameters string "foo=1,bar=2" is splited
+       * by default into a vector containing the substrings "foo=1" and
+       * "bar=2".
+       *
+       * \return The encoded path parameters vector.
+       */
+      virtual zypp::url::ParamVec
+      getPathParamsVec() const;
+
+      /**
+       * Returns a string map with path parameter keys and values.
+       *
+       * The default path parameter separator is the \c ',' character,
+       * the default key/value separator for the path parameters is
+       * the \c '=' character.
+       * A schema specific object may overide the default separators.
+       *
+       * For example, the path parameters string "foo=1,bar=2" is splited
+       * into a map containing "foo" = "1" and "bar" = "2" by default.
+       *
+       * \param eflag Flag if the path parameter keys and values should
+       *               be decoded or not.
+       * \return The path parameters key and values as a string map.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual zypp::url::ParamMap
+      getPathParamsMap(EEncoding eflag) const;
+
+      /**
+       * Return the value for the specified path parameter.
+       *
+       * For example, if the path parameters string is "foo=1,bar=2"
+       * the method will return the substring "1" for the param key
+       * "foo" and "2" for the param key "bar".
+       *
+       * \param param The path parameter key.
+       * \param eflag Flag if the path parameter keys and values should
+       *              be decoded or not.
+       * \return The value for the path parameter key or empty string.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getPathParam(const std::string &param, EEncoding eflag) const;
+
+
+      // -----------------
+      /**
+       * Returns the encoded query string component of the URL.
+       *
+       * The query string is returned without first "?" (separator)
+       * character. Further "?" characters as in e.g. LDAP URL's
+       * remains in the returned string.
+       *
+       * \return The encoded query string component of the URL.
+       */
+      virtual std::string
+      getQueryString() const;
+
+      /**
+       * Returns a vector with query string parameter substrings.
+       *
+       * The default query string parameter separator is the \c '&'
+       * character.
+       * A schema specific object may overide the default separators.
+       *
+       * For example, the query string "foo=1&bar=2" is splited by
+       * default into a vector containing the substrings "foo=1" and
+       * "bar=2".
+       *
+       * \return The query string splited into a vector of substrings.
+       */
+      virtual zypp::url::ParamVec
+      getQueryStringVec() const;
+
+      /**
+       * Returns a string map with query parameter and their values.
+       *
+       * The default query string parameter separator is the \c ','
+       * character, the default key/value separator the \c '=' character.
+       * A schema specific object may overide the default separators.
+       *
+       * For example, the query string "foo=1&bar=2" is splited by
+       * default into a map containing "foo" = "1" and "bar" = "2".
+       *
+       * \param eflag Flag if the query string keys and values should
+       *               be decoded or not.
+       * \return The query string as a key/value string map.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual zypp::url::ParamMap
+      getQueryStringMap(EEncoding eflag) const;
+
+      /**
+       * Return the value for the specified query parameter.
+       *
+       * For example, if the query string is "foo=1,bar=2" the method
+       * will return the substring "1" for the param key "foo" and
+       * "2" for the param key "bar".
+       *
+       * \param param The query parameter key.
+       * \param eflag Flag if the query parameter keys and values should
+       *              be decoded or not.
+       * \return The value for the query parameter key or empty string.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getQueryParam(const std::string &param, EEncoding eflag) const;
+
+
+      // -----------------
+      /**
+       * Returns the encoded fragment component of the URL.
+       * \param eflag Flag if the fragment should be percent-decoded or not.
+       * \return The encoded fragment component of the URL.
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual std::string
+      getFragment(EEncoding eflag) const;
+
+
+      // -----------------
+      /**
+       * \brief Set the scheme name in the URL.
+       * \param scheme The new scheme name.
+       * \throws UrlBadComponentException if the \p scheme
+       *         contains an invalid character or is empty.
+       */
+      virtual void
+      setScheme(const std::string &scheme);
+
+
+      // -----------------
+      /**
+       * \brief Set the authority component in the URL.
+       *
+       * The \p authority string shoud not contain any leading
+       * "//" separator characters (just "user:pass@host:port").
+       *
+       * \param authority The authority component string.
+       * \throws UrlNotAllowedException if the \p authority
+       *         has to be empty in for the current scheme.
+       * \throws UrlBadComponentException if the \p authority
+       *         contains an invalid character.
+       * \throws UrlParsingException if \p authority parsing fails.
+       */
+      virtual void
+      setAuthority(const std::string &authority);
+
+      /**
+       * \brief Set the username in the URL authority.
+       * \param user  The new username.
+       * \param eflag If the \p username is encoded or not.
+       * \throws UrlNotAllowedException if the \p user
+       *         has to be empty in for the current scheme.
+       * \throws UrlBadComponentException if the \p user
+       *         contains an invalid character.
+       */
+      virtual void
+      setUsername(const std::string &user,
+                  EEncoding         eflag);
+
+      /**
+       * \brief Set the password in the URL authority.
+       * \param pass  The new password.
+       * \param eflag If the \p password is encoded or not.
+       * \throws UrlNotAllowedException if the \p pass
+       *         has to be empty in for the current scheme.
+       * \throws UrlBadComponentException if the \p pass
+       *         contains an invalid character.
+       */
+      virtual void
+      setPassword(const std::string &pass,
+                  EEncoding         eflag);
+
+      /**
+       * \brief Set the hostname or IP in the URL authority.
+       *
+       * The \p host parameter may contain a hostname, an IPv4 address
+       * in dotted-decimal form or an IPv6 address literal encapsulated
+       * within square brackets (RFC3513, Sect. 2.2).
+       *
+       * A hostname may contain national alphanumeric UTF8 characters
+       * (letters other than ASCII a-zA-Z), that will be encoded.
+       * This function allows to specify both, a encoded or decoded
+       * hostname.
+       *
+       * Other IP literals in "[v ... ]" square bracket format are not
+       * supported by the implementation in UrlBase class.
+       *
+       * \param host The new hostname or IP address.
+       * \throws UrlNotAllowedException if the \p host
+       *         has to be empty in for the current scheme.
+       * \throws UrlBadComponentException if the \p host is invalid.
+       */
+      virtual void
+      setHost(const std::string &host);
+
+      /**
+       * \brief Set the port number in the URL authority.
+       * \param port The new port number.
+       * \throws UrlNotAllowedException if the \p port
+       *         has to be empty in for the current scheme.
+       * \throws UrlBadComponentException if the \p port is invalid.
+       */
+      virtual void
+      setPort(const std::string &port);
+
+
+      // -----------------
+      /**
+       * \brief Set the path data component in the URL.
+       *
+       * By default, the \p pathdata string may include path
+       * parameters separated by the ";" separator character.
+       *
+       * \param pathdata The encoded path data component string.
+       * \throws UrlBadComponentException if the \p pathdata
+       *         contains an invalid character.
+       */
+      virtual void
+      setPathData(const std::string &pathdata);
+
+      /**
+       * \brief Set the path name.
+       * \param path  The new path name.
+       * \param eflag If the \p path name is encoded or not.
+       * \throws UrlBadComponentException if the \p path name
+       *         contains an invalid character.
+       */
+      virtual void
+      setPathName(const std::string &path,
+                  EEncoding         eflag);
+
+      /**
+       * \brief Set the path parameters.
+       * \param params The new encoded path parameter string.
+       * \throws UrlBadComponentException if the path \p params
+       *         contains an invalid character.
+       */
+      virtual void
+      setPathParams(const std::string &params);
+
+      /**
+       * \brief Set the path parameters.
+       * \param pvec The vector with encoded path parameters.
+       * \throws UrlBadComponentException if the \p pvec
+       *         contains an invalid character.
+       */
+      virtual void
+      setPathParamsVec(const zypp::url::ParamVec &pvec);
+
+      /**
+       * \brief Set the path parameters.
+       * \param pmap The map with decoded path parameters.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       */
+      virtual void
+      setPathParamsMap(const zypp::url::ParamMap &pmap);
+
+      /**
+       * \brief Set or add value for the specified path parameter.
+       * \param param The decoded path parameter name.
+       * \param value The decoded path parameter value.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual void
+      setPathParam(const std::string &param, const std::string &value);
+
+
+      // -----------------
+      /**
+       * \brief Set the query string in the URL.
+       *
+       * The \p querystr string parameter is supposed
+       * to not to contain the "?" URL query separator
+       * character (use just a "foo=bar&x=22" instead
+       * of "?foo=bar&x=22").
+       *
+       * \param querystr The new encoded query string.
+       * \throws UrlBadComponentException if the \p querystr
+       *         contains an invalid character.
+       */
+      virtual void
+      setQueryString(const std::string &querystr);
+
+      /**
+       * \brief Set the query parameters.
+       * \param qvec The vector with encoded query parameters.
+       * \throws UrlBadComponentException if the \p qvec
+       *         contains an invalid character.
+       */
+      virtual void
+      setQueryStringVec(const zypp::url::ParamVec &qvec);
+
+      /**
+       * \brief Set the query parameters.
+       * \param qmap The map with decoded query parameters.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       */
+      virtual void
+      setQueryStringMap(const zypp::url::ParamMap &qmap);
+
+      /**
+       * \brief Set or add value for the specified query parameter.
+       * \param param The decoded query parameter name.
+       * \param value The decoded query parameter value.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual void
+      setQueryParam(const std::string &param, const std::string &value);
+
+      /**
+       * \brief remove the specified query parameter.
+       * \param param The decoded query parameter name.
+       * \throws UrlNotSupportedException if parameter parsing
+       *         is not supported for a URL (scheme).
+       * \throws UrlDecodingException if the decoded result string
+       *         would contain a '\\0' character.
+       */
+      virtual void
+      delQueryParam(const std::string &param);
+
+
+      // -----------------
+      /**
+       * \brief Set the fragment string in the URL.
+       * \param fragment The new fragment string.
+       * \param eflag If the \p fragment is encoded or not.
+       * \throws UrlBadComponentException if the \p querystr
+       *         contains an invalid character.
+       */
+      virtual void
+      setFragment(const std::string &fragment,
+                  EEncoding         eflag);
+
+
+      // -----------------
+      /**
+       * Configures behaviour of the instance.
+       *
+       * This method is called in UrlBase constructors before
+       * any URL components are applied.
+       * Derived classes may reimplement this method to change
+       * the behaviour of the object.
+       * Use the config() methods to query and change them.
+       *
+       * The UrlBase class uses following config variables:
+       *
+       * - Common path parameter separators:
+       *   - \a \c sep_pathparams   \c ";"
+       *     Separator used to split path parameters from path name.
+       *     Setting it to empty string disables splitting of path
+       *     name and path parameters. Set also rx_pathparams to an
+       *     empty string.
+       *   - \a \c psep_pathparam   \c ","
+       *     Separator between path parameters.
+       *   - \a \c vsep_pathparam   \c "="
+       *     Separator between key and value of a path parameter.
+       *   .
+       * .
+       *
+       * - Common query string separators:
+       *   - \a \c psep_querystr    \c "&"
+       *     Separator between query string parameters.
+       *   - \a \c vsep_querystr    \c "="
+       *     Separator between key and value of a query parameter.
+       *   .
+       * .
+       *
+       * - Characters in URL components, that are safe without
+       *   URL percent-encoding (see zypp::url::encode()).
+       *   - \a safe_username
+       *   - \a safe_password
+       *   - \a safe_hostname
+       *   - \a safe_pathname
+       *   - \a safe_pathparams
+       *   - \a safe_querystr
+       *   - \a safe_fragment
+       *   .
+       * .
+       *
+       * - Regular expressions used to verify encoded URL
+       *   components and their sub-components:
+       *   - \a rx_username
+       *   - \a rx_password
+       *   - \a rx_pathname
+       *   - \a rx_pathparams
+       *   - \a rx_querystr
+       *   - \a rx_fragment
+       *   .
+       * .
+       */
+      virtual void
+      configure();
+
+
+      /**
+       * Get the value of a UrlBase configuration variable.
+       *
+       * See configure() method for names an purpose of the
+       * configuration variables used in UrlBase class.
+       *
+       * \param opt The name of the configuration variable.
+       * \return The value of the specified variable
+       *         or empty string.
+       */
+      std::string
+      config(const std::string &opt) const;
+
+      /**
+       * Set the value of a UrlBase configuration variable.
+       *
+       * See configure() method for names an purpose of the
+       * configuration variables used in UrlBase class.
+       *
+       * \param opt The name of the configuration variable.
+       * \param val The new value for the configuration variable.
+       */
+      void
+      config(const std::string &opt, const std::string &val);
+
+
+      /**
+       * Return the view options of the current object.
+       *
+       * This method is used to query the view options
+       * used by the asString() method.
+       *
+       * \return The current view option combination.
+       */
+      ViewOptions
+      getViewOptions() const;
+
+      /**
+       * Change the view options of the current object.
+       *
+       * This method is used to change the view options
+       * used by the asString() method.
+       *
+       * \param vopts New view options combination.
+       */
+      void
+      setViewOptions(const ViewOptions &vopts);
+
+
+    protected:
+      /**
+       * Utility method to cleanup an encoded path name.
+       *
+       * By default, this method makes sure, that the first slash
+       * in the path is not encoded, and that the second slash
+       * before the first path segment, is encoded (to "%2F").
+       * It modifies the path in the url, for example:
+       *   "ftp://host//aaa//bbb" to "ftp://host/%2Faaa//bbb"
+       * or as encoded path only also "%2f/name" to "/%2fname".
+       *
+       * This operation is required to fulfill the path-absolute
+       * rule of RFC3986, if there is no authority. It avoids the
+       * missinterpretation of the path as an authority separator.
+       *
+       * It is not required if there is an authority ("//" behind
+       * the "scheme:"), that is in the path-abempty rule, but it
+       * is used e.g. in ftp url's defined by RFC1738.
+       * 
+       * We apply this operation in both cases (for all paths),
+       * but if \p authority is true, the encoding of the second
+       * slash depends on the schema configuration (for ftp only).
+       *
+       * \param path      The encoded path name to cleanup.
+       * \param authority Whether the url contains authority or not.
+       * \return A modified encoded path.
+       */
+      virtual std::string
+      cleanupPathName(const std::string &path, bool authority) const;
+
+      /**
+       * Utility method to cleanup an encoded path name.
+       *
+       * This variant of the method checks if the host component
+       * in the url is empty or not to differentiate if there is
+       * an authority.
+       *
+       * \param path      The encoded path name to cleanup.
+       * \return A modified encoded path.
+       */
+      virtual std::string
+      cleanupPathName(const std::string &path) const;
+
+      /**
+       * \brief Verifies specified host or IP.
+       *
+       * It verifies, if the specified \p host parameter contains
+       * a hostname, an IPv4 address in dotted-decimal form or an
+       * IPv6 address literal encapsulated within square brackets
+       * (RFC3513, Sect. 2.2).
+       *
+       * A hostname in the \p host parameter, may contain national
+       * alphanumeric UTF8 characters (letters other than ASCII
+       * a-zA-Z) and allows to specify both, a encoded or decoded
+       * hostname.
+       *
+       * This function does not perform any hostname lookups and
+       * supports only IPv6 addresses in "[ ... ]" notation. The
+       * "[v ... ]" square bracket format is not supported by
+       * this implementation.
+       *
+       * \param  host  The host name or IP to verify.
+       * \return True, if host seems to be valid.
+       */
+      virtual bool
+      isValidHost(const std::string &host) const;
+
+      /**
+       * \brief Verifies specified port number.
+       *
+       * \param  port  The port number to verify.
+       * \return True, if port number is valid.
+       */
+      virtual bool
+      isValidPort(const std::string &port) const;
+
+    private:
+      UrlBaseData *m_data;
+    };
+
+
+    // ---------------------------------------------------------------
+    /**
+     * \brief Copy-On-Write Url reference.
+     */
+    typedef RWCOW_pointer<UrlBase>          UrlRef;
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace url
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_URL_URLBASE_H */
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/url/UrlException.h b/zypp/url/UrlException.h
new file mode 100644 (file)
index 0000000..08f6704
--- /dev/null
@@ -0,0 +1,150 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/url/UrlException.h
+ */
+#ifndef   ZYPP_URL_URLEXCEPTION_H
+#define   ZYPP_URL_URLEXCEPTION_H
+
+#include <zypp/base/Exception.h>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace url
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Base class for all URL exceptions.
+     */
+    class UrlException: public zypp::Exception
+    {
+    public:
+      UrlException()
+        : zypp::Exception("Url exception")
+      {}
+
+      UrlException(const std::string &msg)
+        : zypp::Exception(msg)
+      {}
+
+      virtual ~UrlException() throw() {};
+    };
+
+    // ---------------------------------------------------------------
+    /**
+     * Thrown if the encoded string contains a NUL byte (%00).
+     */
+    class UrlDecodingException: public UrlException
+    {
+    public:
+      UrlDecodingException()
+        : UrlException("Url NUL decoding exception")
+      {}
+
+      UrlDecodingException(const std::string &msg)
+        : UrlException(msg)
+      {}
+
+      virtual ~UrlDecodingException() throw() {};
+    };
+
+    // ---------------------------------------------------------------
+    /**
+     * Thrown if the url or a component can't be parsed at all.
+     */
+    class UrlParsingException: public UrlException
+    {
+    public:
+      UrlParsingException()
+        : UrlException("Url parsing failure exception")
+      {}
+
+      UrlParsingException(const std::string &msg)
+        : UrlException(msg)
+      {}
+
+      virtual ~UrlParsingException() throw() {};
+    };
+
+    // ---------------------------------------------------------------
+    /**
+     * Thrown if a url component is invalid.
+     */
+    class UrlBadComponentException: public UrlException
+    {
+    public:
+      UrlBadComponentException()
+        : UrlException("Url bad component exception")
+      {}
+
+      UrlBadComponentException(const std::string &msg)
+        : UrlException(msg)
+      {}
+
+      virtual ~UrlBadComponentException() throw() {};
+    };
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Thrown if scheme does not allow a component.
+     */
+    class UrlNotAllowedException: public UrlException
+    {
+    public:
+      UrlNotAllowedException()
+        : UrlException("Url not allowed component exception")
+      {}
+
+      UrlNotAllowedException(const std::string &msg)
+        : UrlException(msg)
+      {}
+
+      virtual ~UrlNotAllowedException() throw() {};
+    };
+
+
+    // ---------------------------------------------------------------
+    /**
+     * Thrown if a feature e.g. parsing of a component
+     * is not supported for the url/scheme.
+     */
+    class UrlNotSupportedException: public UrlException
+    {
+    public:
+      UrlNotSupportedException()
+        : UrlException("Url parsing unsupported exception")
+      {}
+
+      UrlNotSupportedException(const std::string &msg)
+        : UrlException(msg)
+      {}
+
+      virtual ~UrlNotSupportedException() throw() {};
+    };
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace url
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_URL_URLEXCEPTION_H */
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/url/UrlUtils.cc b/zypp/url/UrlUtils.cc
new file mode 100644 (file)
index 0000000..ca83df6
--- /dev/null
@@ -0,0 +1,326 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/url/UrlUtils.cc
+ */
+#include <zypp/base/Gettext.h>
+#include <zypp/base/String.h>
+#include <zypp/url/UrlUtils.h>
+
+#include <stdlib.h>   // strtol
+#include <cctype>     // isxdigit
+#include <stdexcept>
+
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  namespace url
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    std::string
+    encode(const std::string &str, const std::string &safe,
+                                   EEncoding         eflag)
+    {
+      std::string skip("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                       "abcdefghijklmnopqrstuvwxyz"
+                       "0123456789.~_-");
+      std::string more(":/?#[]@!$&'()*+,;=");
+      size_t      beg, pos, len;
+      std::string out;
+
+      for(size_t i=0; i<safe.size(); i++)
+      {
+        if( more.find(safe.at(i)) != std::string::npos)
+          skip.append(1, safe.at(i));
+      }
+
+      len = str.length();
+      beg = 0;
+      while( beg < len)
+      {
+        pos = str.find_first_not_of(skip, beg);
+        if(pos != std::string::npos)
+        {
+          if( pos > beg)
+          {
+            out.append(str, beg, pos - beg);
+          }
+
+          if( eflag == E_ENCODED &&
+              pos + 2 < len      &&
+              str.at(pos) == '%' &&
+              std::isxdigit(str.at(pos + 1)) &&
+              std::isxdigit(str.at(pos + 2)))
+          {
+            out.append(str, pos, 3);
+            beg = pos + 3;
+          }
+          else
+          {
+            out.append( encode_octet( str.at(pos)));
+            beg = pos + 1;
+          }
+        }
+        else
+        {
+          out.append(str, beg, len - beg);
+          beg = len;
+        }
+      }
+      return out;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    decode(const std::string &str, bool allowNUL)
+    {
+      size_t      pos, end, len;
+      std::string out(str);
+
+      len = out.length();
+      pos = end = 0;
+      while(pos < len)
+      {
+        out[end] = out[pos];
+        if( pos + 2 < len && out.at(pos) == '%')
+        {
+          int c = decode_octet(out.c_str() + pos + 1);
+          switch(c)
+          {
+            case -1:
+              // not a hex noted octet...
+            break;
+
+            case 0:
+              // is a %00 octet allowed ?
+              if( !allowNUL)
+              {
+                ZYPP_THROW(UrlDecodingException(
+                  _("Encoded string contains a NUL byte")
+                ));
+              }
+            default:
+              // other octets are fine...
+              out[end] = c;
+              pos += 2;
+            break;
+          }
+        }
+        pos++;
+        end++;
+      }
+      if( end < pos)
+        out.erase(end);
+      return out;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    encode_octet(const unsigned char c)
+    {
+      static const unsigned char tab[] = "0123456789ABCDEF";
+      unsigned char      out[4];
+
+      out[0] = '%';
+      out[1] = tab[0x0f & (c >> 4)];
+      out[2] = tab[0x0f & c];
+      out[3] = '\0';
+
+      //snprintf(out, sizeof(out), "%%%02X", c);
+      return std::string((char *)out);
+    }
+
+
+    // ---------------------------------------------------------------
+    int
+    decode_octet(const char *hex)
+    {
+      if(hex && std::isxdigit(hex[0]) && std::isxdigit(hex[1]))
+      {
+        char x[3] = { hex[0], hex[1], '\0'};
+        return 0xff & ::strtol(x, NULL, 16);
+      }
+      else
+      {
+        return -1;
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    split(ParamVec          &pvec,
+          const std::string &pstr,
+               const std::string &psep)
+    {
+      size_t beg, pos, len;
+      if( psep.empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          _("Invalid parameter array split separator character")
+        ));
+      }
+
+      len = pstr.length();
+      beg = 0;
+
+      while( beg < len)
+      {
+        pos = pstr.find(psep, beg);
+        if(pos != std::string::npos)
+        {
+          pvec.push_back( pstr.substr(beg, pos - beg));
+          beg = pos + 1;
+        }
+        else
+        {
+          pvec.push_back( pstr.substr(beg, len - beg));
+          beg = len;
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    void
+    split(ParamMap          &pmap,
+          const std::string &str,
+          const std::string &psep,
+          const std::string &vsep,
+          EEncoding         eflag)
+    {
+      ParamVec                 pvec;
+      ParamVec::const_iterator pitr;
+      std::string              k, v;
+      size_t                   pos;
+
+      if( psep.empty() || vsep.empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          _("Invalid parameter map split separator character")
+        ));
+      }
+
+      split(pvec, str, psep);
+
+      for( pitr = pvec.begin(); pitr != pvec.end(); ++pitr)
+      {
+        pos = pitr->find(vsep);
+        if(pos != std::string::npos)
+        {
+          if( eflag == E_DECODED)
+          {
+            k = url::decode(pitr->substr(0, pos));
+            v = url::decode(pitr->substr(pos + 1));
+            pmap[ k ] = v;
+          }
+          else
+          {
+            k = pitr->substr(0, pos);
+            v = pitr->substr(pos + 1);
+            pmap[ k ] = v;
+          }
+        }
+        else
+        {
+          if( eflag == E_DECODED)
+          {
+            pmap[ url::decode(*pitr) ] = "";
+          }
+          else
+          {
+            pmap[ *pitr ] = "";
+          }
+        }
+      }
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    join(const ParamVec    &pvec,
+         const std::string &psep)
+    {
+      std::string    str;
+      ParamVec::const_iterator i( pvec.begin());
+
+      if( i != pvec.end())
+      {
+        str = *i;
+        while( ++i != pvec.end())
+        {
+          str += psep + *i;
+        }
+      }
+
+      return str;
+    }
+
+
+    // ---------------------------------------------------------------
+    std::string
+    join(const ParamMap    &pmap,
+         const std::string &psep,
+         const std::string &vsep,
+         const std::string &safe)
+    {
+      if( psep.empty() || vsep.empty())
+      {
+        ZYPP_THROW(UrlNotSupportedException(
+          _("Invalid parameter array join separator character")
+        ));
+      }
+
+      std::string join_safe;
+      for(std::string::size_type i=0; i<safe.size(); i++)
+      {
+        if( psep.find(safe[i]) == std::string::npos &&
+            vsep.find(safe[i]) == std::string::npos)
+        {
+          join_safe.append(1, safe[i]);
+        }
+      }
+      std::string              str;
+      ParamMap::const_iterator i( pmap.begin());
+
+      if( i != pmap.end())
+      {
+        str = encode(i->first, join_safe);
+        if( !i->second.empty())
+          str += vsep + encode(i->second, join_safe);
+
+        while( ++i != pmap.end())
+        {
+          str += psep + encode(i->first, join_safe);
+          if( !i->second.empty())
+            str +=  vsep + encode(i->second, join_safe);
+        }
+      }
+
+      return str;
+    }
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace url
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/url/UrlUtils.h b/zypp/url/UrlUtils.h
new file mode 100644 (file)
index 0000000..477b0cf
--- /dev/null
@@ -0,0 +1,261 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/**
+ * \file zypp/url/UrlUtils.h
+ */
+#ifndef   ZYPP_URL_URLUTILS_H
+#define   ZYPP_URL_URLUTILS_H
+
+#include <zypp/url/UrlException.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+/** Characters that are safe for URL without percent-encoding. */
+#define URL_SAFE_CHARS ":/?#[]@!$&'()*+,;="
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp
+{ ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+  /** Url details namespace. */
+  namespace url
+  { //////////////////////////////////////////////////////////////////
+
+
+    // ---------------------------------------------------------------
+    /** A parameter vector container.
+     * A string vector containing splited PathParam- or Query-String.
+     * Each string in the vector is allways URL percent encoded and
+     * usually contains a "key=value" pair.
+     */
+    typedef std::vector < std::string >             ParamVec;
+
+
+    /** A parameter map container.
+     * A map containing key and value pairs parsed from a PathParam-
+     * or Query-String.
+     */
+    typedef std::map < std::string, std::string >   ParamMap;
+
+
+    /** Encoding flags.
+     */
+    typedef enum {
+        E_ENCODED, //!< Flag to request encoded string(s).
+        E_DECODED  //!< Flag to request decoded string(s).
+    } EEncoding;
+
+
+    // ---------------------------------------------------------------
+    /** Encodes a string using URL percent encoding.
+     *
+     * By default, all characters except of "a-zA-Z0-9_.-" will be encoded.
+     * Additional characters from the set ":/?#[]@!$&'()*+,;=", that are
+     * safe for a URL compoent without encoding, can be specified in the
+     * \p safe argument.
+     *
+     * If the \p eflag parameter is set to E_ENCODED, then already encoded
+     * substrings will be detected and not encoded a second time.
+     *
+     * The following function call will encode the "@" character as "%40",
+     * but skip encoding of the "%" character, because the \p eflag is set
+     * to E_ENCODED and "%ba" is detected as a valid encoded character.
+     * \code
+     *   zypp::url::encode("foo%bar@localhost", "", E_ENCODED);
+     * \endcode
+     * With \p eflag set to E_DECODED, the "%" character would be encoded
+     * as well. The complete encoded string would be "foo%25bar%40localhost".
+     *
+     * \param str      A string to encode (binary data).
+     * \param safe     Characters safe to skip in encoding,
+     *                 e.g. "/" for path names.
+     * \param eflag    If to detect and skip already encoded substrings.
+     * \return A percent encoded string.
+     */
+    std::string
+    encode(const std::string &str, const std::string &safe = "",
+                                   EEncoding         eflag = E_DECODED);
+
+
+    // ---------------------------------------------------------------
+    /** Decodes a URL percent encoded string.
+     * Replaces all occurences of \c "%<hex><hex>" in the \p str string
+     * with the character encoded using the two hexadecimal digits that
+     * follows the "%" character.
+     *
+     * For example, the encoded string "%40%3F%3D%26%25" will be decoded
+     * to "@?=&%".
+     *
+     * \param str      A string to decode.
+     * \param allowNUL A flag, if \c "%00" (encoded \c '\\0')
+     *                 is allowed or not.
+     * \return A decoded strig (may contain binary data).
+     * \throws UrlDecodingException if \p allowNUL is false and
+     *         a encoded NUL byte (\c "%00") was found in \p str.
+     */
+    std::string
+    decode(const std::string &str, bool allowNUL = false);
+
+
+    // ---------------------------------------------------------------
+    /** Encode one character.
+     *
+     * Encode the specified character \p c into its \c "%<hex><hex>"
+     * representation.
+     *
+     * \param c        A character to encode.
+     * \return A percent encoded representation of the character,
+     *         e.g. %20 for a ' ' (space).
+     */
+    std::string
+    encode_octet(const unsigned char c);
+
+
+    // ---------------------------------------------------------------
+    /** Decode one character.
+     *
+     * Decode the \p hex parameter pointing to (at least) two hexadecimal
+     * digits into its character value and return it.
+     *
+     * Example:
+     * \code
+     *   char *str = "%40";
+     *   char *pct = strchr(str, '%');
+     *   int   chr = pct ? decode_octet(pct+1) : -1;
+     *      // chr is set to the '@' ASCII character now.
+     * \endcode
+     *
+     * \param hex     Pointer to two hex characters representing
+     *                the character value in percent-encoded strings.
+     * \return The value (0-255) encoded in the \p hex characters or -1
+     *         if \p hex does not point to two hexadecimal characters.
+     */
+    int
+    decode_octet(const char *hex);
+
+
+    // ---------------------------------------------------------------
+    /** Split into a parameter vector.
+     *
+     * Splits a parameter string \p pstr into substrings using \p psep
+     * as separator and appends the resulting substrings to \p pvec.
+     *
+     * Usual parameter separators are \c '&' for Query- and \c ',' for
+     * PathParam-Strings.
+     *
+     * \param pvec    Reference to a result parameter vector.
+     * \param pstr    Reference to the PathParam- or Query-String to split.
+     * \param psep    Parameter separator character to split at.
+     * \throws UrlNotSupportedException if \p psep separator is empty.
+     */
+    void
+    split(ParamVec          &pvec,
+          const std::string &pstr,
+          const std::string &psep);
+
+
+    // ---------------------------------------------------------------
+    /** Split into a parameter map.
+     *
+     * Splits a parameter string \p pstr into substrings using \p psep as
+     * separator and then, each substring into key and value pair using
+     * \p vsep as separator between parameter key and value and adds them
+     * to the parameter map \p pmap.
+     *
+     * If a parameter substring doesn't contain any value separator \p vsep,
+     * the substring is used as a parameter key and value is set to an empty
+     * string.
+     *
+     * Usual parameter separators are \c '&' for Query- and \c ',' for
+     * PathParam-Strings. A usual parameter-value separator is \c '=' for
+     * both, Query- and PathParam-Strings.
+     *
+     * If the encoding flag \p eflag is set to \p E_DECODED, then the key
+     * and values are dedcoded before they are stored in the map.
+     *
+     * \param pmap    Reference to a result parameter map.
+     * \param pstr    Reference to the PathParam- or Query-String to split.
+     * \param psep    Separator character to split key-value pairs.
+     * \param vsep    Separator character to split key and value.
+     * \param eflag   Flag if the key and value strings should be URL percent
+     *                decoded before they're stored in the map.
+     * \throws UrlNotSupportedException if \p psep or \p vsep separator
+     *         is empty.
+     */
+    void
+    split(ParamMap          &pmap,
+          const std::string &pstr,
+          const std::string &psep,
+          const std::string &vsep,
+          EEncoding         eflag = E_ENCODED);
+
+
+    // ---------------------------------------------------------------
+    /** Join parameter vector to a string.
+     *
+     * Creates a string containing all substrings from the \p pvec separated
+     * by \p psep separator character. The substrings in \p pvec should be
+     * already URL percent encoded and should't contain \p psep characters.
+     *
+     * Usual parameter separators are \c '&' for Query- and \c ',' for
+     * PathParam-Strings.
+     *
+     * \param pvec    Reference to encoded parameter vector.
+     * \param psep    Parameter separator character to use.
+     * \return A parameter string.
+     */
+    std::string
+    join(const ParamVec     &pvec,
+         const std::string  &psep);
+
+
+    // ---------------------------------------------------------------
+    /** Join parameter map to a string.
+     *
+     * Creates a string containing all parameter key-value pairs from the
+     * parameter map \p pmap, that will be joined using the \p psep character
+     * and the parameter key is separated from the parameter value using the
+     * \p vsep character. Both, key and value will be automatically encoded.
+     *
+     * Usual parameter separators are \c '&' for Query- and \c ',' for
+     * PathParam-Strings. A usual parameter-value separator is \c '=' for
+     * both, Query- and PathParam-Strings.
+     *
+     * See encode() function from details about the \p safe characters.
+     *
+     * \param pmap    Reference to a parameter map.
+     * \param psep    Separator character to use between key-value pairs.
+     * \param vsep    Separator character to use between keys and values.
+     * \param safe    List of characters to accept without encoding.
+     * \return A URL percent-encoded parameter string.
+     * \throws UrlNotSupportedException if \p psep or \p vsep separator
+     *         is empty.
+     */
+    std::string
+    join(const ParamMap     &pmap,
+         const std::string  &psep,
+         const std::string  &vsep,
+         const std::string  &safe);
+
+
+    //////////////////////////////////////////////////////////////////
+  } // namespace url
+  ////////////////////////////////////////////////////////////////////
+
+  ////////////////////////////////////////////////////////////////////
+} // namespace zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_URL_URLUTILS_H */
+/*
+** vim: set ts=2 sts=2 sw=2 ai et:
+*/
diff --git a/zypp/ws/WebpinResult.cc b/zypp/ws/WebpinResult.cc
new file mode 100644 (file)
index 0000000..07b5277
--- /dev/null
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/WebpinResult.cc
+ *
+*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ws/WebpinResult.h"
+
+using namespace std;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+namespace ws
+{
+    
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : WebpinResult::Impl
+  //
+  /** WebpinResult implementation. */
+  struct WebpinResult::Impl
+  {
+    Impl()
+        : priority(0)
+    {}
+
+    ~Impl()
+    {
+      //MIL << std::endl;
+    }
+  public:
+      std::string name;
+      Edition edition;
+      CheckSum checksum;
+      Url repourl;
+      string summary;
+      string distro;
+      int priority;
+      
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  /** \relates WebpinResult::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const WebpinResult::Impl & obj )
+  {
+    return str << "WebpinResult::Impl";
+  }
+
+  WebpinResult::WebpinResult()
+  : _pimpl( new Impl() )
+  {}
+
+  WebpinResult::~WebpinResult()
+  {
+    //MIL << std::endl;
+  }
+
+  WebpinResult & WebpinResult::setName( const std::string &name )
+  {
+    _pimpl->name = name;
+    return *this;
+  }
+
+  std::string WebpinResult::name() const
+  {
+      return _pimpl->name;
+  }
+
+  zypp::Url WebpinResult::repositoryUrl() const
+  {
+      return _pimpl->repourl;    
+  }
+
+  WebpinResult & WebpinResult::setRepositoryUrl( const zypp::Url &url )
+  {
+      _pimpl->repourl = url;
+      return *this;
+  }
+    
+  WebpinResult & WebpinResult::setDistribution( const std::string &distro )
+  {
+    _pimpl->distro = distro;
+    return *this;
+  }
+
+  std::string WebpinResult::distribution() const
+  {
+      return _pimpl->distro;
+  }
+
+  WebpinResult & WebpinResult::setSummary( const std::string &summary )
+  {
+    _pimpl->summary = summary;
+    return *this;
+  }
+
+  std::string WebpinResult::summary() const
+  {
+      return _pimpl->summary;
+  }
+
+  WebpinResult & WebpinResult::setPriority( int priority )
+  {
+    _pimpl->priority = priority;
+    return *this;
+  }
+
+  int WebpinResult::priority() const
+  {
+      return _pimpl->priority;
+  }
+
+
+  WebpinResult & WebpinResult::setEdition( const Edition &edition )
+  {
+    _pimpl->edition = edition;
+    return *this;
+  }
+
+  Edition WebpinResult::edition() const
+  {
+      return _pimpl->edition;
+  }
+
+  WebpinResult & WebpinResult::setChecksum( const CheckSum &checksum )
+  {
+    _pimpl->checksum = checksum;
+    return *this;
+  }
+
+  CheckSum WebpinResult::checksum() const
+  {
+      return _pimpl->checksum;
+  }
+
+
+  std::ostream & WebpinResult::dumpOn( std::ostream & str ) const
+  {
+    str << "- name        : " << name() << std::endl;
+    return str;
+  }
+
+  std::ostream & operator<<( std::ostream & str, const WebpinResult & obj )
+  {
+    return obj.dumpOn(str);
+  }
+
+} // namespace ws
+    
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/ws/WebpinResult.h b/zypp/ws/WebpinResult.h
new file mode 100644 (file)
index 0000000..72af525
--- /dev/null
@@ -0,0 +1,147 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/WebpinResult.h
+ *
+*/
+#ifndef ZYPP_WEBPINRESULT_H
+#define ZYPP_WEBPINRESULT_H
+
+#include <iosfwd>
+#include <list>
+#include <set>
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Iterator.h"
+#include "zypp/base/Deprecated.h"
+
+#include "zypp/CheckSum.h"
+#include "zypp/Edition.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
+#include "zypp/repo/RepoType.h"
+#include "zypp/repo/RepoVariables.h"
+
+namespace zypp
+{ 
+namespace ws
+{
+  /**
+   * \short Represents a result from 
+   * http://api.opensuse-community.org/searchservice/Search
+   * web service
+   *
+   */
+  class WebpinResult
+  {
+    friend std::ostream & operator<<( std::ostream & str, const WebpinResult & obj );
+
+    public:
+    WebpinResult();
+    ~WebpinResult();
+
+    /**
+     * package name
+     */
+    std::string name() const;
+
+    /**
+     * set the package name \see name
+     * \param name
+     */
+    WebpinResult & setName( const std::string &name );
+
+    /**
+     * package edition
+     */
+    zypp::Edition edition() const;
+
+    /**
+     * set the package edition \see edition
+     * \param edition
+     */
+    WebpinResult & setEdition( const zypp::Edition &name );
+
+    /**
+     * repository's url
+     * The url of the repository where this package
+     * is located
+     */
+    zypp::Url repositoryUrl() const;
+
+    /**
+     * set the repository url where this package comes from
+     * \see repositoryUrl
+     * \param url
+     */
+    WebpinResult & setRepositoryUrl( const zypp::Url &url );
+      
+   /**
+     * package priority
+     */
+    int priority() const;
+
+    /**
+     * set the package priority \see priority
+     * \param priority
+     */
+    WebpinResult & setPriority( int priority );
+
+
+    /**
+     * package summary
+     */
+    std::string summary() const;
+
+    /**
+     * set the package summary \see summary
+     * \param summary
+     */
+    WebpinResult & setSummary( const std::string &summary );
+
+    /**
+     * package distribution
+     * Example: openSUSE 10.3
+     */
+    std::string distribution() const;
+
+    /**
+     * set the package distribution \see distribution
+     * \param distribution
+     */
+    WebpinResult & setDistribution( const std::string &distribution );
+
+   /**
+     * package checksum
+     * Example: a md5sum or sha1sum
+     */
+    zypp::CheckSum checksum() const;
+
+    /**
+     * set the package checksum \see checksum
+     * \param checksum
+     */
+    WebpinResult & setChecksum( const zypp::CheckSum &checksum );
+
+
+    std::ostream & dumpOn( std::ostream & str ) const;
+
+    class Impl;
+  private:
+    /** Pointer to implementation */
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates RepoInfo Stream output */
+  std::ostream & operator<<( std::ostream & str, const WebpinResult & obj );
+
+} // namespace ws
+} // namespace zypp
+
+
+#endif // ZYPP_WEBPINRESULT_H
diff --git a/zypp/zypp_detail/ZYppImpl.cc b/zypp/zypp_detail/ZYppImpl.cc
new file mode 100644 (file)
index 0000000..58c753c
--- /dev/null
@@ -0,0 +1,193 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/zypp_detail/ZYppImpl.cc
+ *
+*/
+
+#include <iostream>
+#include "zypp/TmpPath.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+
+#include "zypp/zypp_detail/ZYppImpl.h"
+#include "zypp/solver/detail/Helper.h"
+#include "zypp/target/TargetImpl.h"
+#include "zypp/ZYpp.h"
+#include "zypp/DiskUsageCounter.h"
+#include "zypp/ZConfig.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/PoolItem.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace zypp_detail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ZYppImpl::ZYppImpl
+    // METHOD TYPE : Constructor
+    //
+    ZYppImpl::ZYppImpl()
+    : _target(0)
+    , _resolver( new Resolver( ResPool::instance()) )
+    {
+      ZConfig::instance().about( MIL );
+      MIL << "Initializing keyring..." << std::endl;
+      _keyring = new KeyRing(tmpPath());
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : ZYppImpl::~ZYppImpl
+    // METHOD TYPE : Destructor
+    //
+    ZYppImpl::~ZYppImpl()
+    {}
+
+    //------------------------------------------------------------------------
+    // add/remove resolvables
+
+    DiskUsageCounter::MountPointSet ZYppImpl::diskUsage()
+    {
+      if ( ! _disk_usage )
+      {
+        setPartitions( DiskUsageCounter::detectMountPoints() );
+      }
+      return _disk_usage->disk_usage(pool());
+    }
+
+    void ZYppImpl::setPartitions(const DiskUsageCounter::MountPointSet &mp)
+    {
+      _disk_usage.reset(new DiskUsageCounter());
+      _disk_usage->setMountPoints(mp);
+    }
+
+    DiskUsageCounter::MountPointSet ZYppImpl::getPartitions() const
+    {
+      if (_disk_usage)
+        return _disk_usage->getMountPoints();
+      else
+        return DiskUsageCounter::detectMountPoints();
+    }
+
+    //------------------------------------------------------------------------
+    // target
+
+    Target_Ptr ZYppImpl::target() const
+    {
+      if (! _target)
+       ZYPP_THROW(Exception("Target not initialized."));
+      return _target;
+     }
+
+    void ZYppImpl::initializeTarget( const Pathname & root, bool doRebuild_r )
+    {
+      MIL << "initTarget( " << root << (doRebuild_r?", rebuilddb":"") << ")" << endl;
+      if (_target) {
+          if (_target->root() == root) {
+              MIL << "Repeated call to initializeTarget()" << endl;
+              return;
+          }
+
+          _target->unload();
+
+      }
+      _target = new Target( root, doRebuild_r );
+      _target->buildCache();
+    }
+
+    void ZYppImpl::finishTarget()
+    {
+      if (_target)
+          _target->unload();
+
+      _target = 0;
+    }
+
+    //------------------------------------------------------------------------
+    // commit
+
+    /** \todo Remove workflow from target, lot's of it could be done here,
+     * and target used for transact. */
+    ZYppCommitResult ZYppImpl::commit( const ZYppCommitPolicy & policy_r )
+    {
+      if ( getenv("ZYPP_TESTSUITE_FAKE_ARCH") )
+      {
+        ZYPP_THROW( Exception("ZYPP_TESTSUITE_FAKE_ARCH set. Commit not allowed and disabled.") );
+      }
+
+      MIL << "Attempt to commit (" << policy_r << ")" << endl;
+      if (! _target)
+       ZYPP_THROW( Exception("Target not initialized.") );
+
+      ZYppCommitResult res = _target->_pimpl->commit( pool(), policy_r );
+
+      if (! policy_r.dryRun() )
+      {
+        if ( policy_r.syncPoolAfterCommit() )
+          {
+            // reload new status from target
+            DBG << "reloading " << sat::Pool::instance().systemRepoAlias() << " repo to pool" << endl;
+            _target->load();
+          }
+        else
+          {
+            DBG << "unloading " << sat::Pool::instance().systemRepoAlias() << " repo from pool" << endl;
+            _target->unload();
+          }
+      }
+
+      MIL << "Commit (" << policy_r << ") returned: "
+          << res << endl;
+      return res;
+    }
+
+    void ZYppImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
+    {
+      if (! _target)
+        ZYPP_THROW( Exception("Target not initialized.") );
+      _target->_pimpl->installSrcPackage( srcPackage_r );
+    }
+
+    //------------------------------------------------------------------------
+    // target store path
+
+    Pathname ZYppImpl::homePath() const
+    { return _home_path.empty() ? Pathname("/var/lib/zypp") : _home_path; }
+
+    void ZYppImpl::setHomePath( const Pathname & path )
+    { _home_path = path; }
+
+    Pathname ZYppImpl::tmpPath() const
+    {
+      static TmpDir zypp_tmp_dir( TmpPath::defaultLocation(), "zypp." );
+      return zypp_tmp_dir.path();
+    }
+
+    /******************************************************************
+     **
+     **        FUNCTION NAME : operator<<
+     **        FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const ZYppImpl & obj )
+    {
+      return str << "ZYppImpl";
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace zypp_detail
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/zypp_detail/ZYppImpl.h b/zypp/zypp_detail/ZYppImpl.h
new file mode 100644 (file)
index 0000000..2294d8c
--- /dev/null
@@ -0,0 +1,133 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/zypp_detail/ZYppImpl.h
+ *
+*/
+#ifndef ZYPP_ZYPP_DETAIL_ZYPPIMPL_H
+#define ZYPP_ZYPP_DETAIL_ZYPPIMPL_H
+
+#include <iosfwd>
+
+#include "zypp/TmpPath.h"
+#include "zypp/Target.h"
+#include "zypp/Resolver.h"
+#include "zypp/KeyRing.h"
+#include "zypp/ZYppCommit.h"
+#include "zypp/ResTraits.h"
+#include "zypp/DiskUsageCounter.h"
+
+using namespace zypp::filesystem;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace zypp_detail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : ZYppImpl
+    //
+    /** */
+    class ZYppImpl
+    {
+      friend std::ostream & operator<<( std::ostream & str, const ZYppImpl & obj );
+
+    public:
+      /** Default ctor */
+      ZYppImpl();
+      /** Dtor */
+      ~ZYppImpl();
+
+    public:
+      /** */
+      ResPool pool() const
+      { return ResPool::instance(); }
+
+      ResPoolProxy poolProxy() const
+      { return ResPool::instance().proxy(); }
+
+      /** */
+      KeyRing_Ptr keyRing() const
+      { return _keyring; }
+
+
+      Resolver_Ptr resolver() const
+      { return _resolver; }
+
+    public:
+      /** \todo Signal locale change. */
+      /**
+       * \throws Exception
+       */
+      Target_Ptr target() const;
+
+      /** Same as \ref target but returns NULL if target is not
+       *  initialized, instead of throwing.
+       */
+      Target_Ptr getTarget() const
+      { return _target; }
+
+      /**
+       * \throws Exception
+       * true, just init the target, dont populate store or pool
+       */
+      void initializeTarget( const Pathname & root, bool doRebuild_r );
+
+      /**
+       * \throws Exception
+       */
+      void finishTarget();
+
+      /** Commit changes and transactions. */
+      ZYppCommitResult commit( const ZYppCommitPolicy & policy_r );
+
+      /** Install a source package on the Target. */
+      void installSrcPackage( const SrcPackage_constPtr & srcPackage_r );
+
+    public:
+      /** Get the path where zypp related plugins store persistent data and caches   */
+      Pathname homePath() const;
+
+      /** Get the path where zypp related plugins store tmp data   */
+      Pathname tmpPath() const;
+
+      /** set the home, if you need to change it */
+      void setHomePath( const Pathname & path );
+
+    public:
+      DiskUsageCounter::MountPointSet diskUsage();
+      void setPartitions(const DiskUsageCounter::MountPointSet &mp);
+      DiskUsageCounter::MountPointSet getPartitions() const;
+
+    private:
+      /** */
+      Target_Ptr _target;
+      /** */
+      Resolver_Ptr _resolver;
+
+      KeyRing_Ptr _keyring;
+      /** */
+      Pathname _home_path;
+      /** defined mount points, used for disk usage counting */
+      shared_ptr<DiskUsageCounter> _disk_usage;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates ZYppImpl Stream output */
+    std::ostream & operator<<( std::ostream & str, const ZYppImpl & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace zypp_detail
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_ZYPP_DETAIL_ZYPPIMPL_H
diff --git a/zypp/zypp_detail/ZYppReadOnlyHack.h b/zypp/zypp_detail/ZYppReadOnlyHack.h
new file mode 100644 (file)
index 0000000..24b1dd7
--- /dev/null
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/zypp_detail/ZYppReadOnlyHack.h
+ *
+*/
+
+#ifndef ZYPP_ZYPP_DETAIL_ZYPPREADONLYHACK_H
+#define ZYPP_ZYPP_DETAIL_ZYPPREADONLYHACK_H
+#warning ZYPP_ZYPP_DETAIL_ZYPPREADONLYHACK_H
+
+#include "zypp/base/Deprecated.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace zypp_readonly_hack
+  { /////////////////////////////////////////////////////////////////
+
+    void IWantIt() ZYPP_DEPRECATED;
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace zypp_readonly_hack
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_ZYPP_DETAIL_ZYPPREADONLYHACK_H